Skip to content

Sales Order API: restore Completely Shipped after redistributing invoice discounts#29954

Closed
jeffreybulanadi wants to merge 2 commits into
microsoft:mainfrom
jeffreybulanadi:fix/29071-sales-order-completely-shipped
Closed

Sales Order API: restore Completely Shipped after redistributing invoice discounts#29954
jeffreybulanadi wants to merge 2 commits into
microsoft:mainfrom
jeffreybulanadi:fix/29071-sales-order-completely-shipped

Conversation

@jeffreybulanadi
Copy link
Copy Markdown

@jeffreybulanadi jeffreybulanadi commented Apr 21, 2026

Summary

Fixes #29071

Root Cause

In OnAfterGetRecord of both APIV1 - Sales Orders (page 20000) and APIV2 - Sales Orders (page 30028), calling GraphMgtSalesOrderBuffer.RedistributeInvoiceDiscounts(Rec) has an unintended side effect on Rec.\Completely Shipped\`.

The call chain is:

  1. RedistributeInvoiceDiscounts calls SalesCalcDiscountByType.ResetRecalculateInvoiceDisc(SalesHeader)
  2. Which calls SalesLine.ModifyAll(\Recalculate Invoice Disc.\, false) with a filter on Recalculate Invoice Disc. = Yes
  3. This fires OnAfterModifySalesLine on codeunit 5496, which calls UpdateCompletelyShipped(Rec) where Rec still carries the Recalculate Invoice Disc. = Yes filter
  4. UpdateCompletelyShipped calls SearchSalesLine.CopyFilters(SalesLine), inheriting the spurious filter
  5. SearchSalesLine.IsEmpty() returns rue because no lines have Recalculate Invoice Disc. = Yes after the ModifyAll
  6. Rec.\Completely Shipped\ := SearchSalesLine.IsEmpty() is set to rue incorrectly

Fix

Save the value of Rec.\Completely Shipped\` from the database before invoking RedistributeInvoiceDiscounts and restore it afterward. The invoice discount redistribution function has no business modifying shipping status; this ensures the API returns the actual shipment state of the sales lines.

Files Changed

  • Apps/W1/APIV2/app/src/pages/APIV2SalesOrders.Page.al
  • Apps/W1/APIV1/app/src/pages/APIV1SalesOrders.Page.al

How to Test

  1. Create a Sales Order with one or more sales lines (do not ship any lines).
  2. Call GET /api/v2.0/companies({id})/salesOrders({id}).
  3. Verify ullyShipped is alse.
  4. Before this fix, ullyShipped would incorrectly return rue.

Fixes AB#632884

… Sales Order API pages

RedistributeInvoiceDiscounts called in OnAfterGetRecord triggers an
OnAfterModifySalesLine event handler (UpdateCompletelyShipped) that
copies spurious filters from the SalesLine record and incorrectly
evaluates IsEmpty() as true, causing Completely Shipped to be
set to true regardless of actual shipment status.

Save and restore Rec.'Completely Shipped' around the call in both
APIV1 - Sales Orders and APIV2 - Sales Orders pages so that the
value returned by the API reflects the actual state of sales lines.

Fixes microsoft#29071
@jeffreybulanadi jeffreybulanadi requested a review from a team as a code owner April 21, 2026 08:43
@JesperSchulz JesperSchulz added the Integration GitHub request for Integration area label Apr 24, 2026
@JesperSchulz JesperSchulz self-assigned this Apr 28, 2026
@JesperSchulz JesperSchulz added the linked Issue is linked to a Azure Boards work item label Apr 28, 2026
nikolakukrika
nikolakukrika previously approved these changes Apr 28, 2026
Comment thread Apps/W1/APIV1/app/src/pages/APIV1SalesOrders.Page.al Outdated
Replace the IsCompletelyShipped save/restore pattern with Rec.Find()
after RedistributeInvoiceDiscounts. This re-reads the entire Sales Header
record from the database, ensuring all fields including Completely Shipped
reflect the true persisted state regardless of any in-memory side effects
from the invoice discount redistribution call chain.
Copy link
Copy Markdown
Author

@jeffreybulanadi jeffreybulanadi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the fix to use Rec.Find() after RedistributeInvoiceDiscounts, as suggested. This re-reads the entire Sales Header from the database, which addresses both concerns raised:

  1. No risk of overwriting legitimate future updates - the database is the source of truth, and Rec.Find() always reflects the actual persisted state.
  2. All fields are restored, not just Completely Shipped - any other header field that may have been corrupted in-memory by the call chain is also corrected.

SetCalculatedFields() sets only page-level Text variables (BillingPostalAddressJSONText, ShippingPostalAddressJSONText, CurrencyCodeTxt, PartialOrderShipping) - not Rec fields - so Rec.Find() does not undo that work.

@jeffreybulanadi jeffreybulanadi changed the title Fix: Restore Completely Shipped after RedistributeInvoiceDiscounts in Sales Order API pages Sales Order API: restore Completely Shipped after redistributing invoice discounts Apr 30, 2026
@JesperSchulz
Copy link
Copy Markdown
Contributor

Thank you for your contribution and for taking the time to submit this pull request.

At this stage, we’re not able to accept the change. We’d like to revisit this proposal once the migration to BCApps is complete though. At that point, we’ll be in a better position to properly evaluate and validate changes like this.

We appreciate your contribution and encourage you to resubmit or revisit this after the transition has been finalized.

@nikolakukrika
Copy link
Copy Markdown
Contributor

Please open a BC idea for this and lets get votes. One of the issues here is that we are re-reading the record from the OnAfterGetRecord trigger which is not something we do and can lead to the issues down the line. If we are refactoring this code, it would be better to introduce a more stabile solution. The best would be if we could redistribute discounts always, or maybe from a different trigger/background job or a different system. Patching from the OnAfterGetRecord is a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Integration GitHub request for Integration area linked Issue is linked to a Azure Boards work item

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: SalesOrder "Completely Shipped" is always true in "Sales Order Entity Buffer"

3 participants