Describe the issue
Affects: BC 27, BC 28 — not reproduced on BC 26 (the relevant code path was introduced after BC 26).
When posting a Sales Return Order that partially reverses a shipment on a Sales Order with 100% prepayment already invoiced, posting fails with:
Prepmt Amt to Deduct cannot be more than 193.7 in Sales Line Document Type='Order', Document No.='...', Line No.='...'
The error occurs in Table 37 "Sales Line", field Prepmt Amt to Deduct, OnValidate trigger (check 2: "Prepmt Amt to Deduct" > "Qty. to Invoice" * "Unit Price").
Root cause: Codeunit 1303 "Correct Posted Sales Invoice", procedure UpdateSalesOrderLinePrepmtAmount, uses SalesInvoiceLine.Quantity (the full original invoice quantity) to compute the prepayment adjustment instead of the actual returned quantity (SalesCrMemoLine.Quantity). For a full cancellation these are equal, but for a partial return they are not. The procedure restores the full prepayment amount (e.g. 581.10) to the original order line, while Qty. to Invoice is now only the partial return quantity (1 unit, upper bound 193.70). No value satisfies both bounds simultaneously.
Call chain when error fires:
Sales-Post (Codeunit 80) :: FinalizePosting
→ UpdateSalesOrderLineIfExist(ReturnOrderNo) [local proc]
→ Codeunit 1303 :: UpdateSalesOrderLineIfExist(CrMemoNo)
→ UpdateSalesOrderLinesFromCreditMemo(SalesInvoiceLine, CrMemoLine)
→ UpdateSalesOrderLineInvoicedQuantity(...) ← sets Qty. to Invoice = 1
→ UpdateSalesOrderLinePrepmtAmount(SalesInvoiceLine) ← BUG: uses Quantity = 3
Expected behavior
Posting the partial Sales Return Order succeeds. The prepayment bookkeeping on the original Sales Order line (Prepmt Amt Deducted, Prepmt Amt to Deduct) is adjusted proportionally to the quantity actually returned, not the full original invoice quantity.
For the example scenario (return qty = 1 of 3, unit price = 193.70):
| Field |
Expected value |
AdjustmentAmount |
1 × (581.10 / 3) = 193.70 |
Prepmt Amt Deducted |
581.10 − 193.70 = 387.40 |
Prepmt Amt to Deduct |
0 + 193.70 = 193.70 |
| Upper bound check |
193.70 ≤ 1 × 193.70 ✅ |
Steps to reproduce
- Create a Sales Order for a customer with two item lines (Item1 and Item2, quantity 3 each).
- Set Prepayment % = 100 on the order header.
- Post the prepayment invoice (Post Prepayment Invoice).
- On Item1's line, set Qty. to Ship = 0.
- Post the order (Ship and Invoice) — only Item2 is shipped and invoiced.
- Create a new Sales Return Order for the same customer.
- Use Get Posted Document Lines to Reverse (Posted Shipments) — select the shipment for Item2.
- Change the return quantity to 1 (partial return of the 3 that were shipped).
- Post the return order (Receive and Invoice).
Result: Error Prepmt Amt to Deduct cannot be more than <UnitPrice> in Sales Line ...
Expected: Return order posts successfully.
A BC page scripting file (Create SO & RSO.yml) is attached to automate these steps exactly.
Prerequisites to run the script on a standard BC environment:
- A No. Series must be configured for prepayment invoices (Sales & Receivables Setup → Posted Prepmt. Inv. Nos.)
- A Sales Prepayment Account must be set on the relevant combination in General Posting Setup (Sales Prepayments Account field)
Additional context
Proposed fix — Codeunit 1303 "Correct Posted Sales Invoice"
UpdateSalesOrderLinePrepmtAmount is a local procedure (no external callers). It has two call sites, both within Codeunit 1303. The fix is to add a ReturnedQuantity parameter and use it in the calculation.
1. Change the procedure signature:
// BEFORE
local procedure UpdateSalesOrderLinePrepmtAmount(SalesInvoiceLine: Record "Sales Invoice Line")
// AFTER
local procedure UpdateSalesOrderLinePrepmtAmount(SalesInvoiceLine: Record "Sales Invoice Line"; ReturnedQuantity: Decimal)
2. Replace SalesInvoiceLine.Quantity with ReturnedQuantity in the three Round(...) calls inside the procedure body:
// BEFORE (all three occurrences)
Round(SalesInvoiceLine.Quantity * (...), Currency."Amount Rounding Precision")
// AFTER
Round(ReturnedQuantity * (...), Currency."Amount Rounding Precision")
3. Update call site in UpdateSalesOrderLinesFromCreditMemo — use the actual returned quantity:
// BEFORE
UpdateSalesOrderLinePrepmtAmount(SalesInvoiceLine);
// AFTER (SalesCrMemoLine is already in scope)
UpdateSalesOrderLinePrepmtAmount(SalesInvoiceLine, SalesCrMemoLine.Quantity);
4. Update call site in UpdateSalesOrderLinesFromCancelledInvoice — preserve existing behavior (full qty):
// BEFORE
UpdateSalesOrderLinePrepmtAmount(SalesInvoiceLine);
// AFTER (no behavioral change — cancellation reverses the full invoice quantity)
UpdateSalesOrderLinePrepmtAmount(SalesInvoiceLine, SalesInvoiceLine.Quantity);
This change is backward-compatible. For full invoice cancellations, ReturnedQuantity = SalesInvoiceLine.Quantity and the result is identical to the current code.
Notes
- Tested on BC 27.5 (
w1-27.7.49739.0). BC 26 (w1-26.13.49690.0) is not affected because the call to UpdateSalesOrderLinesFromCreditMemo for return orders was added/activated in a later version.
- An
OnBeforeValidatePrepmtAmttoDeduct integration event exists on Table 37 "Sales Line" and can be used as an extension workaround while a platform fix is pending.
- The separate BC 27 change that removed
SetRange(Type, SalesCrMemoLine.Type::Item) from UpdateSalesOrderLineIfExist (to support the Cancel Invoice with prepayment flow) is a distinct concern and is not the direct cause of this error.
I will provide a fix for a bug
Describe the issue
When posting a Sales Return Order that partially reverses a shipment on a Sales Order with 100% prepayment already invoiced, posting fails with:
The error occurs in
Table 37 "Sales Line", fieldPrepmt Amt to Deduct,OnValidatetrigger (check 2:"Prepmt Amt to Deduct" > "Qty. to Invoice" * "Unit Price").Root cause:
Codeunit 1303 "Correct Posted Sales Invoice", procedureUpdateSalesOrderLinePrepmtAmount, usesSalesInvoiceLine.Quantity(the full original invoice quantity) to compute the prepayment adjustment instead of the actual returned quantity (SalesCrMemoLine.Quantity). For a full cancellation these are equal, but for a partial return they are not. The procedure restores the full prepayment amount (e.g. 581.10) to the original order line, whileQty. to Invoiceis now only the partial return quantity (1 unit, upper bound 193.70). No value satisfies both bounds simultaneously.Call chain when error fires:
Expected behavior
Posting the partial Sales Return Order succeeds. The prepayment bookkeeping on the original Sales Order line (
Prepmt Amt Deducted,Prepmt Amt to Deduct) is adjusted proportionally to the quantity actually returned, not the full original invoice quantity.For the example scenario (return qty = 1 of 3, unit price = 193.70):
AdjustmentAmount1 × (581.10 / 3) = 193.70Prepmt Amt Deducted581.10 − 193.70 = 387.40Prepmt Amt to Deduct0 + 193.70 = 193.70193.70 ≤ 1 × 193.70✅Steps to reproduce
Result: Error
Prepmt Amt to Deduct cannot be more than <UnitPrice> in Sales Line ...Expected: Return order posts successfully.
Additional context
Proposed fix —
Codeunit 1303 "Correct Posted Sales Invoice"UpdateSalesOrderLinePrepmtAmountis alocal procedure(no external callers). It has two call sites, both within Codeunit 1303. The fix is to add aReturnedQuantityparameter and use it in the calculation.1. Change the procedure signature:
2. Replace
SalesInvoiceLine.QuantitywithReturnedQuantityin the threeRound(...)calls inside the procedure body:3. Update call site in
UpdateSalesOrderLinesFromCreditMemo— use the actual returned quantity:4. Update call site in
UpdateSalesOrderLinesFromCancelledInvoice— preserve existing behavior (full qty):This change is backward-compatible. For full invoice cancellations,
ReturnedQuantity = SalesInvoiceLine.Quantityand the result is identical to the current code.Notes
w1-27.7.49739.0). BC 26 (w1-26.13.49690.0) is not affected because the call toUpdateSalesOrderLinesFromCreditMemofor return orders was added/activated in a later version.OnBeforeValidatePrepmtAmttoDeductintegration event exists onTable 37 "Sales Line"and can be used as an extension workaround while a platform fix is pending.SetRange(Type, SalesCrMemoLine.Type::Item)fromUpdateSalesOrderLineIfExist(to support the Cancel Invoice with prepayment flow) is a distinct concern and is not the direct cause of this error.I will provide a fix for a bug