using AccountsDAL.CustomCode.Payment; using AccountsDAL.DTO.Payment; using AccountsDAL.DTO.Voucher; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using MMDAL.DTO.CostCenterAllocation; using Newtonsoft.Json; namespace AccountsBLL.Payment { public class PaymentBLL : IPaymentBLL { private readonly IPaymentDAL _paymentDAL; public PaymentBLL(IPaymentDAL paymentDAL) { _paymentDAL = paymentDAL; } public Task GetSelectListPayment(int firstNumber, int maxResult, CriteriaDTO criteriaDTO, LoginDTO loginDTO) => _paymentDAL.GetSelectListPayment(firstNumber, maxResult, criteriaDTO, loginDTO); // Ported from GB4 PaymentBLL.PaymentToVoucher — a pure read-only "derive a proposed // Voucher preview from an existing Payment" transform, never persisted. Distinct accounts // (PayTo and PayFrom, excluding the -1 sentinel) each become one VoucherDetailDTO line, // amount summed across every detail row sharing that account. // // Preserved GB4 quirk, not "fixed": cost-center allocations are looked up against only the // FIRST detail row per distinct account (matching GB4's GroupBy(...).Select(g => g.First())), // even though the line's amount is summed across every detail row for that account — so a // second/third detail row sharing an account contributes to the total but its own // cost-center allocations (if any) are silently dropped. public async Task GetPaymentToVoucher(int paymentId, LoginDTO loginDTO) { var header = await _paymentDAL.GetPaymentHeaderForVoucher(paymentId, loginDTO).ConfigureAwait(false); if (header == null) throw new InvalidOperationException($"Payment {paymentId} not found."); var details = await _paymentDAL.GetPaymentDetailsForVoucher(paymentId, loginDTO).ConfigureAwait(false); var costCenterAllocations = await _paymentDAL.GetCostCenterAllocationForPayment(paymentId, loginDTO).ConfigureAwait(false); var organizationUnit = await _paymentDAL.GetOrganizationUnitForVoucher(loginDTO.WorkOUId, loginDTO).ConfigureAwait(false); var costCenterApplicableCache = new Dictionary(); var voucherDTO = new VoucherDTO { VoucherReferenceDate = header.PaymentReferenceDate, VoucherReferenceNumber = header.PaymentReferenceNumber, VoucherVoucherDate = header.PaymentDate, VoucherValidTillDate = new DateTime(9999, 12, 31), VoucherNarration = header.PaymentRemarks, VoucherPostedFromType = 7, // Payment VoucherPostedFromId = paymentId }; short slno = 1; var payToFirstRows = details.Where(d => d.PayToAccountId != -1) .GroupBy(d => d.PayToAccountId) .Select(g => g.First()); foreach (var first in payToFirstRows) { decimal amount = details.Where(d => d.PayToAccountId == first.PayToAccountId) .Sum(d => (decimal)(d.AmountRequired - d.AmountPaid)); var voucherDetail = new VoucherDetailDTO { VoucherAccountId = first.PayToAccountId, InterOUId = loginDTO.WorkOUId, InterOUCode = organizationUnit?.OUCode ?? string.Empty, InterOUName = organizationUnit?.OUName ?? string.Empty, VoucherAccountCode = first.PayToAccountCode, VoucherAccountName = first.PayToAccountName, VoucherAccountBillAllocationType = first.PayToAccountBillAllocationType, VoucherAccountType = first.PayToAccountType, VoucherAccountAccountGroupId = first.PayToAccountGroupId, VoucherDetailDetailType = 0, VoucherDetailAmount = (double)amount, VoucherDetailAmountFC = (double)amount, CurrencyId = first.PayToCurrencyId, CurrencyCode = first.PayToCurrencyCode, CurrencyName = first.PayToCurrencyName, VoucherDetailCurrencyConversion = 1, VoucherDetailVoucherLineNarration = first.Remarks, VoucherDetailSlno = slno, VoucherDetailCostCenterApplicable = await IsCostCenterApplicable(first.PayToAccountGroupId, costCenterApplicableCache, loginDTO).ConfigureAwait(false) }; var lineCostCenters = costCenterAllocations.Where(c => c.CostCenterAllocationObjectId == first.PaymentDetailId).ToList(); if (lineCostCenters.Count > 0) voucherDetail.VoucherCostCenterArray = lineCostCenters.Select(MapToVoucherCostCenter).ToList(); slno++; voucherDTO.VoucherDetailArray.Add(voucherDetail); } var payFromFirstRows = details.Where(d => d.PayFromAccountId != -1) .GroupBy(d => d.PayFromAccountId) .Select(g => g.First()); foreach (var first in payFromFirstRows) { decimal amount = details.Where(d => d.PayFromAccountId == first.PayFromAccountId) .Sum(d => (decimal)(d.AmountRequired - d.AmountPaid)); var voucherDetail = new VoucherDetailDTO { VoucherAccountId = first.PayFromAccountId, InterOUId = loginDTO.WorkOUId, InterOUCode = organizationUnit?.OUCode ?? string.Empty, InterOUName = organizationUnit?.OUName ?? string.Empty, VoucherAccountCode = first.PayFromAccountCode, VoucherAccountName = first.PayFromAccountName, VoucherAccountBillAllocationType = first.PayFromAccountBillAllocationType, VoucherAccountType = first.PayFromAccountType, VoucherAccountAccountGroupId = first.PayFromAccountGroupId, VoucherDetailDetailType = 1, VoucherDetailAmount = (double)amount, VoucherDetailAmountFC = (double)amount, CurrencyId = first.PayFromCurrencyId, CurrencyCode = first.PayFromCurrencyCode, CurrencyName = first.PayFromCurrencyName, VoucherDetailCurrencyConversion = 1, VoucherDetailVoucherLineNarration = first.Remarks, VoucherDetailSlno = slno, VoucherDetailCostCenterApplicable = await IsCostCenterApplicable(first.PayFromAccountGroupId, costCenterApplicableCache, loginDTO).ConfigureAwait(false) }; var lineCostCenters = costCenterAllocations.Where(c => c.CostCenterAllocationObjectId == first.PaymentDetailId).ToList(); if (lineCostCenters.Count > 0) voucherDetail.VoucherCostCenterArray = lineCostCenters.Select(MapToVoucherCostCenter).ToList(); slno++; voucherDTO.VoucherDetailArray.Add(voucherDetail); } return JsonConvert.SerializeObject(voucherDTO); } private async Task IsCostCenterApplicable(int accountGroupId, Dictionary cache, LoginDTO loginDTO) { if (!cache.TryGetValue(accountGroupId, out var hasCondition)) { hasCondition = await _paymentDAL.HasCostCategoryConditionForAccountGroup(accountGroupId, loginDTO).ConfigureAwait(false); cache[accountGroupId] = hasCondition; } // GB4: a matching MCOSTCATEGORYCONDITION means cost-center is NOT applicable (0); // no match means it IS applicable (1, the default). return hasCondition ? 0 : 1; } private static VoucherCostCenterDTO MapToVoucherCostCenter(CostCenterAllocationDTO source) => new() { VoucherCostCenterCostCategorySlno = source.CostCenterAllocationCostCategorySlno, CostCategoryId = source.CostCategoryId, CostCategoryCode = source.CostCategoryCode, CostCategoryName = source.CostCategoryName, VoucherCostCenterSlno = source.CostCenterAllocationSlno, CostCenterId = source.CostCenterId, CostCenterCode = source.CostCenterCode, CostCenterName = source.CostCenterName, VoucherCostCenterAllocatedAmount = source.CostCenterAllocationAllocatedQuantity }; } }