using Microsoft.EntityFrameworkCore; using vendorApproverBackend.Data; using vendorApproverBackend.Enums; using vendorApproverBackend.Entities; using vendorApproverBackend.Requests; using vendorApproverBackend.Responses; namespace vendorApproverBackend.Services { public class PaymentDetailService { private readonly DataContext _context; public PaymentDetailService(DataContext context) { _context = context; } public async Task Save(PaymentDetail paymentDetail) { _context.PaymentDetail.Add(paymentDetail); await _context.SaveChangesAsync(); return paymentDetail; } public async Task Update(PaymentDetail paymentDetail) { _context.Entry(paymentDetail).State = EntityState.Modified; await _context.SaveChangesAsync(); return paymentDetail; } public async Task FindById(long id) { return await _context.PaymentDetail .Where(pd => pd.Id == id) .Include(pd => pd.PrequalificationRequest) .ThenInclude(pr => pr.Vendor) .ThenInclude(v => v.User) .FirstOrDefaultAsync(); } public async Task FindByPrequalificationRequestId(long requestId) { var request = await _context.PrequalificationRequests .Include(r => r.PaymentDetail) .FirstOrDefaultAsync(r => r.Id == requestId); return request?.PaymentDetail; } public async Task AddProductPaymentDetails(long productId, long? requestLetterId, AddPaymentDetailRequest dto) { // 1. Fetch from LetterProducts table instead of Products var letterProduct = await _context.LetterProducts.FindAsync(productId); if (letterProduct == null) { throw new Exception("Letter Product not found."); } // 2. Map the PaymentDetail (linked to the ProductId) var newPaymentDetail = new PaymentDetail { Amount = dto.Amount, PaymentMethode = dto.PaymentMethode, PaymentDate = dto.PaymentDate.ToUniversalTime(), Reference = dto.Reference, AttachementId = dto.AttachementId, Description = dto.Description, Status = PaymentStatus.PENDING, ProductId = productId, RequestLetterId = requestLetterId }; // 3. Save the payment detail using your existing logic await Save(newPaymentDetail); // 4. Update the letter product status specifically to PAYMENT_PENDING letterProduct.Status = LetterProductStatus.PAYMENT_PENDING; // 5. Commit changes to the LetterProduct table await _context.SaveChangesAsync(); return newPaymentDetail; } public async Task AddPaymentDetails(long requestId, AddPaymentDetailRequest dto) { var request = await _context.PrequalificationRequests.FindAsync(requestId); if (request == null) { throw new Exception("PrequalificationRequest not found."); } if (request.PaymentDetailId.HasValue) { throw new Exception("PrequalificationRequest already has a payment detail."); } var newPaymentDetail = new PaymentDetail { Amount = dto.Amount, PaymentMethode = dto.PaymentMethode, PaymentDate = dto.PaymentDate.ToUniversalTime(), Reference = dto.Reference, AttachementId = dto.AttachementId, Description = dto.Description, Status = PaymentStatus.PENDING }; newPaymentDetail.PrequalificationRequest = request; await Save(newPaymentDetail); request.PaymentDetail = newPaymentDetail; request.Status = RequestStatus.SUBMITTED; await _context.SaveChangesAsync(); return newPaymentDetail; } public async Task UpdatePaymentDetails(long requestId, AddPaymentDetailRequest dto) { var paymentDetail = await FindByPrequalificationRequestId(requestId); if (paymentDetail == null) { throw new Exception("PaymentDetail not found for the given request."); } paymentDetail.Amount = dto.Amount; paymentDetail.PaymentMethode = dto.PaymentMethode; paymentDetail.PaymentDate = dto.PaymentDate.ToUniversalTime(); paymentDetail.Reference = dto.Reference; paymentDetail.AttachementId = dto.AttachementId; paymentDetail.Description = dto.Description; return await Update(paymentDetail); } public async Task UpdatePaymentStatus(long requestId, UpdatePaymentDetailStatus dto) { var request = await _context.PrequalificationRequests .Include(r => r.PaymentDetail) .FirstOrDefaultAsync(r => r.Id == requestId); if (request?.PaymentDetail == null) { return false; } var paymentDetail = request.PaymentDetail; var currentStatus = paymentDetail.Status; paymentDetail.Status = dto.Status; if (currentStatus == PaymentStatus.PAID && dto.Status != PaymentStatus.PAID) { paymentDetail.AttachementId = null; request.Status = RequestStatus.SUBMITTED; } if (dto.Status == PaymentStatus.PAID) { paymentDetail.AttachementId = dto.AttachementId; request.Status = RequestStatus.CONFIRMED; } await _context.SaveChangesAsync(); return true; } public async Task UpdateProductPaymentStatus(long productId, UpdatePaymentDetailStatus dto) { // 1. Find the PaymentDetail that has the matching productId // Note: Since _context.PaymentDetails isn't available, we use Set() var paymentDetail = await _context.Set() .FirstOrDefaultAsync(p => p.ProductId == productId); if (paymentDetail == null) { return false; } // 2. Find the associated LetterProduct to update its status var letterProduct = await _context.LetterProducts.FindAsync(productId); if (letterProduct == null) { throw new Exception("Associated Letter Product not found."); } var currentStatus = paymentDetail.Status; paymentDetail.Status = dto.Status; // Logic: If status is set to PAID (Confirmed) if (dto.Status == PaymentStatus.PAID) { paymentDetail.AttachementId = dto.AttachementId; letterProduct.Status = LetterProductStatus.PAIED; // Or a specific 'PAID' status if you have one } // Logic: If payment was PAID but is now reversed/changed else if (currentStatus == PaymentStatus.PAID && dto.Status != PaymentStatus.PAID) { paymentDetail.AttachementId = null; letterProduct.Status = LetterProductStatus.PAYMENT_PENDING; } await _context.SaveChangesAsync(); return true; } public async Task Delete(long id) { var paymentDetail = await _context.PaymentDetail.FindAsync(id); if (paymentDetail == null) { return false; } _context.PaymentDetail.Remove(paymentDetail); await _context.SaveChangesAsync(); return true; } public async Task> FindByCriteria( int page, int size, long? id, PaymentStatus? status, PaymentMethode? paymentMethode, DateTime? paymentDate, double? amount, string vendorRepresentativeName, RequestStatus? requestStatus) { var paymentDetailQuery = _context.PaymentDetail .AsQueryable(); paymentDetailQuery = paymentDetailQuery .Include(pd => pd.PrequalificationRequest) .ThenInclude(pr => pr.Vendor) .ThenInclude(v => v.User); if (id.HasValue) { paymentDetailQuery = paymentDetailQuery.Where(pd => pd.Id == id.Value); } if (status.HasValue) { paymentDetailQuery = paymentDetailQuery.Where(pd => pd.Status == status.Value); } if (paymentMethode.HasValue) { paymentDetailQuery = paymentDetailQuery.Where(pd => pd.PaymentMethode == paymentMethode.Value); } if (amount.HasValue) { paymentDetailQuery = paymentDetailQuery.Where(pd => pd.Amount == amount.Value); } if (paymentDate.HasValue) { paymentDetailQuery = paymentDetailQuery.Where(pd => pd.PaymentDate.Date == paymentDate.Value.Date); } if (requestStatus.HasValue) { paymentDetailQuery = paymentDetailQuery .Where(pd => pd.PrequalificationRequest != null && pd.PrequalificationRequest.Status == requestStatus.Value); } if (!string.IsNullOrWhiteSpace(vendorRepresentativeName)) { paymentDetailQuery = paymentDetailQuery .Where(pd => pd.PrequalificationRequest != null && pd.PrequalificationRequest.Vendor != null && pd.PrequalificationRequest.Vendor.RepresentativeName.Contains(vendorRepresentativeName)); } var totalCount = await paymentDetailQuery.CountAsync(); var items = await paymentDetailQuery .OrderByDescending(pd => pd.Timestamp) .Skip(page * size) .Take(size) .ToListAsync(); return new PagedResult(items, totalCount, page, size); } } }