using Distribution.Features.Invoice; using Distribution.Infra.Authorization; using Distribution.Infra.UoW; using Distribution.Models; namespace Distribution.Features.Payment; public class PaymentService( IPaymentRepository repo, IInvoiceRepository invoiceRepo, IUnitOfWork uow, ICurrentUserService currentUser) { private static PaymentResponse ToResponse(Models.Payment p) => new(p.Id, p.FK_InvoiceId, p.Amount, p.Method, p.EntryMode, p.ChequeNumber, p.ChequeDate, p.CollectedAt, p.CollectedBy, p.RecordedBy, p.CreatedAt); public async Task?> GetByInvoiceIdAsync(Guid invoiceId) { var invoice = await invoiceRepo.GetByIdAsync(invoiceId); if (invoice is null || !invoice.IsActive) return null; var payments = await repo.GetByInvoiceIdAsync(invoiceId); return payments.Select(ToResponse).ToList(); } public async Task CreateAsync(Guid invoiceId, CreatePaymentRequest request, CancellationToken ct = default) { var invoice = await invoiceRepo.GetByIdAsync(invoiceId); if (invoice is null || !invoice.IsActive) return null; if (invoice.Status == InvoiceStatus.Finalized) throw new InvalidOperationException("Cannot record a payment against a finalized invoice"); if (request.Method == PaymentMethod.Cheque && (string.IsNullOrWhiteSpace(request.ChequeNumber) || request.ChequeDate is null)) throw new InvalidOperationException("ChequeNumber and ChequeDate are required when Method is Cheque"); var recorderId = currentUser.UserId ?? request.CollectedByUserId ?? throw new InvalidOperationException("CollectedByUserId is required while auth is unavailable"); var collectedBy = request.CollectedByUserId ?? recorderId; var recordedBy = request.EntryMode == EntryMode.DayEndManual && collectedBy != recorderId ? recorderId : (Guid?)null; var payment = new Models.Payment { FK_InvoiceId = invoiceId, Amount = request.Amount, Method = request.Method, EntryMode = request.EntryMode, ChequeNumber = request.Method == PaymentMethod.Cheque ? request.ChequeNumber : null, ChequeDate = request.Method == PaymentMethod.Cheque ? request.ChequeDate : null, CollectedAt = DateTime.Now, CollectedBy = collectedBy, RecordedBy = recordedBy }; await uow.BeginAsync(ct); try { await repo.AddAsync(payment); var allPayments = await repo.GetByInvoiceIdAsync(invoiceId); var total = allPayments.Sum(p => p.Amount) + request.Amount; invoice.PaymentStatus = total == 0 ? PaymentStatus.Pending : total < invoice.TotalAmount ? PaymentStatus.PartiallyPaid : PaymentStatus.Paid; invoiceRepo.Update(invoice); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } return ToResponse(payment); } }