using eprintServer.Data; using eprintServer.Entities; using eprintServer.Requests; using eprintServer.Enums; using eprintServer.Responses; using Microsoft.EntityFrameworkCore; namespace eprintServer.Services; public class InvoiceService { private readonly DataContext _context; public InvoiceService(DataContext context) { _context = context; } /* public async Task CreateInvoiceAsync(InvoiceRequest request) { var invoice = new Invoice { UserId = request.UserId, DiscountPercentage = request.DiscountPercentage, TaxAmount = request.TaxAmount, InvoiceDate = DateTime.UtcNow, Status = InvoiceStatus.PAID }; decimal subtotal = 0; if (request.SellableProductIds != null) { foreach (var id in request.SellableProductIds) { var p = await _context.SellableProducts.FindAsync(id); if (p != null) { decimal price = (decimal)p.Price; invoice.InvoiceItems.Add(new OrderItem { SellableProductId = id, Quantity = 1, UnitPrice = price }); subtotal += price; } } } if (request.CustomizableProductIds != null) { foreach (var id in request.CustomizableProductIds) { var cp = await _context.CustomizableProducts.FindAsync(id); if (cp != null) { invoice.InvoiceItems.Add(new OrderItem { CustomizableProductId = id, Quantity = 1, UnitPrice = cp.BasePrice }); subtotal += cp.BasePrice; } } } if (request.OfferIds != null) { foreach (var id in request.OfferIds) { var o = await _context.Offers.FindAsync((int)id); decimal price = o?.OfferPrice ?? 0; invoice.InvoiceItems.Add(new OrderItem { OfferId = id, Quantity = 1, UnitPrice = price }); subtotal += price; } } invoice.DiscountAmount = subtotal * (decimal)(request.DiscountPercentage / 100); invoice.TotalAmount = (subtotal - invoice.DiscountAmount) + request.TaxAmount; _context.Invoices.Add(invoice); await _context.SaveChangesAsync(); return invoice; } */ public async Task CreateInvoiceAsync(InvoiceRequest request) { Order? order = null; long finalOrderId; // 1. Handle the Order Relationship if (request.OrderId > 0) { order = await _context.Orders .Include(o => o.OrderItems) .FirstOrDefaultAsync(o => o.Id == request.OrderId); if (order == null) throw new Exception($"Order {request.OrderId} not found."); finalOrderId = order.Id; } else { // No OrderId passed? Create a placeholder order to satisfy the DB constraint order = new Order { UserId = request.UserId, OrderDate = DateTime.UtcNow, Status = OrderStatus.PENDING, TotalAmount = 0 }; _context.Orders.Add(order); await _context.SaveChangesAsync(); // Get the ID finalOrderId = order.Id; } // 2. Initialize the Invoice var invoice = new Invoice { OrderId = finalOrderId, UserId = request.UserId, DiscountPercentage = request.DiscountPercentage, TaxAmount = request.TaxAmount, InvoiceDate = DateTime.UtcNow, Status = InvoiceStatus.PAID }; decimal subtotal = 0; // 3. Add EXISTING OrderItems (from the Order) if (order.OrderItems != null) { foreach (var item in order.OrderItems) { invoice.InvoiceItems.Add(item); subtotal += item.UnitPrice * item.Quantity; } } // 4. Add EXTRA Items (from the Lists) // Sellable Products if (request.SellableProductIds != null) { foreach (var id in request.SellableProductIds) { var p = await _context.SellableProducts.FindAsync(id); if (p != null) { decimal price = (decimal)p.Price; invoice.InvoiceItems.Add(new OrderItem { SellableProductId = id, Quantity = 1, UnitPrice = price, OrderId = finalOrderId }); subtotal += price; } } } // Customizable Products if (request.CustomizableProductIds != null) { foreach (var id in request.CustomizableProductIds) { var cp = await _context.CustomizableProducts.FindAsync(id); if (cp != null) { invoice.InvoiceItems.Add(new OrderItem { CustomizableProductId = id, Quantity = 1, UnitPrice = cp.BasePrice, OrderId = finalOrderId }); subtotal += cp.BasePrice; } } } // Offers if (request.OfferIds != null) { foreach (var id in request.OfferIds) { var o = await _context.Offers.FindAsync((int)id); decimal price = o?.OfferPrice ?? 0; invoice.InvoiceItems.Add(new OrderItem { OfferId = id, Quantity = 1, UnitPrice = price, OrderId = finalOrderId }); subtotal += price; } } // 5. Final Calculations invoice.DiscountAmount = subtotal * (decimal)(request.DiscountPercentage / 100); invoice.TotalAmount = (subtotal - invoice.DiscountAmount) + request.TaxAmount; // Update the Order total to match the finalized invoice order.TotalAmount = invoice.TotalAmount; // 6. Save everything _context.Invoices.Add(invoice); await _context.SaveChangesAsync(); return invoice; } public async Task GetByIdAsync(long id) { return await _context.Invoices .Include(i => i.User) .Include(i => i.InvoiceItems) .ThenInclude(ii => ii.SellableProduct) .Include(i => i.InvoiceItems) .ThenInclude(ii => ii.CustomizableProduct) .FirstOrDefaultAsync(i => i.Id == id); } public async Task> GetPagedInvoicesAsync(int page, int size, long? userId) { var query = _context.Invoices .Include(i => i.User) // 1. Include the items .Include(i => i.InvoiceItems) .ThenInclude(oi => oi.SellableProduct) // 2. Include customizable products .Include(i => i.InvoiceItems) .ThenInclude(oi => oi.CustomizableProduct) // 3. Include deep user customization details (Mirroring your Order logic) .Include(i => i.InvoiceItems) .ThenInclude(oi => oi.UserCustomization) .ThenInclude(uc => uc.Steps) .ThenInclude(s => s.FieldValues) .Include(i => i.InvoiceItems) .ThenInclude(oi => oi.UserCustomization) .ThenInclude(uc => uc.Steps) .ThenInclude(s => s.UserMockup) .ThenInclude(um => um.File) .AsQueryable(); if (userId.HasValue) query = query.Where(i => i.UserId == userId.Value); int totalCount = await query.CountAsync(); var items = await query.OrderByDescending(i => i.InvoiceDate) .Skip(page * size) .Take(size) .ToListAsync(); return new PagedResult(items, totalCount, page, size); } public async Task DeleteInvoiceAsync(long id) { var invoice = await _context.Invoices.FindAsync(id); if (invoice == null) return false; _context.Invoices.Remove(invoice); await _context.SaveChangesAsync(); return true; } public async Task GetInvoiceStatsAsync(long? userId = null) { var query = _context.Invoices.AsQueryable(); if (userId.HasValue) { query = query.Where(i => i.UserId == userId.Value); } return new InvoiceStatsResponse { TotalInvoicesAmount = await query .SumAsync(i => i.TotalAmount), TotalPaidInvoicesAmount = await query .Where(i => i.Status == InvoiceStatus.PAID) .SumAsync(i => i.TotalAmount), TotalPendingInvoicesAmount = await query .Where(i => i.Status == InvoiceStatus.SUBMITTED) // Assuming SUBMITTED is Pending .SumAsync(i => i.TotalAmount), TotalRefundedInvoicesAmount = await query .Where(i => i.Status == InvoiceStatus.REFUNDED) .SumAsync(i => i.TotalAmount) }; } }