sales fixers
This commit is contained in:
@@ -67,7 +67,7 @@ public sealed class BundleSaleService : IBundleSaleService
|
||||
var total = await q.CountAsync(ct);
|
||||
var rows = await q.OrderByDescending(x => x.BundleSaleTemplateId).Skip(query.Skip).Take(query.PageSize).ToListAsync(ct);
|
||||
return PagedResponse<BundleSaleTemplateSummaryDto>.Create(rows.Select(x => new BundleSaleTemplateSummaryDto(
|
||||
x.BundleSaleTemplateId, x.TemplateCode, x.TemplateName, x.Description, x.Status, x.Lines.Count, x.CreatedAt, x.UpdatedAt)).ToList(), query.Page, query.PageSize, total);
|
||||
x.BundleSaleTemplateId, x.TemplateCode, x.TemplateName, x.Description, x.BundlePrice, x.Status, x.Lines.Count, x.CreatedAt, x.UpdatedAt)).ToList(), query.Page, query.PageSize, total);
|
||||
}
|
||||
|
||||
public async Task<BundleSaleTemplateDto?> GetTemplateAsync(int bundleSaleTemplateId, CancellationToken ct = default)
|
||||
@@ -78,6 +78,7 @@ public sealed class BundleSaleService : IBundleSaleService
|
||||
template.TemplateCode,
|
||||
template.TemplateName,
|
||||
template.Description,
|
||||
template.BundlePrice,
|
||||
template.Status,
|
||||
template.CreatedAt,
|
||||
template.UpdatedAt,
|
||||
@@ -92,10 +93,18 @@ public sealed class BundleSaleService : IBundleSaleService
|
||||
TemplateCode = await _numbers.NextAsync(DocumentTypes.BundleSaleTemplate, ct),
|
||||
TemplateName = request.TemplateName,
|
||||
Description = request.Description,
|
||||
BundlePrice = request.BundlePrice ?? 0m,
|
||||
Status = EntityStatus.Active,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
if (request.BundlePrice is { } bundlePrice)
|
||||
{
|
||||
if (!request.Lines.Any(l => l.IncludeInBundle))
|
||||
throw new DomainException(ErrorCodes.Validation, "A bundle needs at least one included line to carry its price.", 422);
|
||||
AllocateBundlePrice(request.Lines, bundlePrice);
|
||||
}
|
||||
|
||||
foreach (var line in request.Lines)
|
||||
{
|
||||
if (!await _items.Query().AsNoTracking().AnyAsync(x => x.ItemId == line.ItemId, ct))
|
||||
@@ -118,12 +127,38 @@ public sealed class BundleSaleService : IBundleSaleService
|
||||
await _uow.SaveChangesAsync(ct);
|
||||
|
||||
return new BundleSaleTemplateDto(
|
||||
template.BundleSaleTemplateId, template.TemplateCode, template.TemplateName, template.Description,
|
||||
template.BundleSaleTemplateId, template.TemplateCode, template.TemplateName, template.Description, template.BundlePrice,
|
||||
template.Status, template.CreatedAt, template.UpdatedAt,
|
||||
template.Lines.OrderBy(x => x.SortOrder).Select(x => new BundleSaleTemplateLineDto(
|
||||
x.BundleSaleTemplateLineId, x.ItemId, x.WarehouseId, x.Qty, x.UnitPrice, x.IncludeInBundle, x.SortOrder)).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the bundle price over the included lines' unit prices, in proportion to each line's
|
||||
/// UnitPrice × Qty as sent (the items' normal value), or to Qty when no line carries a price.
|
||||
/// Line totals are rounded to 2 dp with the last included line taking the rounding remainder, so the
|
||||
/// totals add up to the bundle price; excluded lines are priced 0.
|
||||
/// </summary>
|
||||
private static void AllocateBundlePrice(List<CreateBundleSaleTemplateLineRequest> lines, decimal bundlePrice)
|
||||
{
|
||||
var included = lines.Where(l => l.IncludeInBundle).ToList();
|
||||
var weights = included.Select(l => l.UnitPrice * l.Qty).ToList();
|
||||
if (weights.Sum() <= 0) weights = included.Select(l => l.Qty).ToList();
|
||||
var totalWeight = weights.Sum();
|
||||
|
||||
var allocated = 0m;
|
||||
for (var i = 0; i < included.Count; i++)
|
||||
{
|
||||
var lineTotal = i == included.Count - 1
|
||||
? bundlePrice - allocated
|
||||
: Math.Round(bundlePrice * weights[i] / totalWeight, 2, MidpointRounding.AwayFromZero);
|
||||
allocated += lineTotal;
|
||||
included[i].UnitPrice = Math.Round(lineTotal / included[i].Qty, 4, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
foreach (var line in lines.Where(l => !l.IncludeInBundle)) line.UnitPrice = 0m;
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<BundleSaleSummaryDto>> ListAsync(PageQuery query, BundleSaleStatus? status, int? customerId, int? warehouseId, CancellationToken ct = default)
|
||||
{
|
||||
IQueryable<BundleSale> q = _bundles.Query().AsNoTracking().Include(x => x.Lines);
|
||||
|
||||
Reference in New Issue
Block a user