Add discoont rule
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
using ERPCore.Domain.Enums;
|
||||||
|
|
||||||
namespace ERPCore.Domain.Entities;
|
namespace ERPCore.Domain.Entities;
|
||||||
|
|
||||||
public class SalesInvoiceLine
|
public class SalesInvoiceLine
|
||||||
@@ -22,6 +24,7 @@ public class SalesInvoiceLine
|
|||||||
public decimal UnitPrice { get; set; }
|
public decimal UnitPrice { get; set; }
|
||||||
public decimal BaseCost { get; set; }
|
public decimal BaseCost { get; set; }
|
||||||
public string PriceSource { get; set; } = string.Empty;
|
public string PriceSource { get; set; } = string.Empty;
|
||||||
|
public SalesDiscountMode DiscountMode { get; set; } = SalesDiscountMode.Percentage;
|
||||||
public decimal DiscountPct { get; set; }
|
public decimal DiscountPct { get; set; }
|
||||||
public decimal DiscountAmount { get; set; }
|
public decimal DiscountAmount { get; set; }
|
||||||
public decimal NetUnitPrice { get; set; }
|
public decimal NetUnitPrice { get; set; }
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using ERPCore.Domain.Enums;
|
||||||
|
|
||||||
namespace ERPCore.Domain.Entities;
|
namespace ERPCore.Domain.Entities;
|
||||||
|
|
||||||
public class SalesSlipLine
|
public class SalesSlipLine
|
||||||
@@ -22,6 +24,7 @@ public class SalesSlipLine
|
|||||||
public decimal UnitPrice { get; set; }
|
public decimal UnitPrice { get; set; }
|
||||||
public decimal BaseCost { get; set; }
|
public decimal BaseCost { get; set; }
|
||||||
public string PriceSource { get; set; } = string.Empty;
|
public string PriceSource { get; set; } = string.Empty;
|
||||||
|
public SalesDiscountMode DiscountMode { get; set; } = SalesDiscountMode.Percentage;
|
||||||
public decimal DiscountPct { get; set; }
|
public decimal DiscountPct { get; set; }
|
||||||
public decimal DiscountAmount { get; set; }
|
public decimal DiscountAmount { get; set; }
|
||||||
public decimal NetUnitPrice { get; set; }
|
public decimal NetUnitPrice { get; set; }
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace ERPCore.Domain.Enums;
|
||||||
|
|
||||||
|
public enum SalesDiscountMode
|
||||||
|
{
|
||||||
|
Percentage = 1,
|
||||||
|
FixedAmount = 2
|
||||||
|
}
|
||||||
@@ -6,8 +6,8 @@ namespace ERPCore.Dtos.Sales;
|
|||||||
public sealed record SalesInvoiceLineDto(
|
public sealed record SalesInvoiceLineDto(
|
||||||
int SalesInvoiceLineId, int ItemId, string Description, decimal Qty, decimal FreeQty, int UomId,
|
int SalesInvoiceLineId, int ItemId, string Description, decimal Qty, decimal FreeQty, int UomId,
|
||||||
int WarehouseId, decimal UnitPrice, decimal BaseCost, string PriceSource, decimal DiscountPct,
|
int WarehouseId, decimal UnitPrice, decimal BaseCost, string PriceSource, decimal DiscountPct,
|
||||||
decimal DiscountAmount, decimal NetUnitPrice, decimal LineTotal, decimal TaxPct, decimal TaxAmount,
|
decimal DiscountAmount, SalesDiscountMode DiscountMode, decimal NetUnitPrice, decimal LineTotal,
|
||||||
bool IsFreeIssue, int? ParentLineId);
|
decimal TaxPct, decimal TaxAmount, bool IsFreeIssue, int? ParentLineId);
|
||||||
|
|
||||||
public sealed record SalesInvoiceTotalsDto(
|
public sealed record SalesInvoiceTotalsDto(
|
||||||
decimal Subtotal, decimal DiscountTotal, decimal TaxTotal, decimal GrandTotal,
|
decimal Subtotal, decimal DiscountTotal, decimal TaxTotal, decimal GrandTotal,
|
||||||
@@ -33,8 +33,10 @@ public sealed class CreateSalesInvoiceLineRequest
|
|||||||
[Range(0, double.MaxValue)] public decimal FreeQty { get; set; }
|
[Range(0, double.MaxValue)] public decimal FreeQty { get; set; }
|
||||||
[Range(0, double.MaxValue)] public decimal? UnitPrice { get; set; }
|
[Range(0, double.MaxValue)] public decimal? UnitPrice { get; set; }
|
||||||
public bool AllowManualPriceOverride { get; set; }
|
public bool AllowManualPriceOverride { get; set; }
|
||||||
|
[Required, EnumDataType(typeof(SalesDiscountMode))] public SalesDiscountMode DiscountMode { get; set; } = SalesDiscountMode.Percentage;
|
||||||
[Range(0, 100)] public decimal DiscountPct { get; set; }
|
[Range(0, 100)] public decimal DiscountPct { get; set; }
|
||||||
[Range(0, double.MaxValue)] public decimal DiscountAmount { get; set; }
|
[Range(0, double.MaxValue)] public decimal DiscountAmount { get; set; }
|
||||||
|
[Range(0, double.MaxValue)] public decimal DiscountValue { get; set; }
|
||||||
[Range(0, 100)] public decimal TaxPct { get; set; }
|
[Range(0, 100)] public decimal TaxPct { get; set; }
|
||||||
public bool IsFreeIssue { get; set; }
|
public bool IsFreeIssue { get; set; }
|
||||||
public int? ParentLineId { get; set; }
|
public int? ParentLineId { get; set; }
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ namespace ERPCore.Dtos.Sales;
|
|||||||
public sealed record SalesSlipLineDto(
|
public sealed record SalesSlipLineDto(
|
||||||
int SalesSlipLineId, int ItemId, string Description, decimal Qty, decimal FreeQty, int UomId,
|
int SalesSlipLineId, int ItemId, string Description, decimal Qty, decimal FreeQty, int UomId,
|
||||||
int WarehouseId, decimal UnitPrice, decimal BaseCost, string PriceSource, decimal DiscountPct,
|
int WarehouseId, decimal UnitPrice, decimal BaseCost, string PriceSource, decimal DiscountPct,
|
||||||
decimal DiscountAmount, decimal NetUnitPrice, decimal LineTotal, decimal TaxPct, decimal TaxAmount,
|
decimal DiscountAmount, SalesDiscountMode DiscountMode, decimal NetUnitPrice, decimal LineTotal,
|
||||||
bool IsFreeIssue, int? ParentLineId);
|
decimal TaxPct, decimal TaxAmount, bool IsFreeIssue, int? ParentLineId);
|
||||||
|
|
||||||
public sealed record SalesSlipTotalsDto(
|
public sealed record SalesSlipTotalsDto(
|
||||||
decimal Subtotal, decimal DiscountTotal, decimal TaxTotal, decimal GrandTotal,
|
decimal Subtotal, decimal DiscountTotal, decimal TaxTotal, decimal GrandTotal,
|
||||||
@@ -32,8 +32,10 @@ public sealed class CreateSalesSlipLineRequest
|
|||||||
[Range(0, double.MaxValue)] public decimal FreeQty { get; set; }
|
[Range(0, double.MaxValue)] public decimal FreeQty { get; set; }
|
||||||
[Range(0, double.MaxValue)] public decimal? UnitPrice { get; set; }
|
[Range(0, double.MaxValue)] public decimal? UnitPrice { get; set; }
|
||||||
public bool AllowManualPriceOverride { get; set; }
|
public bool AllowManualPriceOverride { get; set; }
|
||||||
|
[Required, EnumDataType(typeof(SalesDiscountMode))] public SalesDiscountMode DiscountMode { get; set; } = SalesDiscountMode.Percentage;
|
||||||
[Range(0, 100)] public decimal DiscountPct { get; set; }
|
[Range(0, 100)] public decimal DiscountPct { get; set; }
|
||||||
[Range(0, double.MaxValue)] public decimal DiscountAmount { get; set; }
|
[Range(0, double.MaxValue)] public decimal DiscountAmount { get; set; }
|
||||||
|
[Range(0, double.MaxValue)] public decimal DiscountValue { get; set; }
|
||||||
[Range(0, 100)] public decimal TaxPct { get; set; }
|
[Range(0, 100)] public decimal TaxPct { get; set; }
|
||||||
public bool IsFreeIssue { get; set; }
|
public bool IsFreeIssue { get; set; }
|
||||||
public int? ParentLineId { get; set; }
|
public int? ParentLineId { get; set; }
|
||||||
|
|||||||
@@ -182,8 +182,7 @@ public sealed class SalesInvoiceService : ISalesInvoiceService
|
|||||||
var priceSource = resolved.PriceSource;
|
var priceSource = resolved.PriceSource;
|
||||||
|
|
||||||
var gross = r.Qty * unitPrice;
|
var gross = r.Qty * unitPrice;
|
||||||
var discountAfterPct = gross * (r.DiscountPct / 100m);
|
var discountTotal = CalculateDiscount(gross, r.DiscountMode, r.DiscountPct, r.DiscountValue, r.DiscountAmount);
|
||||||
var discountTotal = discountAfterPct + r.DiscountAmount;
|
|
||||||
var netUnit = r.Qty > 0 ? (gross - discountTotal) / r.Qty : 0m;
|
var netUnit = r.Qty > 0 ? (gross - discountTotal) / r.Qty : 0m;
|
||||||
var lineTotal = gross - discountTotal;
|
var lineTotal = gross - discountTotal;
|
||||||
var taxAmount = lineTotal * (r.TaxPct / 100m);
|
var taxAmount = lineTotal * (r.TaxPct / 100m);
|
||||||
@@ -201,6 +200,7 @@ public sealed class SalesInvoiceService : ISalesInvoiceService
|
|||||||
PriceSource = priceSource,
|
PriceSource = priceSource,
|
||||||
DiscountPct = r.DiscountPct,
|
DiscountPct = r.DiscountPct,
|
||||||
DiscountAmount = discountTotal,
|
DiscountAmount = discountTotal,
|
||||||
|
DiscountMode = r.DiscountMode,
|
||||||
NetUnitPrice = netUnit,
|
NetUnitPrice = netUnit,
|
||||||
LineTotal = lineTotal,
|
LineTotal = lineTotal,
|
||||||
TaxPct = r.TaxPct,
|
TaxPct = r.TaxPct,
|
||||||
@@ -224,6 +224,18 @@ public sealed class SalesInvoiceService : ISalesInvoiceService
|
|||||||
invoice.BalanceAmount = invoice.NetPayable;
|
invoice.BalanceAmount = invoice.NetPayable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static decimal CalculateDiscount(decimal gross, SalesDiscountMode mode, decimal discountPct, decimal discountValue, decimal legacyDiscountAmount)
|
||||||
|
{
|
||||||
|
var computed = mode == SalesDiscountMode.FixedAmount
|
||||||
|
? discountValue
|
||||||
|
: gross * (discountPct / 100m);
|
||||||
|
|
||||||
|
if (computed <= 0m && legacyDiscountAmount > 0m)
|
||||||
|
computed = legacyDiscountAmount;
|
||||||
|
|
||||||
|
return Math.Min(gross, Math.Max(0m, computed));
|
||||||
|
}
|
||||||
|
|
||||||
private static SalesInvoiceSummaryDto MapSummary(SalesInvoice x) => new(
|
private static SalesInvoiceSummaryDto MapSummary(SalesInvoice x) => new(
|
||||||
x.SalesInvoiceId, x.InvoiceNo, x.InvoiceDate, x.CustomerId, x.CustomerSnapshotName,
|
x.SalesInvoiceId, x.InvoiceNo, x.InvoiceDate, x.CustomerId, x.CustomerSnapshotName,
|
||||||
x.WarehouseId, x.InvoiceType, x.Status, new SalesInvoiceTotalsDto(
|
x.WarehouseId, x.InvoiceType, x.Status, new SalesInvoiceTotalsDto(
|
||||||
@@ -233,5 +245,5 @@ public sealed class SalesInvoiceService : ISalesInvoiceService
|
|||||||
x.SalesInvoiceId, x.InvoiceNo, x.InvoiceDate, x.CustomerId, x.CustomerSnapshotName, x.CustomerSnapshotTaxNo,
|
x.SalesInvoiceId, x.InvoiceNo, x.InvoiceDate, x.CustomerId, x.CustomerSnapshotName, x.CustomerSnapshotTaxNo,
|
||||||
x.WarehouseId, x.InvoiceType, x.Status, x.CreatedBy, x.CreatedAt, x.UpdatedAt,
|
x.WarehouseId, x.InvoiceType, x.Status, x.CreatedBy, x.CreatedAt, x.UpdatedAt,
|
||||||
new SalesInvoiceTotalsDto(x.Subtotal, x.DiscountTotal, x.TaxTotal, x.GrandTotal, x.RoundOff, x.NetPayable, x.PaidAmount, x.BalanceAmount),
|
new SalesInvoiceTotalsDto(x.Subtotal, x.DiscountTotal, x.TaxTotal, x.GrandTotal, x.RoundOff, x.NetPayable, x.PaidAmount, x.BalanceAmount),
|
||||||
x.Lines.Select(l => new SalesInvoiceLineDto(l.SalesInvoiceLineId, l.ItemId, l.Description, l.Qty, l.FreeQty, l.UomId, l.WarehouseId, l.UnitPrice, l.BaseCost, l.PriceSource, l.DiscountPct, l.DiscountAmount, l.NetUnitPrice, l.LineTotal, l.TaxPct, l.TaxAmount, l.IsFreeIssue, l.ParentLineId)).ToList());
|
x.Lines.Select(l => new SalesInvoiceLineDto(l.SalesInvoiceLineId, l.ItemId, l.Description, l.Qty, l.FreeQty, l.UomId, l.WarehouseId, l.UnitPrice, l.BaseCost, l.PriceSource, l.DiscountPct, l.DiscountAmount, l.DiscountMode, l.NetUnitPrice, l.LineTotal, l.TaxPct, l.TaxAmount, l.IsFreeIssue, l.ParentLineId)).ToList());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,8 +183,7 @@ public sealed class SalesSlipService : ISalesSlipService
|
|||||||
var priceSource = resolved.PriceSource;
|
var priceSource = resolved.PriceSource;
|
||||||
|
|
||||||
var gross = r.Qty * unitPrice;
|
var gross = r.Qty * unitPrice;
|
||||||
var discountAfterPct = gross * (r.DiscountPct / 100m);
|
var discountTotal = CalculateDiscount(gross, r.DiscountMode, r.DiscountPct, r.DiscountValue, r.DiscountAmount);
|
||||||
var discountTotal = discountAfterPct + r.DiscountAmount;
|
|
||||||
var netUnit = r.Qty > 0 ? (gross - discountTotal) / r.Qty : 0m;
|
var netUnit = r.Qty > 0 ? (gross - discountTotal) / r.Qty : 0m;
|
||||||
var lineTotal = gross - discountTotal;
|
var lineTotal = gross - discountTotal;
|
||||||
var taxAmount = lineTotal * (r.TaxPct / 100m);
|
var taxAmount = lineTotal * (r.TaxPct / 100m);
|
||||||
@@ -200,6 +199,7 @@ public sealed class SalesSlipService : ISalesSlipService
|
|||||||
UnitPrice = unitPrice,
|
UnitPrice = unitPrice,
|
||||||
BaseCost = unitPrice,
|
BaseCost = unitPrice,
|
||||||
PriceSource = priceSource,
|
PriceSource = priceSource,
|
||||||
|
DiscountMode = r.DiscountMode,
|
||||||
DiscountPct = r.DiscountPct,
|
DiscountPct = r.DiscountPct,
|
||||||
DiscountAmount = discountTotal,
|
DiscountAmount = discountTotal,
|
||||||
NetUnitPrice = netUnit,
|
NetUnitPrice = netUnit,
|
||||||
@@ -223,6 +223,18 @@ public sealed class SalesSlipService : ISalesSlipService
|
|||||||
slip.BalanceAmount = slip.GrandTotal - slip.PaidAmount;
|
slip.BalanceAmount = slip.GrandTotal - slip.PaidAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static decimal CalculateDiscount(decimal gross, SalesDiscountMode mode, decimal discountPct, decimal discountValue, decimal legacyDiscountAmount)
|
||||||
|
{
|
||||||
|
var computed = mode == SalesDiscountMode.FixedAmount
|
||||||
|
? discountValue
|
||||||
|
: gross * (discountPct / 100m);
|
||||||
|
|
||||||
|
if (computed <= 0m && legacyDiscountAmount > 0m)
|
||||||
|
computed = legacyDiscountAmount;
|
||||||
|
|
||||||
|
return Math.Min(gross, Math.Max(0m, computed));
|
||||||
|
}
|
||||||
|
|
||||||
private static SalesSlipSummaryDto MapSummary(SalesSlip x) => new(
|
private static SalesSlipSummaryDto MapSummary(SalesSlip x) => new(
|
||||||
x.SalesSlipId, x.SlipNo, x.SlipDate, x.CustomerId, x.CustomerSnapshotName, x.WarehouseId, x.Status,
|
x.SalesSlipId, x.SlipNo, x.SlipDate, x.CustomerId, x.CustomerSnapshotName, x.WarehouseId, x.Status,
|
||||||
new SalesSlipTotalsDto(x.Subtotal, x.DiscountTotal, x.TaxTotal, x.GrandTotal, x.PaidAmount, x.BalanceAmount), x.CreatedAt);
|
new SalesSlipTotalsDto(x.Subtotal, x.DiscountTotal, x.TaxTotal, x.GrandTotal, x.PaidAmount, x.BalanceAmount), x.CreatedAt);
|
||||||
@@ -230,5 +242,5 @@ public sealed class SalesSlipService : ISalesSlipService
|
|||||||
private static SalesSlipDto Map(SalesSlip x) => new(
|
private static SalesSlipDto Map(SalesSlip x) => new(
|
||||||
x.SalesSlipId, x.SlipNo, x.SlipDate, x.CustomerId, x.CustomerSnapshotName, x.WarehouseId, x.CashierUserId, x.Status, x.CreatedAt, x.UpdatedAt,
|
x.SalesSlipId, x.SlipNo, x.SlipDate, x.CustomerId, x.CustomerSnapshotName, x.WarehouseId, x.CashierUserId, x.Status, x.CreatedAt, x.UpdatedAt,
|
||||||
new SalesSlipTotalsDto(x.Subtotal, x.DiscountTotal, x.TaxTotal, x.GrandTotal, x.PaidAmount, x.BalanceAmount),
|
new SalesSlipTotalsDto(x.Subtotal, x.DiscountTotal, x.TaxTotal, x.GrandTotal, x.PaidAmount, x.BalanceAmount),
|
||||||
x.Lines.Select(l => new SalesSlipLineDto(l.SalesSlipLineId, l.ItemId, l.Description, l.Qty, l.FreeQty, l.UomId, l.WarehouseId, l.UnitPrice, l.BaseCost, l.PriceSource, l.DiscountPct, l.DiscountAmount, l.NetUnitPrice, l.LineTotal, l.TaxPct, l.TaxAmount, l.IsFreeIssue, l.ParentLineId)).ToList());
|
x.Lines.Select(l => new SalesSlipLineDto(l.SalesSlipLineId, l.ItemId, l.Description, l.Qty, l.FreeQty, l.UomId, l.WarehouseId, l.UnitPrice, l.BaseCost, l.PriceSource, l.DiscountPct, l.DiscountAmount, l.DiscountMode, l.NetUnitPrice, l.LineTotal, l.TaxPct, l.TaxAmount, l.IsFreeIssue, l.ParentLineId)).ToList());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user