Add discoont rule

This commit is contained in:
2026-07-27 15:21:35 +05:30
committed by ImanThiyanga
parent 4f722432cd
commit 2dab7051b3
7 changed files with 51 additions and 10 deletions
@@ -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; }
+4 -2
View File
@@ -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());
} }
+15 -3
View File
@@ -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());
} }