using HotelPms.Client.Blazor.Dialog;
|
using HotelPms.Client.Blazor.Models;
|
using HotelPms.Client.Blazor.Util;
|
using HotelPms.Data.Common;
|
using HotelPms.Share.IO;
|
using HotelPms.Share.Util;
|
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components.Web;
|
using MudBlazor;
|
using static HotelPms.Client.Blazor.Util.SystemEnum;
|
|
namespace HotelPms.Client.Blazor.Pages.UseDetail;
|
|
public partial class SaleInput : ComponentBase
|
{
|
[Parameter] public ViewModel.UseInput? Data { get; set; }
|
[Parameter] public Action OnRefresh { get; set; }
|
|
private System.Threading.SemaphoreSlim slimlock = new SemaphoreSlim(1, 1);
|
|
private IEnumerable<FeeDtailRow> FeeDetailList = new List<FeeDtailRow>();
|
|
private IEnumerable<SaleChildRow> SaleChildList = new List<SaleChildRow>();
|
|
private SaleInputRow selectedItem1 = null;
|
private List<string> editEvents = new();
|
private string searchString = "";
|
private string RowSpaceStyle = "mt-2 pt-0 mb-0 pb-0";
|
private MudTable<SaleInputRow> _table;
|
|
private bool keyDownPreventDefault = false;
|
private bool keyPressPreventDefault = false;
|
private bool enterPush = false;
|
private bool arrowUpPush = false;
|
|
private EditGridFocusEventArgs m_CurrentFocusData = new EditGridFocusEventArgs();
|
|
protected override void OnInitialized()
|
{
|
//EnvironmentSetting.Debug($"SaleInput.OnInitialized開始:");
|
base.OnInitialized();
|
}
|
|
protected override void OnAfterRender(bool firstRender)
|
{
|
//EnvironmentSetting.Debug($"SaleInput.OnAfterRender開始:{firstRender}");
|
base.OnAfterRender(firstRender);
|
}
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
{
|
await slimlock.WaitAsync();
|
EnvironmentSetting.Debug($"SaleInput.OnAfterRenderAsync開始:{firstRender}");
|
await ShowFocus();
|
EnvironmentSetting.Debug($"SaleInput.OnAfterRenderAsync終了:{firstRender}");
|
slimlock.Release();
|
}
|
|
/// <summary>
|
/// フォーカス移動準備
|
/// </summary>
|
/// <param name="row"></param>
|
/// <param name="col"></param>
|
/// <param name="action">EditModeの場合、自動で次のフォーカス移動する</param>
|
private void SetFocus(int row, int col, EditGridFocusEventArgs.ActionType action = EditGridFocusEventArgs.ActionType.Focus)
|
{
|
EnvironmentSetting.Debug($"フォーカス移動準備:開始⇒Row:{row},Col:{col},Action:{action},Enabled:{m_CurrentFocusData.Enabled}");
|
m_CurrentFocusData.Row = row;
|
m_CurrentFocusData.Col = col;
|
m_CurrentFocusData.Action = action;
|
m_CurrentFocusData.Enabled = true;
|
EnvironmentSetting.Debug($"フォーカス移動準備:完了⇒Row:{row},Col:{col},Action:{action},Enabled:{m_CurrentFocusData.Enabled}");
|
}
|
|
/// <summary>
|
/// OnAfterRenderAsync時フォーカスを設定
|
/// </summary>
|
private async Task<bool> ShowFocus()
|
{
|
try
|
{
|
EnvironmentSetting.Debug($"SaleInput.FocusAsync行Before:Row={m_CurrentFocusData.Row},Col={m_CurrentFocusData.Col},Action={m_CurrentFocusData.Action},Enabled={m_CurrentFocusData.Enabled}");
|
if (!m_CurrentFocusData.Enabled)
|
{
|
EnvironmentSetting.Debug($"OnAfterRenderAsync⇒return false");
|
return false;
|
}
|
m_CurrentFocusData.Enabled = false;
|
|
EnvironmentSetting.Debug($"ShowFocus処理:Row={m_CurrentFocusData.Row},Col={m_CurrentFocusData.Col},Action={m_CurrentFocusData.Action},Enabled={m_CurrentFocusData.Enabled}");
|
if (m_CurrentFocusData.Action == EditGridFocusEventArgs.ActionType.Focus && m_CurrentFocusData.Row != -1 && m_CurrentFocusData.Col != -1)
|
{
|
//この時点Grid編集状態ですので、フォーカス可能になる
|
EnvironmentSetting.Debug($"フォーカスへ開始:Row = {m_CurrentFocusData.Row}, Col = {m_CurrentFocusData.Col}");
|
await (Data.SaleList as List<SaleInputRow>)[m_CurrentFocusData.Row].Cells[m_CurrentFocusData.Col].Ref.FocusAsync(); //ここに再度OnAfterRenderAsyncに入るが、EnabledはFalseのため、ShowFocusの動作がしない
|
EnvironmentSetting.Debug($"フォーカスへ完了:Row = {m_CurrentFocusData.Row}, Col = {m_CurrentFocusData.Col}");
|
}
|
return true;
|
}
|
catch(Exception ex)
|
{
|
EnvironmentSetting.Debug(ex.Message);
|
return false;
|
}
|
}
|
|
//protected override async Task OnAfterRenderAsync(bool firstRender)
|
//{
|
// EnvironmentSetting.Debug($"SaleInput.OnAfterRenderAsync開始:{firstRender}");
|
// //await ShowFocus();
|
// //await JSInteropEx.DelGridCol(JSRuntime, "SaleInputTable", 11);
|
//}
|
|
protected override void OnParametersSet()
|
{
|
EnvironmentSetting.Debug($"SaleInput.OnParametersSet開始:");
|
base.OnParametersSet();
|
}
|
|
protected override bool ShouldRender()
|
{
|
EnvironmentSetting.Debug($"SaleInput.ShouldRender開始:");
|
return base.ShouldRender();
|
}
|
|
private async Task KeyPress(int index, SaleInputRow data, KeyboardEventArgs e)
|
{
|
//if (e.Key == "Enter")
|
//{
|
// List<SaleInputRow> list = (Data.SaleList as List<SaleInputRow>);
|
// int rowIdx = list.IndexOf(selectedItem1);
|
|
// // https://bytemeta.vip/repo/MudBlazor/MudBlazor/issues/4192
|
// // https://github.com/MudBlazor/MudBlazor/issues/3292
|
// SaleInputRow newitem = (rowIdx == list.Count - 1) ? list[0] : list[rowIdx + 1];
|
// _table.SetSelectedItem(newitem);
|
// _table.SetEditingItem(newitem);
|
// await Task.Delay(50); //画面更新のため
|
// await newitem.Cells[(int)SaleInputRow.ColType.ItemName].Ref.FocusAsync(); //OnAfterRenderAsyncへ
|
//}
|
|
if (e.Key == "Enter")
|
{
|
try
|
{
|
int rowIdx = GetRowIndex(data);
|
Console.WriteLine($"Row:{rowIdx} Col:{index}");
|
Console.WriteLine("Data:" + CConvert.ToJsonText<SaleInputRow>(data));
|
|
//※その時、Fields[index].Textの値まだ変っていない!!!!!
|
string text = await data.GetInputValue(index, JSRuntime);
|
EnvironmentSetting.Debug($"Return:{index}⇒{text}");
|
keyPressPreventDefault = true; //イベント中止
|
enterPush = true; //Enterを押した知らせ
|
|
//通常チェックを行う
|
if (!await IsValid(index, text, data, true)) { return; }
|
|
if (!SetAutoNextFocus(index, true, data)) { return; }
|
}
|
catch (Exception ex)
|
{
|
OperationLog.Instance.WriteLog($"KeyPress:{ex.Message}");
|
}
|
finally
|
{
|
Refresh("KeyPress"); //UI更新(binding値変更通知)
|
}
|
}
|
else
|
{
|
if (index == (int)SaleInputRow.ColType.PersonCount || index == (int)SaleInputRow.ColType.UnitPrice)
|
{
|
if (CConvert.IsNumPress(e.Key))
|
{
|
keyPressPreventDefault = false;
|
}
|
else
|
{
|
keyPressPreventDefault = true; //入力禁止(※:IMEモードで入力したものをここでは通らないため、防げない)
|
}
|
}
|
else
|
{
|
keyPressPreventDefault = false;
|
}
|
enterPush = false;
|
}
|
}
|
|
/// <summary>
|
/// フォーカス移動
|
/// </summary>
|
/// <param name="index">列Index</param>
|
/// <param name="isEnter"></param>
|
/// <returns></returns>
|
private bool SetAutoNextFocus(int index, bool isEnter, SaleInputRow data)
|
{
|
try
|
{
|
int rowIdx = GetRowIndex(data);
|
List<SaleInputRow> list = (Data.SaleList as List<SaleInputRow>);
|
if (isEnter)
|
{
|
if (rowIdx == -1) { return true; } //削除された場合
|
|
if (index == (int)SaleInputRow.ColType.Page)
|
{
|
if (rowIdx < (list.Count - 1))
|
{
|
//OnAfterRenderAsync:閲覧状態且つ新規行表示⇒編集状態⇒次項目へフォーカス
|
SetFocus(rowIdx + 1, (int)SaleInputRow.ColType.ItemName);
|
}
|
}
|
else if (index == (int)SaleInputRow.ColType.DiscountSummary)
|
{
|
SetFocus(rowIdx, index + 2);
|
}
|
else
|
{
|
SetFocus(rowIdx, index + 1);
|
}
|
}
|
else
|
{
|
if (index == (int)SaleInputRow.ColType.ItemName)
|
{
|
if (rowIdx > 0)
|
{
|
SetFocus(rowIdx - 1, (int)SaleInputRow.ColType.ItemName);
|
}
|
}
|
else if (index == (int)SaleInputRow.ColType.SumDate)
|
{
|
SetFocus(rowIdx, index - 2);
|
}
|
else
|
{
|
SetFocus(rowIdx, index - 1);
|
}
|
}
|
return true;
|
}
|
catch
|
{
|
return false;
|
}
|
}
|
|
private void DetailClick(SaleInputRow data)
|
{
|
//Snackbar.Add("詳細表示");
|
List<FeeDtailRow> list = FeeDetailList as List<FeeDtailRow>;
|
list.Clear();
|
list.Add(new() { Name = "本体額", Value = "10,000000" });
|
list.Add(new() { Name = "サービス", Value = "2,000" });
|
list.Add(new() { Name = "消費税", Value = "100" });
|
list.Add(new() { Name = "入湯税", Value = "250" });
|
list.Add(new() { Name = "宿泊税", Value = "400" });
|
list.Add(new() { Name = "合 計", Value = "16,850" });
|
|
List<SaleChildRow> listChild = SaleChildList as List<SaleChildRow>;
|
listChild.Clear();
|
SaleChildRow row = new();
|
row.Cells[0] = "S";
|
row.Cells[1] = "101";
|
row.Cells[2] = "基本料金";
|
listChild.Add(row);
|
|
row = new();
|
row.Cells[0] = "S";
|
row.Cells[1] = "101";
|
row.Cells[2] = "朝食";
|
listChild.Add(row);
|
|
data.ActiveCol = (int)SaleInputRow.ColType.Detail;
|
data.ShowDetails = !data.ShowDetails;
|
}
|
|
private void OnCommitEditButtonClicked(MouseEventArgs e)
|
{
|
_table.RowEditCommit?.Invoke(_table.SelectedItem);
|
_table.OnCommitEditClick.InvokeAsync(e);
|
_table.SetSelectedItem(null);
|
}
|
|
private async Task Save()
|
{
|
(Data.SaleList as List<SaleInputRow>).Add(new());
|
//SaleInputRow newitem = (Data.SaleList as List<SaleInputRow>)[5];
|
////await Task.Delay(50);
|
//_table.SetSelectedItem(newitem);
|
//_table.SetEditingItem(newitem);
|
await Task.Delay(50);
|
//await newitem.Cells[(int)SaleInputRow.ColType.ItemName].Ref.FocusAsync();
|
}
|
|
private async Task KeyDown(int index, SaleInputRow data, KeyboardEventArgs e)
|
{
|
if (e.Key == "ArrowUp")
|
{
|
try
|
{
|
EnvironmentSetting.Debug($"ArrowUp:{index}");
|
if (SetAutoNextFocus(index, false, data))
|
{
|
//BlazorのBugでEnterのパラメータ変数できない!
|
//Uncaught Error: System.ArgumentException: There is no event handler associated with this event. EventId: '290'. (Parameter 'eventHandlerId')
|
//mkArtakMSFT modified the milestones: Next sprint planning, 6.0-preview4 on 20 Mar ← Net6.0-preview4対応だそう
|
keyDownPreventDefault = true;
|
arrowUpPush = true;
|
}
|
else
|
{
|
keyDownPreventDefault = false;
|
arrowUpPush = false;
|
}
|
}
|
catch (Exception ex)
|
{
|
OperationLog.Instance.WriteLog($"KeyDown:{ex.Message}");
|
}
|
finally
|
{
|
Refresh("KeyDown⇒↑"); //UI更新(binding値変更通知)
|
}
|
}
|
else if (e.Key == "End")
|
{
|
if (data.Cells[index].ShowStyle == EShowStyle.ShowList)
|
{
|
data.ActiveCol = index;
|
if (index == (int)SaleInputRow.ColType.SumDate)
|
{
|
var parameters = new DialogParameters { ["Title"] = $"集計日選択", ["Data"] = CConvert.ToDateTime(data.Cells[index].Text) };
|
var dialog = DialogService.Show<SelectDate>(string.Empty, parameters);
|
var ret = await dialog.Result;
|
if (!ret.Cancelled)
|
{
|
EnvironmentSetting.Debug(ret.Data.ToString());
|
data.Cells[index].Text = ret.Data.ToString();
|
//StateHasChanged();
|
}
|
}
|
else
|
{
|
data.ShowDetails = !data.ShowDetails;
|
}
|
}
|
}
|
else if (e.Key == "Delete")
|
{
|
if (index == (int)SaleInputRow.ColType.ItemName)
|
{
|
int rowIdx = GetRowIndex(data);
|
List<SaleInputRow> list = (Data.SaleList as List<SaleInputRow>);
|
bool isLastRow = rowIdx == (list.Count - 1);
|
if (isLastRow) { return; }
|
SetFocus(rowIdx, (int)SaleInputRow.ColType.ItemName);
|
EnvironmentSetting.Debug($"削除前件数:{list.Count}");
|
list.Remove(data);
|
EnvironmentSetting.Debug($"削除後件数:{list.Count}");
|
keyDownPreventDefault = false;
|
arrowUpPush = false;
|
}
|
}
|
else
|
{
|
keyDownPreventDefault = false;
|
arrowUpPush = false;
|
}
|
|
}
|
|
private async Task Enter(int index, SaleInputRow data, FocusEventArgs e)
|
{
|
await slimlock.WaitAsync();
|
int rowIdx = GetRowIndex(data);
|
Console.WriteLine($"Enter⇒Row:{rowIdx} Col:{index}");
|
slimlock.Release();
|
//ここは必ずUI更新
|
}
|
|
private async Task Leave(int index, SaleInputRow data, FocusEventArgs e)
|
{
|
string text = string.Empty;
|
try
|
{
|
await slimlock.WaitAsync();
|
Console.WriteLine($"Leave⇒Start");
|
int rowIdx = GetRowIndex(data);
|
text = data.GetCellText(index);
|
Console.WriteLine($"Leave⇒Row:{rowIdx} Col:{index} enterPush:{enterPush}, text:{text}");
|
|
if (enterPush)
|
{
|
//Enterキーを押した時にも既にチェック済
|
enterPush = false;
|
}
|
else
|
{
|
//値変更したら、イベント発生
|
if (!data.IsValueChanged(index, text))
|
{
|
//必須判断:初期表示時:OrgTextは空白のため、値変更しないまま
|
return;
|
}
|
|
//通常チェックを行う
|
if (!await IsValid(index, text, data, false))
|
{
|
return;
|
}
|
|
//Refresh("Leave(変更あり)"); //UI更新(binding値変更通知)
|
}
|
}
|
catch (Exception ex)
|
{
|
OperationLog.Instance.WriteLog($"Leave:{ex.Message}");
|
}
|
finally
|
{
|
//Refresh(); //UI更新(binding値変更通知)
|
Console.WriteLine($"Leave⇒End");
|
slimlock.Release();
|
|
//ここは必ずUI更新
|
}
|
}
|
|
private int GetRowIndex(SaleInputRow data)
|
{
|
return (Data.SaleList as List<SaleInputRow>).IndexOf(data);
|
}
|
|
/// <summary>
|
/// 業務チェック処理
|
/// </summary>
|
/// <param name="index"></param>
|
/// <param name="inputText"></param>
|
/// <param name="data"></param>
|
/// <param name="isEnter"></param>
|
/// <returns></returns>
|
private async Task<bool> IsValid(int index, string inputText, SaleInputRow data, bool isEnter)
|
{
|
int rowIdx = GetRowIndex(data);
|
//最終行判断
|
List<SaleInputRow> list = (Data.SaleList as List<SaleInputRow>);
|
bool isLastRow = rowIdx == (list.Count - 1);
|
|
if (index == (int)SaleInputRow.ColType.ItemName)
|
{
|
if (!await IsValidItemName(rowIdx, inputText, data, isEnter, isLastRow, true))
|
{
|
return false;
|
}
|
}
|
else if(index == (int)SaleInputRow.ColType.SumDate)
|
{
|
}
|
else
|
{
|
|
}
|
return true;
|
}
|
|
|
|
private RenderFragment RenderWidget(string msg) => builder =>
|
{
|
builder.OpenComponent(0, typeof(MessageContext));
|
builder.AddAttribute(1, "Text", msg);
|
builder.CloseComponent();
|
};
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="inputText"></param>
|
/// <param name="data"></param>
|
/// <param name="isEnter"></param>
|
/// <param name="isLastRow">Trueの場合、必ずID</param>
|
/// <param name="isAny">True:ID or Name False:ID</param>
|
/// <returns></returns>
|
private async Task<bool> IsValidItemName(int rowIdx, string inputText, SaleInputRow data, bool isEnter, bool isLastRow, bool isAny)
|
{
|
if (inputText.Trim().Length == 0) { return true; }
|
HotelPms.Data.UseInfo.Sale curSale = data.GetSale();
|
bool nameChanged = false;
|
|
if (isLastRow)
|
{
|
if (!CanCreateSale()) { return true; }
|
}
|
else
|
{
|
//変更されたかどうか
|
bool isChanged = false;
|
if (isAny)
|
{
|
//直接入力する(数字の場合、ItemIDで扱う)
|
if (CConvert.IsWholeNumber(inputText))
|
{
|
if (curSale.IsReadOnly()) { return true; }
|
if (inputText.PadLeft(50, '0') != curSale.ItemID.PadLeft(50, '0')) { isChanged = true; } else { data.RestoreText(SaleInputRow.ColType.ItemName); }
|
}
|
else
|
{
|
if (inputText != curSale.ItemName) { isChanged = true; nameChanged = true; }
|
}
|
}
|
else
|
{
|
//Endキーより別の科目を選択など
|
if (inputText != curSale.ItemID) { isChanged = true; }
|
}
|
if (!isChanged) { return true; } //変更しなかったら
|
}
|
|
if (!RaiseValueChanged((int)SaleInputRow.ColType.ItemName, data)) { return false; } //変更イベント通知
|
|
//名称変更のみ
|
if (nameChanged)
|
{
|
data.SetDataField((int)SaleInputRow.ColType.ItemName, inputText);
|
return true;
|
}
|
|
//入力値はItemIDと処理する
|
HotelPms.Data.Master.Item item = await MasterCore.GetItem(inputText);
|
if (item == null)
|
{
|
//科目が存在しない
|
if (isEnter) { Message.Show(RenderMessage(inputText)); }
|
data.RestoreText(SaleInputRow.ColType.ItemName);
|
return false;
|
}
|
|
if (isLastRow)
|
{
|
int saleID = await EnvironmentSetting.GetSeq(ESeqType.Sale);
|
int taxRate = await EnvironmentSetting.GetTaxRate(Data.SelUseRoomRow.UseRoom.UseDate.ToText(), item.TaxType);
|
curSale = Data.SelUseRoomRow.UseRoom.AddSaleByItem(saleID, taxRate, item); //選択された部屋より追加
|
data.DataList[rowIdx] = curSale;
|
}
|
else
|
{
|
//既存行の科目変更
|
if (curSale.ItemKind == (int)EItemKind.StayBase || curSale.ItemKind == (int)EItemKind.DayUseBase)
|
{
|
if (item.Kind != (int)EItemKind.StayBase && item.Kind != (int)EItemKind.DayUseBase)
|
{
|
if (isEnter) { Message.Show(RenderMessage("基本科目を入力してください。")); }
|
data.RestoreText(SaleInputRow.ColType.ItemName);
|
return false;
|
}
|
}
|
else
|
{
|
if (item.Kind == (int)EItemKind.StayBase || item.Kind == (int)EItemKind.DayUseBase)
|
{
|
if (isEnter) { Message.Show(RenderMessage("追加科目を入力してください。")); }
|
data.RestoreText(SaleInputRow.ColType.ItemName);
|
return false;
|
}
|
}
|
|
HotelPms.Data.UseInfo.UseRoom useRoom;
|
HotelPms.Data.UseInfo.Sale newSale;
|
if (Data.SaleDispType == ESaleDispType.Normal)
|
{
|
useRoom = curSale.Parent as HotelPms.Data.UseInfo.UseRoom;
|
useRoom.RemoveSale(curSale);
|
int saleID = await EnvironmentSetting.GetSeq(ESeqType.Sale);
|
int taxRate = await EnvironmentSetting.GetTaxRate(useRoom.UseDate.ToText(), item.TaxType);
|
newSale = useRoom.AddSaleByItem(saleID, taxRate, item, curSale);
|
data.DataList.Clear();
|
data.DataList.Add(newSale);
|
}
|
else
|
{
|
//まとめモードの変更
|
List<HotelPms.Data.UseInfo.Sale> copyList = new List<HotelPms.Data.UseInfo.Sale>();
|
copyList.AddRange(data.DataList);
|
foreach (HotelPms.Data.UseInfo.Sale tagSlip in copyList)
|
{
|
curSale = tagSlip as HotelPms.Data.UseInfo.Sale;
|
useRoom = curSale.Parent as HotelPms.Data.UseInfo.UseRoom;
|
useRoom.RemoveSale(curSale); //元の伝票を削除
|
int saleID = await EnvironmentSetting.GetSeq(ESeqType.Sale);
|
int taxRate = await EnvironmentSetting.GetTaxRate(useRoom.UseDate.ToText(), item.TaxType);
|
newSale = useRoom.AddSaleByItem(saleID, taxRate, item, curSale); //新伝票の追加
|
int orgSlipIndex = data.DataList.IndexOf(curSale);
|
data.DataList[orgSlipIndex] = newSale; //切替える
|
}
|
}
|
}
|
|
|
data.SetDataField((int)SaleInputRow.ColType.ItemName, item.Name);
|
|
//画面表示(イベントを起こすため、必ず一番最後)
|
data.Cells[(int)SaleInputRow.ColType.ItemName].Text = item.Name;
|
|
//新規行追加
|
if (isLastRow)
|
{
|
//OnAfterRenderAsync:閲覧状態且つ新規行表示⇒編集状態⇒次項目へフォーカス
|
SetFocus(rowIdx, (int)SaleInputRow.ColType.PersonCount);
|
EnvironmentSetting.Debug($"新規行追加⇒開始");
|
(Data.SaleList as List<SaleInputRow>).Add(new());
|
EnvironmentSetting.Debug($"新規行追加⇒終了");
|
return false; //次の列SetAutoNextFocus行かない
|
}
|
return true;
|
}
|
|
/// <summary>
|
/// 新規伝票可能かどうか
|
/// </summary>
|
/// <returns></returns>
|
private bool CanCreateSale()
|
{
|
return true;
|
}
|
|
/// <summary>
|
/// 値変更
|
/// </summary>
|
public bool RaiseValueChanged(int index, SaleInputRow data)
|
{
|
return true;
|
}
|
|
/// <summary>
|
/// コントロール再表示
|
/// </summary>
|
public void Refresh(string type)
|
{
|
EnvironmentSetting.Debug($"再表示へ開始:{type}");
|
StateHasChanged();
|
EnvironmentSetting.Debug($"再表示へ終了:{type}");
|
//await InvokeAsync(StateHasChanged);
|
//OnRefresh?.Invoke();
|
}
|
}
|