Skip to content

[Bug]: Partial Sales Return Order against fully prepaid Sales Order fails – "Prepmt Amt to Deduct cannot be more than X" #8042

@dawid-gierszewski-westwing

Description

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

  1. Create a Sales Order for a customer with two item lines (Item1 and Item2, quantity 3 each).
  2. Set Prepayment % = 100 on the order header.
  3. Post the prepayment invoice (Post Prepayment Invoice).
  4. On Item1's line, set Qty. to Ship = 0.
  5. Post the order (Ship and Invoice) — only Item2 is shipped and invoiced.
  6. Create a new Sales Return Order for the same customer.
  7. Use Get Posted Document Lines to Reverse (Posted Shipments) — select the shipment for Item2.
  8. Change the return quantity to 1 (partial return of the 3 that were shipped).
  9. 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

  • I will provide a fix for a bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    IntegrationGitHub request for Integration area

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions