@page "/admin/revenue-trackings" @using TaxBaik.Web.Services.AdminClients @inject IRevenueTrackingBrowserClient RevenueClient @inject IClientBrowserClient ClientClient @inject ISnackbar Snackbar @inject IDialogService DialogService @attribute [Authorize] 수익 추적 관리 CRM & 세무관리 수익 추적 관리 청구, 납부, 미수금 상태를 한 화면에서 관리합니다. 새 청구 추가 @if (revenues is null) { } else if (revenues.Count == 0) { 청구 기록이 없습니다. } else { @if (clientMap.TryGetValue(context.Item.ClientId, out var clientName)) { @clientName } @if (context.Item.PaymentStatus == "paid") { 납부 } else { 미납 } @if (context.Item.PaymentStatus != "paid") { } } 새 청구 추가 @foreach (var client in clients) { @GetClientDisplayName(client) } 기장 수수료 세무조정료 세무상담료 신고 대행료 자문 수수료 취소 저장 @code { [CascadingParameter] private Task? AuthStateTask { get; set; } private List? revenues; private List clients = []; private Dictionary clientMap = new(); private MudForm? form; private bool isDialogOpen; private RevenueForm revenueForm = new(); protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { if (AuthStateTask != null) { var authState = await AuthStateTask; if (authState.User.Identity?.IsAuthenticated == true) { await LoadData(); StateHasChanged(); } } } } private async Task LoadData() { try { revenues = await RevenueClient.GetAllAsync(); var (clientItems, _) = await ClientClient.GetPagedAsync(pageSize: 1000); clients = clientItems.ToList(); clientMap = clients.ToDictionary(c => c.Id, GetClientDisplayName); } catch (Exception ex) { Snackbar.Add($"데이터 로드 실패: {ex.Message}", Severity.Error); } } private void OpenCreateDialog() { revenueForm = new RevenueForm { ClientId = clients.FirstOrDefault()?.Id ?? 0, InvoiceDate = DateTime.Today, DueDate = DateTime.Today.AddDays(14) }; isDialogOpen = true; } private async Task SaveRevenue() { if (form != null) { await form.Validate(); if (!form.IsValid) { Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning); return; } } try { var newId = await RevenueClient.CreateAsync( revenueForm.ClientId, revenueForm.InvoiceNumber, revenueForm.InvoiceDate ?? DateTime.Now, revenueForm.Amount, revenueForm.ServiceType, revenueForm.DueDate); if (newId > 0) { Snackbar.Add("청구가 추가되었습니다.", Severity.Success); CloseDialog(); await LoadData(); } } catch (Exception ex) { Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error); } } private async Task MarkPaid(int id) { try { await RevenueClient.MarkPaidAsync(id, DateTime.Now); Snackbar.Add("납부가 처리되었습니다.", Severity.Success); await LoadData(); } catch (Exception ex) { Snackbar.Add($"처리 실패: {ex.Message}", Severity.Error); } } private async Task DeleteRevenue(int id) { var parameters = new DialogParameters { { "Title", "삭제 확인" }, { "Message", "이 청구를 삭제하시겠습니까?" } }; var dialog = await DialogService.ShowAsync("", parameters); var result = await dialog.Result; if (result?.Canceled ?? true) return; try { await RevenueClient.DeleteAsync(id); Snackbar.Add("청구가 삭제되었습니다.", Severity.Success); await LoadData(); } catch (Exception ex) { Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error); } } private void CloseDialog() { isDialogOpen = false; revenueForm = new(); } private static string GetClientDisplayName(Client client) => !string.IsNullOrWhiteSpace(client.CompanyName) ? client.CompanyName : !string.IsNullOrWhiteSpace(client.Name) ? client.Name : $"Client #{client.Id}"; private class RevenueForm { public int ClientId { get; set; } public string InvoiceNumber { get; set; } = ""; public DateTime? InvoiceDate { get; set; } public decimal Amount { get; set; } public string? ServiceType { get; set; } public DateTime? DueDate { get; set; } } }