@page "/admin/contracts" @rendermode @(new InteractiveWebAssemblyRenderMode(prerender: true)) @using TaxBaik.Web.Services.AdminClients @using TaxBaik.WasmClient.Components.Admin.Shared @inject IContractBrowserClient ContractClient @inject IClientBrowserClient ClientClient @inject ISnackbar Snackbar @inject IDialogService DialogService @attribute [Authorize] 계약 관리
CRM & 세무관리 계약 관리 고객 계약과 월 정기수익을 함께 관리합니다. @if (mrr > 0) { 월 정기수익: ₩@mrr.ToString("N0") }
새 계약 추가
@if (contracts is null) { } else { @if (contracts.Count == 0) { 계약이 없습니다. } else { @foreach (var item in contracts) { var isActive = !item.EndDate.HasValue || item.EndDate.Value >= DateTime.Today; }
ID 고객 계약번호 서비스 유형 월 수수료 계약기간 상태 작업
@item.Id @clientMap.GetValueOrDefault(item.ClientId) @item.ContractNumber @item.ServiceType @(item.MonthlyFee?.ToString("C") ?? string.Empty) @item.StartDate.ToString("yyyy-MM-dd") @if (item.EndDate.HasValue) { ~@item.EndDate.Value.ToString("yyyy-MM-dd") } @(isActive ? "활성" : "만료")
}
@(isEditMode ? "계약 상세 정보" : "새 계약 추가") @if (isEditMode) { 새로 작성 }
@if (isEditMode) { 삭제 } else { 저장 }
}
@code { [CascadingParameter] private Task? AuthStateTask { get; set; } private List? contracts; private List clients = []; private Dictionary clientMap = new(); private decimal mrr = 0; private MudForm? form; private bool isEditMode; private Contract? selectedContract; private ContractFormModel contractForm = new(); private RenderFragment ContractSkeleton => builder => { builder.OpenComponent(0); builder.AddAttribute(1, "Rows", 6); builder.AddAttribute(2, "Columns", 4); builder.CloseComponent(); }; protected override async Task OnInitializedAsync() { if (AuthStateTask != null) { var authState = await AuthStateTask; if (authState.User.Identity?.IsAuthenticated == true) { await LoadData(); PrepareCreate(); } else { contracts = []; } } else { contracts = []; } } private async Task LoadData() { try { contracts = await ContractClient.GetAllAsync(); var (clientItems, _) = await ClientClient.GetPagedAsync(pageSize: 1000); clients = clientItems.ToList(); clientMap = clients.ToDictionary(c => c.Id, GetClientDisplayName); mrr = await ContractClient.GetMonthlyRecurringRevenueAsync(); } catch (Exception ex) { Snackbar.Add($"데이터 로드 실패: {ex.Message}", Severity.Error); } } private void PrepareCreate() { selectedContract = null; isEditMode = false; contractForm = new ContractFormModel { ClientId = clients.FirstOrDefault()?.Id, StartDate = DateTime.Today }; } private void OnRowSelected(Contract contract) { if (contract == null) return; selectedContract = contract; isEditMode = true; contractForm = new ContractFormModel { ClientId = contract.ClientId, ContractNumber = contract.ContractNumber, ServiceType = contract.ServiceType, StartDate = contract.StartDate, MonthlyFee = contract.MonthlyFee }; } private async Task SaveContract() { if (form != null) { await form.Validate(); if (!form.IsValid) { Snackbar.Add("필수 항목을 입력해주세요.", Severity.Warning); return; } } try { if (contractForm.ClientId == null) return; var newId = await ContractClient.CreateAsync( contractForm.ClientId.Value, contractForm.ContractNumber, contractForm.ServiceType, contractForm.StartDate ?? DateTime.Now, contractForm.MonthlyFee); if (newId > 0) { Snackbar.Add("계약이 추가되었습니다.", Severity.Success); PrepareCreate(); await LoadData(); } } catch (Exception ex) { Snackbar.Add($"저장 실패: {ex.Message}", Severity.Error); } } private async Task DeleteContract(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 ContractClient.DeleteAsync(id); Snackbar.Add("계약이 삭제되었습니다.", Severity.Success); if (selectedContract?.Id == id) { PrepareCreate(); } await LoadData(); } catch (Exception ex) { Snackbar.Add($"삭제 실패: {ex.Message}", Severity.Error); } } private static string GetClientDisplayName(Client client) => !string.IsNullOrWhiteSpace(client.CompanyName) ? client.CompanyName : !string.IsNullOrWhiteSpace(client.Name) ? client.Name : $"Client #{client.Id}"; }