0072

Trace an Order's Fulfilment Across Posted Documents

sales
posted-documents
partial-shipment
dictionary

Trace an Order's Fulfilment Across Posted Documents

"What actually happened to order S-1042?" is a question your support team answers every day, and Business Central makes them earn it: the order was shipped in two parts, invoiced from a separate document, and the base app offers no single view that ties it all together. The order's own lines only show totals — the per-document story lives in the posted tables, which record exactly which order line each posted line came from.

Your job is a codeunit that reconstructs that story for one order line: which posted shipments delivered it, which posted invoices billed it, how an invoice traces back to a specific shipment, and what still has not left the warehouse.

Requirements

Create a codeunit named "Order Fulfilment Trace" with four public procedures:

procedure ShippedQuantityByDocument(OrderNo: Code[20]; OrderLineNo: Integer; var QtyByDocument: Dictionary of [Code[20], Decimal])
procedure InvoicedQuantityByDocument(OrderNo: Code[20]; OrderLineNo: Integer; var QtyByDocument: Dictionary of [Code[20], Decimal])
procedure InvoicedQuantityForShipmentLine(ShipmentNo: Code[20]; ShipmentLineNo: Integer): Decimal
procedure OutstandingQuantity(OrderNo: Code[20]; OrderLineNo: Integer): Decimal

Rules:

  1. ShippedQuantityByDocument fills QtyByDocument with one entry per posted sales shipment that shipped the given order line: the key is the posted shipment's document number, the value is the quantity of that order line shipped on that document. Posted shipment lines record their origin in Sales Shipment Line's "Order No." and "Order Line No." fields.
  2. InvoicedQuantityByDocument does the same for posted sales invoices: Sales Invoice Line carries the same "Order No."/"Order Line No." pair. If one posted invoice carries several lines for the same order line — an invoice assembled from several shipments does — their quantities are summed into that invoice's single entry.
  3. Both dictionary procedures start from a clean slate: whatever the caller left in QtyByDocument is discarded, and an order line with no posted documents yields an empty dictionary.
  4. InvoicedQuantityForShipmentLine answers the reverse question — how much of one posted shipment line has been billed. Posted invoice lines record which shipment line they invoice in Sales Invoice Line's "Shipment No." and "Shipment Line No." fields; return the total invoiced quantity pointing at the given shipment line, or 0 if nothing does.
  5. OutstandingQuantity returns the part of the order line's quantity that has not been shipped yet. It must agree with what the order line itself reports: an order line of 10 with 7 shipped has 3 outstanding. You may assume the order line exists.
  6. Matching is always per order line, not per order: two lines of the same item on one order must keep their own separate trace.
  7. Pick object IDs in the range 50100–50199, and reference other objects by name, never by ID.

What the tests check

The tests build real orders and post them in parts — ship some, ship more, then invoice the shipped quantity, both directly from the order and via a separate invoice pulled from the shipments — and assert the exact content of each dictionary: number of entries, keys, and per-document quantities. They pre-load a stale entry into the dictionary to check it gets discarded, put two lines of the same item on one order to check the traces stay separate — in the shipped map, in the invoiced map, and in the per-shipment-line invoiced total — sum a two-shipment invoice into one entry, ask for the invoiced quantity of a specific shipment line (including one that was never invoiced, expecting 0), and cross-check OutstandingQuantity against the order line's own quantity fields — including a fully shipped line, expecting 0.

Learn More

Hint 1
The order's own lines only hold running totals. Where does Business Central write what each posting actually created? Look at the fields of the posted line tables — they remember exactly where each line came from.
Hint 2
Sales Shipment Line and Sales Invoice Line both carry "Order No." and "Order Line No." pointing back at the originating order line; Sales Invoice Line additionally carries "Shipment No." and "Shipment Line No." naming the shipment line it bills. Filter on the pair you were given, walk the lines, and group quantities by "Document No.".
Hint 3
Dictionary.Add raises an error when the key already exists — and an invoice assembled from two shipments has two lines with the same order line, so check ContainsKey and Set the accumulated value instead. For the outstanding quantity, the order line itself tracks Quantity and "Quantity Shipped".
ALBusiness Central 28.5
Press Compile to check your code compiles — Submit runs the tests.
The code editor is desktop-only
Open this problem on a computer to write and run code. Reading the description, tests and discussion works fine here.