price adjustment and stock adjustments

This commit is contained in:
2026-10-12 14:08:23 +05:30
committed by ImanThiyanga
parent a788349138
commit d0354ed169
166 changed files with 98818 additions and 252 deletions
@@ -0,0 +1,66 @@
using System.ComponentModel.DataAnnotations;
using ERPCore.Domain.Enums;
using ERPCore.Dtos.Common;
namespace ERPCore.Dtos.Accounts;
// --- Cash in hand (docs/BANK_DEPOSIT_MODULE_PLAN.md §2, §7.1) ---
/// <summary>
/// One business day's till cash. CashIn is slip/bundle cash plus cash invoice payments received
/// that day; CashOut is expense payments made in cash; Deposited is Posted bank deposits for that
/// business day. Closing = Opening + NetCash − Deposited, and becomes the next day's Opening.
/// Days before the tracking start date carry nothing in: their Opening is 0.
/// </summary>
public sealed record CashPositionDto(
DateOnly Date, decimal Opening, decimal CashIn, decimal CashOut, decimal NetCash, decimal Deposited, decimal Closing);
/// <summary>
/// A day's cash position for the deposit form. <paramref name="AvailableToDeposit"/> is the lowest
/// closing balance from that day up to today: a back-dated deposit lowers every later day too.
/// <paramref name="DefaultCashAccountCode"/> is the GL cash account the form preselects.
/// </summary>
public sealed record CashInHandDto(
CashPositionDto Position, decimal AvailableToDeposit, int? BranchId, DateOnly? TrackingStartDate, string TimeZone,
string? DefaultCashAccountCode);
public sealed record CashInHandDailyDto(
DateOnly From, DateOnly To, IReadOnlyList<CashPositionDto> Days, int? BranchId, DateOnly? TrackingStartDate, string TimeZone);
// --- Deposits ---
public sealed record BankDepositDto(
int BankDepositId, string DocNo, int BranchId, string? BranchName,
DateOnly BusinessDate, DateOnly DepositDate, decimal Amount,
long FromGlCashAccountId, string FromCashAccountCode, string FromCashAccountName,
long ToGlBankAccountId, string ToBankAccountCode, string ToBankAccountName, string? ToBankName, string? ToAccountNumber,
string? DepositSlipNo, string? DepositedBy, string? Remarks,
BankDepositStatus Status, string? GlJournalNo, DateTime? GlPostedAt,
string? GlReversalJournalNo, string? CancelReason, DateTime? CancelledAt,
int CreatedBy, DateTime CreatedAt);
/// <summary>A page of deposits plus the total amount of every row matching the filter (not just this page).</summary>
public sealed record BankDepositListDto(IReadOnlyList<BankDepositDto> Items, PaginationDto Pagination, decimal TotalAmount);
public sealed record BankDepositListFilter(
DateOnly? From, DateOnly? To, int? BranchId, long? BankAccountId, BankDepositStatus? Status);
public sealed class CreateBankDepositRequest
{
/// <summary>Required when branching is enabled; otherwise the default branch is used. A branch user always deposits for their own branch.</summary>
public int BranchId { get; set; }
[Required] public DateOnly BusinessDate { get; set; }
/// <summary>When the bank received it; also the GL entry date. Omitted ⇒ the business date.</summary>
public DateOnly? DepositDate { get; set; }
[Range(0.01, 999_999_999_999)] public decimal Amount { get; set; }
[Required] public long FromGlCashAccountId { get; set; }
[Required] public long ToGlBankAccountId { get; set; }
[StringLength(50)] public string? DepositSlipNo { get; set; }
[StringLength(150)] public string? DepositedBy { get; set; }
[StringLength(500)] public string? Remarks { get; set; }
}
public sealed class CancelBankDepositRequest
{
[Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty;
}
@@ -0,0 +1,43 @@
using System.ComponentModel.DataAnnotations;
using ERPCore.Domain.Enums;
namespace ERPCore.Dtos.Approvals;
/// <summary>One decision in a document's approval trail.</summary>
public sealed record ApprovalStepDto(int Level, ApprovalDecision Decision, int DecidedBy, string? DecidedByName, DateTime DecidedAt, string? Comment);
/// <summary>Settings → Approvals (docs/DUAL_APPROVAL_PLAN.md §7).</summary>
public sealed record ApprovalConfigDto(
int StockAdjustmentLevels, decimal StockAdjustmentLevel2ThresholdValue,
int PriceAdjustmentLevels, decimal PriceAdjustmentLevel2ThresholdPercent,
IReadOnlyList<string> Level2RoleCodes, DateTime? UpdatedAt);
public sealed class UpdateApprovalConfigRequest
{
[Range(0, 2)] public int StockAdjustmentLevels { get; set; }
[Range(0, double.MaxValue)] public decimal StockAdjustmentLevel2ThresholdValue { get; set; }
[Range(0, 2)] public int PriceAdjustmentLevels { get; set; }
[Range(0, 1000)] public decimal PriceAdjustmentLevel2ThresholdPercent { get; set; }
public List<string> Level2RoleCodes { get; set; } = new();
}
public sealed class ApproveRequest
{
[StringLength(250)] public string? Comment { get; set; }
}
public sealed class RejectRequest
{
[Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty;
}
/// <summary>
/// A request waiting in the Approvals inbox. <paramref name="Level"/> is the approval it waits for
/// (1 or 2) of <paramref name="RequiredApprovals"/>; <paramref name="CanApprove"/> says whether the
/// current user may decide it (not the requester, not the level-1 approver, has the right role).
/// </summary>
public sealed record PendingApprovalDto(
string DocType, int DocId, string DocNo, string Summary, int Level, int RequiredApprovals,
int RequestedBy, string? RequestedByName, DateTime RequestedAt, bool CanApprove, string? WhyNot);
public sealed record PendingApprovalsDto(IReadOnlyList<PendingApprovalDto> Items, int ActionableCount);
@@ -4,6 +4,9 @@ using ERPCore.Domain.Enums;
namespace ERPCore.Dtos.Customers;
/// <summary>Customer resource used by sales documents.</summary>
/// <param name="OutstandingAmount">Unpaid balance across the customer's Posted sales invoices. Filled on the list only.</param>
/// <param name="OutstandingInvoiceCount">Posted sales invoices with a balance still due. Filled on the list only.</param>
/// <param name="LastPaymentDate">When the customer last paid against an invoice; null if never. Filled on the list only.</param>
public sealed record CustomerDto(
int CustomerId,
string CustomerCode,
@@ -23,7 +26,10 @@ public sealed record CustomerDto(
EntityStatus Status,
int BranchId,
DateTime CreatedAt,
DateTime? UpdatedAt);
DateTime? UpdatedAt,
decimal OutstandingAmount = 0m,
int OutstandingInvoiceCount = 0,
DateTime? LastPaymentDate = null);
public sealed class CreateCustomerRequest
{
@@ -0,0 +1,120 @@
using System.ComponentModel.DataAnnotations;
using ERPCore.Domain.Enums;
namespace ERPCore.Dtos.Expenses;
// --- Setup: categories and titles (docs/EXPENSE_MODULE_PLAN.md §6 "Setup") ---
public sealed record ExpenseTitleDto(
int ExpenseTitleId, int ExpenseCategoryId, string Name, string GlExpenseAccountCode,
bool IsSystem, EntityStatus Status, int SortOrder);
public sealed record ExpenseCategoryDto(
int ExpenseCategoryId, string Code, string Name, bool IsSystem, EntityStatus Status, int SortOrder,
IReadOnlyList<ExpenseTitleDto> Titles);
public sealed class UpdateExpenseCategoryRequest
{
/// <summary>Ignored-if-unchanged for system rows; a different name on a system row is rejected.</summary>
[Required, StringLength(100, MinimumLength = 1)] public string Name { get; set; } = string.Empty;
public EntityStatus Status { get; set; } = EntityStatus.Active;
}
public sealed class CreateExpenseTitleRequest
{
[Required, StringLength(100, MinimumLength = 1)] public string Name { get; set; } = string.Empty;
[Required, StringLength(20, MinimumLength = 1)] public string GlExpenseAccountCode { get; set; } = string.Empty;
}
public sealed class UpdateExpenseTitleRequest
{
/// <summary>Ignored-if-unchanged for system rows; a different name on a system row is rejected.</summary>
[Required, StringLength(100, MinimumLength = 1)] public string Name { get; set; } = string.Empty;
[Required, StringLength(20, MinimumLength = 1)] public string GlExpenseAccountCode { get; set; } = string.Empty;
public EntityStatus Status { get; set; } = EntityStatus.Active;
}
// --- Expenses ---
public sealed record ExpenseSummaryDto(
int ExpenseId, string DocNo, int BranchId, DateOnly ExpenseDate, DateOnly? DueDate,
int ExpenseCategoryId, string CategoryName, int ExpenseTitleId, string TitleName,
string? Payee, string? BillReference, string? PeriodMonth,
decimal Amount, decimal PaidAmount, decimal BalanceAmount,
ExpenseStatus Status, ExpensePaymentStatus PaymentStatus, string? GlJournalNo, DateTime CreatedAt);
public sealed record ExpensePaymentDto(
int ExpensePaymentId, int ExpenseId, decimal Amount, DateTime PaymentDate, ExpensePaymentMethod PaymentMethod,
long GlBankAccountId, string BankAccountName, string? ChequeNo, string? Reference, string? GlJournalNo, DateTime CreatedAt);
public sealed record ExpenseDto(
int ExpenseId, string DocNo, int BranchId, DateOnly ExpenseDate, DateOnly? DueDate,
int ExpenseCategoryId, string CategoryName, int ExpenseTitleId, string TitleName,
string? Payee, int? VendorId, string? VendorName, string? BillReference, string? Description, string? PeriodMonth,
decimal Amount, decimal PaidAmount, decimal BalanceAmount,
ExpenseStatus Status, ExpensePaymentStatus PaymentStatus,
string? GlExpenseAccountCode, string? GlJournalNo, DateTime? GlPostedAt, string? GlReversalJournalNo, string? CancelReason,
int CreatedBy, DateTime CreatedAt, DateTime? UpdatedAt,
IReadOnlyList<ExpensePaymentDto> Payments);
public class ExpenseRequestBase
{
/// <summary>Required when branching is enabled; otherwise the default branch is used.</summary>
public int BranchId { get; set; }
[Required] public DateOnly ExpenseDate { get; set; }
public DateOnly? DueDate { get; set; }
[Required] public int ExpenseCategoryId { get; set; }
[Required] public int ExpenseTitleId { get; set; }
[StringLength(150)] public string? Payee { get; set; }
public int? VendorId { get; set; }
[StringLength(50)] public string? BillReference { get; set; }
[StringLength(500)] public string? Description { get; set; }
[RegularExpression(@"^\d{4}-(0[1-9]|1[0-2])$", ErrorMessage = "PeriodMonth must be yyyy-MM.")]
public string? PeriodMonth { get; set; }
[Range(0.01, 999_999_999_999)] public decimal Amount { get; set; }
}
public sealed class CreateExpenseRequest : ExpenseRequestBase;
public sealed class UpdateExpenseRequest : ExpenseRequestBase;
public sealed class CancelExpenseRequest
{
[Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty;
}
public sealed class ExpensePaymentLineRequest
{
[Required] public long GlBankAccountId { get; set; }
[Range(0.01, double.MaxValue)] public decimal Amount { get; set; }
}
/// <summary>
/// One payment submission: one or more lines (e.g. part cash, part bank), all posted in a
/// single GL journal entry. Total must not exceed the balance (for Post &amp; Pay, the amount); partial is fine.
/// </summary>
public sealed class CreateExpensePaymentRequest
{
[Required, MinLength(1)] public List<ExpensePaymentLineRequest> Lines { get; set; } = new();
public ExpensePaymentMethod PaymentMethod { get; set; } = ExpensePaymentMethod.Cash;
/// <summary>Required when <see cref="PaymentMethod"/> is Cheque.</summary>
[StringLength(30)] public string? ChequeNo { get; set; }
[StringLength(100)] public string? Reference { get; set; }
/// <summary>When it was paid; also the GL entry date. Omitted ⇒ today. Never in the future.</summary>
public DateOnly? PaymentDate { get; set; }
}
public sealed record ExpenseListFilter(
ExpenseStatus? Status, ExpensePaymentStatus? PaymentStatus, int? CategoryId, int? TitleId,
DateOnly? From, DateOnly? To, bool OverdueOnly);
public sealed record ExpenseTitleTotalDto(int ExpenseTitleId, string TitleName, decimal Amount, int Count);
public sealed record ExpenseCategoryTotalDto(
int ExpenseCategoryId, string CategoryName, decimal Amount, int Count, IReadOnlyList<ExpenseTitleTotalDto> Titles);
/// <summary>Posted (non-cancelled) expenses dated within [From, To], plus the all-time unpaid/overdue position.</summary>
public sealed record ExpenseSummaryReportDto(
DateOnly From, DateOnly To, decimal TotalAmount, decimal PaidAmount, decimal UnpaidAmount, int Count,
decimal OutstandingAmount, decimal OverdueAmount, int OverdueCount, int DraftCount,
IReadOnlyList<ExpenseCategoryTotalDto> Categories);
@@ -2,6 +2,14 @@ using ERPCore.Domain.Enums;
namespace ERPCore.Dtos.Items;
/// <summary>
/// One posted GRN receipt of an item — what it was bought at. <paramref name="NetUnitCost"/> is the
/// cost after trade discount (the FIFO stock cost, VAT excluded) — the figure to price against.
/// </summary>
public sealed record ItemPurchasePriceDto(
int GrnId, string GrnDocNo, DateTime ReceivedAt, int VendorId, string? VendorName, string? WarehouseCode,
decimal Qty, decimal UnitCost, decimal DiscountPct, decimal NetUnitCost, decimal VatPct);
/// <summary>
/// Item row enriched with cross-module aggregates (current FIFO stock value, low-stock
/// flag, last purchase, trailing-30-day sales) for the Items list page. Deliberately a
@@ -0,0 +1,47 @@
using System.ComponentModel.DataAnnotations;
using ERPCore.Domain.Enums;
using ERPCore.Dtos.Approvals;
namespace ERPCore.Dtos.Products;
/// <summary>One item's requested change. Current* are the item's prices now — they differ from Old* when it was repriced since.</summary>
public sealed record PriceAdjustmentLineDto(
int PriceAdjustmentLineId, int ItemId, string Sku, string ItemName,
decimal? OldMinPrice, decimal? OldMaxPrice, decimal? OldFlatPrice,
decimal? NewMinPrice, decimal? NewMaxPrice, decimal? NewFlatPrice,
decimal? CurrentMinPrice, decimal? CurrentMaxPrice, decimal? CurrentFlatPrice);
public sealed record PriceAdjustmentSummaryDto(
int PriceAdjustmentId, string DocNo, string Reason, PriceAdjustmentStatus Status, int RequiredApprovals, int? PendingLevel,
int LineCount, int CreatedBy, string? CreatedByName, DateTime CreatedAt, DateTime? AppliedAt);
/// <param name="PendingLevel">The approval it waits for (1 or 2); null once decided.</param>
public sealed record PriceAdjustmentDto(
int PriceAdjustmentId, string DocNo, string Reason, PriceAdjustmentStatus Status, int RequiredApprovals, int? PendingLevel,
int CreatedBy, string? CreatedByName, DateTime CreatedAt,
int? ApprovedLevel1By, DateTime? ApprovedLevel1At, int? ApprovedLevel2By, DateTime? ApprovedLevel2At,
int? RejectedBy, DateTime? RejectedAt, string? RejectReason, DateTime? CancelledAt, DateTime? AppliedAt,
IReadOnlyList<PriceAdjustmentLineDto> Lines, IReadOnlyList<ApprovalStepDto> ApprovalHistory, bool CanApprove, string? WhyNot,
bool CanWithdraw);
public sealed class PriceAdjustmentLineInput
{
[Required] public int ItemId { get; set; }
/// <summary>The prices the item should have afterwards — null clears that price.</summary>
[Range(0, double.MaxValue)] public decimal? NewMinPrice { get; set; }
[Range(0, double.MaxValue)] public decimal? NewMaxPrice { get; set; }
[Range(0, double.MaxValue)] public decimal? NewFlatPrice { get; set; }
}
public sealed class CreatePriceAdjustmentRequest
{
[Required, StringLength(250, MinimumLength = 1)] public string Reason { get; set; } = string.Empty;
[Required, MinLength(1)] public List<PriceAdjustmentLineInput> Lines { get; set; } = new();
}
public sealed record ItemPriceHistoryDto(
int ItemPriceHistoryId, int ItemId, int? PriceAdjustmentId, string? DocNo,
decimal? OldMinPrice, decimal? OldMaxPrice, decimal? OldFlatPrice,
decimal? NewMinPrice, decimal? NewMaxPrice, decimal? NewFlatPrice,
int RequestedBy, string? RequestedByName, int? ApprovedLevel1By, string? ApprovedLevel1ByName,
int? ApprovedLevel2By, string? ApprovedLevel2ByName, DateTime AppliedAt);
@@ -0,0 +1,51 @@
namespace ERPCore.Dtos.Reports;
/// <summary>
/// One business day's figures (or, with <see cref="Date"/> null, a range's totals) — docs/DAY_END_SALES_REPORT_PLAN.md §4.
/// Sales are Posted slips, bundle sales and invoices dated that day; Paid is what has been paid on
/// those sales so far (split by how it was paid) and Due the rest.
/// </summary>
/// <param name="Income">TotalSales − ExpenseTotal (sales returns aren't priced, so they can't be netted off).</param>
/// <param name="CostOfGoodsSold">FIFO cost of the stock those sales issued (the stock ledger's Out value).</param>
/// <param name="GrossProfit">TotalSales − CostOfGoodsSold.</param>
/// <param name="NetProfit">GrossProfit − ExpenseTotal; negative is a loss.</param>
/// <param name="EarlierInvoiceCollections">Invoice payments received this day against invoices from
/// earlier days — not part of PaidAmount.</param>
/// <param name="NetCash">Cash that came in this day (slip/bundle cash + cash invoice payments, any
/// invoice) minus expenses paid in cash this day — a till check.</param>
/// <param name="OpeningCashInHand">Cash left in the till from earlier days (docs/BANK_DEPOSIT_MODULE_PLAN.md §9).
/// For a range's totals, the first day's opening.</param>
/// <param name="CashDeposited">Posted bank deposits made against this business day.</param>
/// <param name="ClosingCashInHand">Opening + NetCash − CashDeposited: cash still in hand. For a range's totals, the last day's closing.</param>
public sealed record DayEndReportDayDto(
DateOnly? Date,
decimal TotalSales, decimal PaidAmount, decimal DueAmount,
decimal PaidByCash, decimal PaidByCard, decimal PaidByOther,
int SalesCount, int CashSalesCount, int CardSalesCount, int MixedSalesCount, int OtherSalesCount, int UnpaidSalesCount,
decimal ExpenseTotal, decimal Income,
decimal CostOfGoodsSold, decimal GrossProfit, decimal NetProfit,
decimal EarlierInvoiceCollections,
decimal NetCash,
decimal OpeningCashInHand, decimal CashDeposited, decimal ClosingCashInHand);
/// <summary>One sale of the day. <paramref name="PaidBy"/> is Cash, Card, Bank transfer, Cheque, Mixed or Unpaid.</summary>
public sealed record DayEndReportSaleDto(
string DocType, int DocId, string DocNo, DateTime At, string CustomerName,
decimal Total, decimal Paid, decimal Due, string PaidBy, decimal Cost, decimal GrossProfit);
public sealed record DayEndReportExpenseDto(
int ExpenseId, string DocNo, string CategoryName, string TitleName, string? Payee, decimal Amount, decimal PaidAmount);
/// <summary>A Posted bank deposit of this business day's cash.</summary>
public sealed record DayEndReportDepositDto(
int BankDepositId, string DocNo, string BankAccountName, string? BankName, string? AccountNumber,
string? DepositSlipNo, DateOnly DepositDate, decimal Amount);
/// <summary>A single day: its figures plus the sales, expenses and bank deposits behind them.</summary>
public sealed record DayEndReportDto(
DayEndReportDayDto Summary, IReadOnlyList<DayEndReportSaleDto> Sales, IReadOnlyList<DayEndReportExpenseDto> Expenses,
IReadOnlyList<DayEndReportDepositDto> Deposits, int? BranchId, string TimeZone);
/// <summary>Day-wise figures for a range (every day present, zeros on quiet days) plus the range's totals.</summary>
public sealed record DayEndDailyReportDto(
DateOnly From, DateOnly To, IReadOnlyList<DayEndReportDayDto> Days, DayEndReportDayDto Totals, int? BranchId, string TimeZone);
+5 -1
View File
@@ -12,7 +12,7 @@ public sealed record BundleSaleDto(
int WarehouseId, int CashierUserId, int BundleSaleTemplateId, string BundleName, string BundleCode,
BundleSaleStatus Status, decimal ComponentSubtotal, decimal BundlePrice, decimal MarginAmount,
decimal DiscountTotal, decimal TaxTotal, decimal GrandTotal, DateTime CreatedAt, DateTime? UpdatedAt,
IReadOnlyList<BundleSaleLineDto> Lines);
IReadOnlyList<BundleSaleLineDto> Lines, decimal CashAmount, decimal CardAmount);
public sealed record BundleSaleSummaryDto(
int BundleSaleId, string BundleNo, DateTime BundleDate, int CustomerId, string CustomerSnapshotName,
@@ -66,6 +66,8 @@ public sealed class CreateBundleSaleRequest
[Range(0, double.MaxValue)] public decimal BundlePrice { get; set; }
public bool AllowPriceOverride { get; set; }
[Required, MinLength(1)] public List<CreateBundleSaleTemplateLineRequest> Lines { get; set; } = new();
/// <summary>The part of the total tendered by card; the rest is cash. Omit for all cash.</summary>
[Range(0, double.MaxValue)] public decimal? CardAmount { get; set; }
}
public sealed class UpdateBundleSaleRequest
@@ -78,4 +80,6 @@ public sealed class UpdateBundleSaleRequest
[Range(0, double.MaxValue)] public decimal BundlePrice { get; set; }
public bool AllowPriceOverride { get; set; }
[Required, MinLength(1)] public List<CreateBundleSaleTemplateLineRequest> Lines { get; set; } = new();
/// <summary>See <see cref="CreateBundleSaleRequest.CardAmount"/>.</summary>
[Range(0, double.MaxValue)] public decimal? CardAmount { get; set; }
}
@@ -31,12 +31,15 @@ public sealed record SalesInvoiceSummaryDto(
public sealed record SalesInvoicePaymentDto(
int SalesInvoicePaymentId, int SalesInvoiceId, decimal Amount, DateTime PaymentDate,
long GlBankAccountId, string BankAccountName, string? Reference, string? GlJournalNo, DateTime CreatedAt);
long GlBankAccountId, string BankAccountName, string? Reference, string? GlJournalNo, DateTime CreatedAt,
SalesPaymentMethod PaymentMethod);
public sealed class CreateSalesInvoicePaymentRequest
{
[Range(0.01, double.MaxValue)] public decimal Amount { get; set; }
[Required] public long GlBankAccountId { get; set; }
/// <summary>How the customer paid. Omitted ⇒ derived from the account: a Cash account ⇒ Cash, a Bank account ⇒ Card.</summary>
public SalesPaymentMethod? PaymentMethod { get; set; }
[StringLength(100)] public string? Reference { get; set; }
/// <summary>When the customer paid; also the GL entry date. Omitted ⇒ today. Never in the future.</summary>
public DateOnly? PaymentDate { get; set; }
@@ -47,6 +50,8 @@ public sealed class BulkSalesInvoicePaymentLine
{
[Range(0.01, double.MaxValue)] public decimal Amount { get; set; }
[Required] public long GlBankAccountId { get; set; }
/// <summary>See <see cref="CreateSalesInvoicePaymentRequest.PaymentMethod"/>.</summary>
public SalesPaymentMethod? PaymentMethod { get; set; }
}
/// <summary>
+8 -1
View File
@@ -12,7 +12,7 @@ public sealed record SalesSlipLineDto(
public sealed record SalesSlipTotalsDto(
decimal Subtotal, decimal DiscountTotal, decimal FreeQtyTotal, decimal TaxTotal, decimal GrandTotal,
decimal PaidAmount, decimal BalanceAmount);
decimal PaidAmount, decimal BalanceAmount, decimal CashAmount, decimal CardAmount);
public sealed record SalesSlipDto(
int SalesSlipId, string SlipNo, DateTime SlipDate, int CustomerId,
@@ -87,6 +87,10 @@ public sealed class CreateSalesSlipRequest
/// Receivable when it posts (see SalesDayEndService.CloseAsync).
/// </summary>
[Range(0, double.MaxValue)] public decimal? PaidAmount { get; set; }
/// <summary>The part of the paid amount tendered by card; the rest of it is cash. Omit for all
/// cash. Must be between 0 and the paid amount.</summary>
[Range(0, double.MaxValue)] public decimal? CardAmount { get; set; }
}
public sealed class UpdateSalesSlipRequest
@@ -98,4 +102,7 @@ public sealed class UpdateSalesSlipRequest
/// <summary>See <see cref="CreateSalesSlipRequest.PaidAmount"/>.</summary>
[Range(0, double.MaxValue)] public decimal? PaidAmount { get; set; }
/// <summary>See <see cref="CreateSalesSlipRequest.CardAmount"/>.</summary>
[Range(0, double.MaxValue)] public decimal? CardAmount { get; set; }
}
+15 -3
View File
@@ -5,17 +5,29 @@ namespace ERPCore.Dtos.Stock;
// Responses (docs/11 §5.5) ------------------------------------------------------
public sealed record AdjustmentLineDto(int AdjLineId, int ItemId, int? BinId, int? BatchId, decimal QtyDelta, int? SerialId);
/// <param name="RequestedSerialIds">Serials named to write off (shown while the request is pending).</param>
/// <param name="RequestedSerialNos">Serial numbers named as found (shown while the request is pending).</param>
public sealed record AdjustmentLineDto(
int AdjLineId, int ItemId, int? BinId, int? BatchId, decimal QtyDelta, int? SerialId,
IReadOnlyList<int>? RequestedSerialIds = null, IReadOnlyList<string>? RequestedSerialNos = null);
/// <param name="PendingLevel">The approval it waits for (1 or 2); null once decided or posted.</param>
/// <param name="CanApprove">Whether the current user may give that approval; <paramref name="WhyNot"/> says why not.</param>
public sealed record AdjustmentDto(
int AdjustmentId, string DocNo, int WarehouseId, int BranchId, int ReasonCodeId, AdjustmentStatus Status,
int CreatedBy, DateTime CreatedAt, string? GlJournalNo, DateTime? GlPostedAt,
IReadOnlyList<AdjustmentLineDto> Lines, IReadOnlyList<int> LedgerRefs);
IReadOnlyList<AdjustmentLineDto> Lines, IReadOnlyList<int> LedgerRefs,
int RequiredApprovals = 0, int? PendingLevel = null,
int? ApprovedLevel1By = null, DateTime? ApprovedLevel1At = null, int? ApprovedLevel2By = null, DateTime? ApprovedLevel2At = null,
int? RejectedBy = null, DateTime? RejectedAt = null, string? RejectReason = null, DateTime? PostedAt = null,
IReadOnlyList<Approvals.ApprovalStepDto>? ApprovalHistory = null, bool CanApprove = false, string? WhyNot = null,
bool CanWithdraw = false, string? CreatedByName = null);
/// <summary>Row shape for <c>GET /stock-adjustments</c> — no lines/ledgerRefs (those need a per-row query).</summary>
public sealed record AdjustmentSummaryDto(
int AdjustmentId, string DocNo, int WarehouseId, int BranchId, int ReasonCodeId, AdjustmentStatus Status,
int CreatedBy, DateTime CreatedAt, int LineCount, string? GlJournalNo);
int CreatedBy, DateTime CreatedAt, int LineCount, string? GlJournalNo,
int RequiredApprovals = 0, int? PendingLevel = null);
// Requests ----------------------------------------------------------------------
+4 -1
View File
@@ -18,7 +18,10 @@ public sealed record CountSummaryDto(
int CountId, string DocNo, int WarehouseId, int BranchId, CountType CountType, CountStatus Status,
int CreatedBy, DateTime CreatedAt, int LineCount);
public sealed record CountPostResultDto(int CountId, CountStatus Status, int? AdjustmentId, string? GlJournalNo, IReadOnlyList<int> LedgerRefs);
/// <param name="AdjustmentStatus">The variance adjustment's status — <c>PendingApproval</c> when it must be approved before stock moves.</param>
public sealed record CountPostResultDto(
int CountId, CountStatus Status, int? AdjustmentId, string? GlJournalNo, IReadOnlyList<int> LedgerRefs,
AdjustmentStatus? AdjustmentStatus = null, string? AdjustmentDocNo = null);
// Requests ----------------------------------------------------------------------