@page "/admin/revenue-trackings" @using TaxBaik.Web.Services.AdminClients @inject IRevenueTrackingBrowserClient RevenueClient @inject IClientBrowserClient ClientClient @inject IJSRuntime JS @attribute [Authorize] 수익 추적 관리
CRM & 세무관리

수익 추적 관리

청구, 납부, 미수금 상태를 한 화면에서 관리합니다.

@if (revenues is null) { } else if (revenues.Count == 0) {
청구 기록이 없습니다.
} else {
@foreach (var item in revenues) { }
ID 고객 청구번호 청구일 청구액 납부여부 작업
@item.Id @clientMap.GetValueOrDefault(item.ClientId, $"Client #{item.ClientId}") @item.InvoiceNumber @item.InvoiceDate.ToString("yyyy-MM-dd") @item.Amount.ToString("C") @(item.PaymentStatus == "paid" ? "납부" : "미납")
@if (item.PaymentStatus != "paid") { }
}

새 청구 추가

@code { [CascadingParameter] private Task? AuthStateTask { get; set; } private List? revenues; private List clients = []; private Dictionary clientMap = new(); private bool isDialogOpen; private RevenueForm revenueForm = new(); private string ClientIdText { get => revenueForm.ClientId > 0 ? revenueForm.ClientId.ToString() : ""; set => revenueForm.ClientId = int.TryParse(value, out var id) ? id : 0; } private string InvoiceDateText { get => revenueForm.InvoiceDate?.ToString("yyyy-MM-dd") ?? ""; set => revenueForm.InvoiceDate = DateTime.TryParse(value, out var dt) ? dt : null; } private string AmountText { get => revenueForm.Amount?.ToString() ?? ""; set => revenueForm.Amount = decimal.TryParse(value, out var amt) ? amt : null; } private string DueDateText { get => revenueForm.DueDate?.ToString("yyyy-MM-dd") ?? ""; set => revenueForm.DueDate = DateTime.TryParse(value, out var dt) ? dt : null; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && 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) { await JS.InvokeVoidAsync("alert", $"데이터 로드 실패: {ex.Message}"); } } 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 (revenueForm.ClientId <= 0 || string.IsNullOrWhiteSpace(revenueForm.InvoiceNumber) || revenueForm.Amount is null) { await JS.InvokeVoidAsync("alert", "필수 항목을 입력해주세요."); return; } try { var newId = await RevenueClient.CreateAsync(revenueForm.ClientId, revenueForm.InvoiceNumber, revenueForm.InvoiceDate ?? DateTime.Today, revenueForm.Amount.Value, revenueForm.ServiceType, revenueForm.DueDate); if (newId > 0) { await JS.InvokeVoidAsync("alert", "청구가 추가되었습니다."); CloseDialog(); await LoadData(); } } catch (Exception ex) { await JS.InvokeVoidAsync("alert", $"저장 실패: {ex.Message}"); } } private async Task MarkPaid(int id) { try { await RevenueClient.MarkPaidAsync(id, DateTime.Now); await JS.InvokeVoidAsync("alert", "납부가 처리되었습니다."); await LoadData(); } catch (Exception ex) { await JS.InvokeVoidAsync("alert", $"처리 실패: {ex.Message}"); } } private async Task DeleteRevenue(int id) { if (!await JS.InvokeAsync("confirm", "이 청구를 삭제하시겠습니까?")) return; try { await RevenueClient.DeleteAsync(id); await JS.InvokeVoidAsync("alert", "청구가 삭제되었습니다."); await LoadData(); } catch (Exception ex) { await JS.InvokeVoidAsync("alert", $"삭제 실패: {ex.Message}"); } } 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 sealed 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; } } }