ca81e9bc42
- Implemented General Ledger client component for managing GL integration. - Created a dedicated page for General Ledger configuration. - Developed Finance Report client component for generating various financial reports. - Added report blocks for structured display of financial data. - Introduced API endpoints for fetching GL accounts and financial reports. - Updated navigation to include links for GL integration and financial reports. - Enhanced permissions to manage GL and view financial reports. - Updated appsettings to include General Ledger API configuration.
106 lines
4.2 KiB
C#
106 lines
4.2 KiB
C#
using Distribution.Features.GeneralLedger;
|
|
using Distribution.Features.Stock;
|
|
using Distribution.Infra.Authorization;
|
|
using Distribution.Infra.UoW;
|
|
using Distribution.Models;
|
|
|
|
namespace Distribution.Features.Invoice;
|
|
|
|
public class InvoiceService(
|
|
IInvoiceRepository repo,
|
|
IUnitOfWork uow,
|
|
ICurrentUserService currentUser,
|
|
GlPostingService gl)
|
|
{
|
|
private static InvoiceResponse ToResponse(Models.Invoice i) =>
|
|
new(i.Id, i.FK_ShopPOId,
|
|
i.ShopPO?.FK_StoreId ?? Guid.Empty,
|
|
i.ShopPO?.Store?.StoreName ?? string.Empty,
|
|
i.Status, i.PaymentStatus, i.CreatedBy,
|
|
i.PrintedAt, i.PrintedBy,
|
|
i.FinalizedAt, i.FinalizedBy,
|
|
i.TotalAmount,
|
|
i.Payments?.Where(p => p.IsActive).Sum(p => p.Amount) ?? 0m,
|
|
i.IsActive, i.CreatedAt);
|
|
|
|
public async Task<InvoiceResponse?> GetByIdAsync(Guid id)
|
|
{
|
|
var invoice = await repo.GetByIdAsync(id);
|
|
return invoice is null ? null : ToResponse(invoice);
|
|
}
|
|
|
|
public async Task<PagedResponse<InvoiceResponse>> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, PaymentStatus? paymentStatus, int page, int pageSize)
|
|
{
|
|
var total = await repo.GetCountAsync(agentId, routeId, shopId, status, paymentStatus);
|
|
var items = await repo.GetAllAsync(agentId, routeId, shopId, status, paymentStatus, page, pageSize);
|
|
return new PagedResponse<InvoiceResponse>(page, pageSize, total, items.Select(ToResponse).ToList());
|
|
}
|
|
|
|
public async Task<OutstandingPaymentsResponse> GetOutstandingSummaryAsync()
|
|
{
|
|
var invoices = await repo.GetOutstandingAsync();
|
|
var count = invoices.Count;
|
|
var outstanding = invoices.Sum(i => i.TotalAmount - i.Payments.Sum(p => p.Amount));
|
|
return new OutstandingPaymentsResponse(count, outstanding);
|
|
}
|
|
|
|
public async Task<InvoiceResponse> PrintAsync(Guid id, Guid printedBy, CancellationToken ct = default)
|
|
{
|
|
var invoice = await repo.GetByIdAsync(id)
|
|
?? throw new KeyNotFoundException("Invoice not found");
|
|
|
|
if (invoice.Status != InvoiceStatus.Draft)
|
|
throw new InvalidOperationException($"Cannot print an invoice in {invoice.Status} status");
|
|
|
|
invoice.Status = InvoiceStatus.Printed;
|
|
invoice.PrintedAt = DateTime.Now;
|
|
invoice.PrintedBy = printedBy;
|
|
|
|
await uow.BeginAsync(ct);
|
|
try { repo.Update(invoice); await uow.CommitAsync(ct); }
|
|
catch { await uow.RollbackAsync(ct); throw; }
|
|
|
|
return ToResponse(invoice);
|
|
}
|
|
|
|
public async Task<InvoiceResponse> FinalizeAsync(Guid id, FinalizeInvoiceRequest request, Guid finalizedBy, CancellationToken ct = default)
|
|
{
|
|
var invoice = await repo.GetByIdAsync(id)
|
|
?? throw new KeyNotFoundException("Invoice not found");
|
|
|
|
if (invoice.Status != InvoiceStatus.Printed)
|
|
throw new InvalidOperationException($"Cannot finalize an invoice in {invoice.Status} status");
|
|
|
|
var adjustments = request.Adjustments ?? [];
|
|
if (adjustments.Count > 0)
|
|
{
|
|
var poItems = invoice.ShopPO.Items.Where(i => i.IsActive).ToList();
|
|
decimal total = 0;
|
|
foreach (var item in poItems)
|
|
{
|
|
var adjustment = adjustments.FirstOrDefault(a => a.ShopPOItemId == item.Id);
|
|
var billedQuantity = adjustment is not null ? adjustment.AdjustedQuantity : item.Quantity;
|
|
|
|
if (billedQuantity < 0 || billedQuantity > item.Quantity)
|
|
throw new InvalidOperationException(
|
|
$"Adjusted quantity for item {item.Id} must be between 0 and {item.Quantity}");
|
|
|
|
total += billedQuantity * item.UnitPrice;
|
|
}
|
|
invoice.TotalAmount = total;
|
|
}
|
|
|
|
invoice.Status = InvoiceStatus.Finalized;
|
|
invoice.FinalizedAt = DateTime.Now;
|
|
invoice.FinalizedBy = finalizedBy;
|
|
|
|
await uow.BeginAsync(ct);
|
|
try { repo.Update(invoice); await uow.CommitAsync(ct); }
|
|
catch { await uow.RollbackAsync(ct); throw; }
|
|
|
|
await gl.PostInvoiceFinalizedAsync(invoice, ct);
|
|
|
|
return ToResponse(invoice);
|
|
}
|
|
}
|