|
|
|
@@ -1,6 +1,7 @@
|
|
|
|
|
using Distribution.Features.Invoice;
|
|
|
|
|
using Distribution.Features.Notifications;
|
|
|
|
|
using Distribution.Features.PriceList;
|
|
|
|
|
using Distribution.Features.Scheme;
|
|
|
|
|
using Distribution.Features.Stock;
|
|
|
|
|
using Distribution.Infra.UoW;
|
|
|
|
|
using Distribution.Models;
|
|
|
|
@@ -11,9 +12,30 @@ public class ShopPOService(
|
|
|
|
|
IShopPORepository repo,
|
|
|
|
|
IInvoiceRepository invoiceRepo,
|
|
|
|
|
PriceListService priceListService,
|
|
|
|
|
SchemeService schemeService,
|
|
|
|
|
INotificationService notifications,
|
|
|
|
|
IUnitOfWork uow)
|
|
|
|
|
{
|
|
|
|
|
// Paid lines are priced from the request or the price list; accepted free-issue lines are always priced 0.
|
|
|
|
|
private async Task<ShopPOItem> BuildLineAsync(ShopPOItemRequest line, Guid storeId)
|
|
|
|
|
{
|
|
|
|
|
if (line.IsFree)
|
|
|
|
|
return new ShopPOItem
|
|
|
|
|
{
|
|
|
|
|
FK_ItemId = line.ItemId,
|
|
|
|
|
Quantity = line.Quantity,
|
|
|
|
|
UnitPrice = 0m,
|
|
|
|
|
IsFree = true,
|
|
|
|
|
FK_SchemeId = line.SchemeId
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var unitPrice = line.UnitPrice
|
|
|
|
|
?? (await priceListService.ResolveByStoreAsync(line.ItemId, storeId, DateTime.Now))?.Price
|
|
|
|
|
?? throw new InvalidOperationException($"No resolvable price for item {line.ItemId}");
|
|
|
|
|
|
|
|
|
|
return new ShopPOItem { FK_ItemId = line.ItemId, Quantity = line.Quantity, UnitPrice = unitPrice };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tells the person who raised the order (and their supervisor) what happened to it, unless they did it themselves.
|
|
|
|
|
private Task NotifyOrderAsync(string type, Models.ShopPO po, Guid actingUserId, string? reason, CancellationToken ct) =>
|
|
|
|
|
notifications.NotifyAsync(
|
|
|
|
@@ -22,14 +44,15 @@ public class ShopPOService(
|
|
|
|
|
new NotificationArgs(new { StoreName = po.Store?.StoreName, Reason = reason }, "ShopPO", po.Id, "/sales/orders", actingUserId),
|
|
|
|
|
ct);
|
|
|
|
|
|
|
|
|
|
private static ShopPOResponse ToResponse(Models.ShopPO p, Dictionary<Guid, (string Name, string Role)>? users = null) =>
|
|
|
|
|
private static ShopPOResponse ToResponse(Models.ShopPO p, Dictionary<Guid, (string Name, string Role)>? users = null, List<ShopPODiscountResponse>? discounts = null) =>
|
|
|
|
|
new(p.Id, p.FK_StoreId, p.Store?.StoreName ?? string.Empty,
|
|
|
|
|
p.Store?.StoreAddress, p.Store?.ContactNumber,
|
|
|
|
|
p.RaisedById, p.InvoiceId, p.TotalAmount, p.Status,
|
|
|
|
|
p.ConfirmedAt, p.ConfirmedBy, p.FulfilledAt,
|
|
|
|
|
p.Items.Where(i => i.IsActive).Select(i =>
|
|
|
|
|
new ShopPOItemResponse(i.Id, i.FK_ItemId, i.Item?.Name ?? string.Empty,
|
|
|
|
|
i.Quantity, i.QuantityFulfilled, i.UnitPrice, i.IsActive)).ToList(),
|
|
|
|
|
i.Quantity, i.QuantityFulfilled, i.UnitPrice, i.IsActive,
|
|
|
|
|
i.IsFree, i.FK_SchemeId, i.SchemeName)).ToList(),
|
|
|
|
|
p.IsActive, p.CreatedAt,
|
|
|
|
|
users != null && users.TryGetValue(p.RaisedById, out var u) ? u.Name : null,
|
|
|
|
|
users != null && users.TryGetValue(p.RaisedById, out var r) ? r.Role : null,
|
|
|
|
@@ -37,7 +60,8 @@ public class ShopPOService(
|
|
|
|
|
users != null && p.ConfirmedBy is Guid cb2 && users.TryGetValue(cb2, out var c2) ? c2.Role : null,
|
|
|
|
|
p.CancelledBy, p.CancelledAt, p.CancelReason,
|
|
|
|
|
users != null && p.CancelledBy is Guid xb && users.TryGetValue(xb, out var x) ? x.Name : null,
|
|
|
|
|
users != null && p.CancelledBy is Guid xb2 && users.TryGetValue(xb2, out var x2) ? x2.Role : null);
|
|
|
|
|
users != null && p.CancelledBy is Guid xb2 && users.TryGetValue(xb2, out var x2) ? x2.Role : null,
|
|
|
|
|
discounts ?? []);
|
|
|
|
|
|
|
|
|
|
private static IEnumerable<Guid> UserIds(Models.ShopPO p)
|
|
|
|
|
{
|
|
|
|
@@ -62,7 +86,7 @@ public class ShopPOService(
|
|
|
|
|
var po = await repo.GetByIdAsync(id);
|
|
|
|
|
if (po is null) return null;
|
|
|
|
|
var users = await repo.GetUserInfoAsync(UserIds(po));
|
|
|
|
|
return ToResponse(po, users);
|
|
|
|
|
return ToResponse(po, users, await schemeService.GetDiscountsForShopPOAsync(po.Id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<List<ShopPOResponse>> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId)
|
|
|
|
@@ -82,20 +106,14 @@ public class ShopPOService(
|
|
|
|
|
|
|
|
|
|
foreach (var line in request.Items)
|
|
|
|
|
{
|
|
|
|
|
var unitPrice = line.UnitPrice
|
|
|
|
|
?? (await priceListService.ResolveByStoreAsync(line.ItemId, request.StoreId, DateTime.Now))?.Price
|
|
|
|
|
?? throw new InvalidOperationException($"No resolvable price for item {line.ItemId}");
|
|
|
|
|
|
|
|
|
|
items.Add(new ShopPOItem
|
|
|
|
|
{
|
|
|
|
|
FK_ItemId = line.ItemId,
|
|
|
|
|
Quantity = line.Quantity,
|
|
|
|
|
QuantityFulfilled = 0,
|
|
|
|
|
UnitPrice = unitPrice
|
|
|
|
|
});
|
|
|
|
|
total += line.Quantity * unitPrice;
|
|
|
|
|
var item = await BuildLineAsync(line, request.StoreId);
|
|
|
|
|
items.Add(item);
|
|
|
|
|
total += item.Quantity * item.UnitPrice;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await schemeService.ValidateFreeLinesAsync(request.StoreId, DateTime.Now, items);
|
|
|
|
|
await schemeService.ValidateDiscountSchemesAsync(request.StoreId, DateTime.Now, items, request.DiscountSchemeIds);
|
|
|
|
|
|
|
|
|
|
var po = new Models.ShopPO
|
|
|
|
|
{
|
|
|
|
|
FK_StoreId = request.StoreId,
|
|
|
|
@@ -110,6 +128,7 @@ public class ShopPOService(
|
|
|
|
|
await repo.AddAsync(po);
|
|
|
|
|
foreach (var item in items) item.FK_ShopPOId = po.Id;
|
|
|
|
|
await repo.AddItemsAsync(items);
|
|
|
|
|
await schemeService.SyncRedemptionsAsync(po.Id, items, request.DiscountSchemeIds);
|
|
|
|
|
await uow.CommitAsync(ct);
|
|
|
|
|
}
|
|
|
|
|
catch { await uow.RollbackAsync(ct); throw; }
|
|
|
|
@@ -137,27 +156,22 @@ public class ShopPOService(
|
|
|
|
|
|
|
|
|
|
foreach (var line in request.Items)
|
|
|
|
|
{
|
|
|
|
|
var unitPrice = line.UnitPrice
|
|
|
|
|
?? (await priceListService.ResolveByStoreAsync(line.ItemId, po.FK_StoreId, DateTime.Now))?.Price
|
|
|
|
|
?? throw new InvalidOperationException($"No resolvable price for item {line.ItemId}");
|
|
|
|
|
|
|
|
|
|
newItems.Add(new ShopPOItem
|
|
|
|
|
{
|
|
|
|
|
FK_ShopPOId = id,
|
|
|
|
|
FK_ItemId = line.ItemId,
|
|
|
|
|
Quantity = line.Quantity,
|
|
|
|
|
QuantityFulfilled = 0,
|
|
|
|
|
UnitPrice = unitPrice
|
|
|
|
|
});
|
|
|
|
|
total += line.Quantity * unitPrice;
|
|
|
|
|
var item = await BuildLineAsync(line, po.FK_StoreId);
|
|
|
|
|
item.FK_ShopPOId = id;
|
|
|
|
|
newItems.Add(item);
|
|
|
|
|
total += item.Quantity * item.UnitPrice;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await schemeService.ValidateFreeLinesAsync(po.FK_StoreId, po.CreatedAt, newItems);
|
|
|
|
|
await schemeService.ValidateDiscountSchemesAsync(po.FK_StoreId, po.CreatedAt, newItems, request.DiscountSchemeIds);
|
|
|
|
|
|
|
|
|
|
po.TotalAmount = total;
|
|
|
|
|
|
|
|
|
|
await uow.BeginAsync(ct);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await repo.ReplaceItemsAsync(id, newItems);
|
|
|
|
|
await schemeService.SyncRedemptionsAsync(id, newItems, request.DiscountSchemeIds);
|
|
|
|
|
repo.Update(po);
|
|
|
|
|
await uow.CommitAsync(ct);
|
|
|
|
|
}
|
|
|
|
@@ -212,6 +226,9 @@ public class ShopPOService(
|
|
|
|
|
CreatedBy = userId,
|
|
|
|
|
TotalAmount = po.TotalAmount
|
|
|
|
|
};
|
|
|
|
|
// Start the invoice at the discounted total so it matches what print/finalize will recompute.
|
|
|
|
|
invoice.DiscountAmount = Math.Min(await invoiceRepo.GetSchemeDiscountAsync(po.Id), po.TotalAmount);
|
|
|
|
|
invoice.TotalAmount = po.TotalAmount - invoice.DiscountAmount;
|
|
|
|
|
|
|
|
|
|
await uow.BeginAsync(ct);
|
|
|
|
|
try
|
|
|
|
|