0110

Ship Now, Receive Later

transfer-order
in-transit
item-ledger-entry
inventory-posting

Ship Now, Receive Later

When goods move between two warehouses they spend days on a truck — no longer at the source, not yet at the destination. Business Central models that limbo honestly: at shipment the quantity is posted out of the from-location and into a dedicated in-transit location, and at receipt out of in-transit and into the destination. Your job is to drive that lifecycle from code and to answer, at any moment in between, "what is where?" — straight from the item ledger.

Requirements

Create a codeunit named "Transfer Flow" with five public procedures:

procedure CreateOrder(ItemNo: Code[20]; FromLocation: Code[10]; InTransitLocation: Code[10]; ToLocation: Code[10]; Quantity: Decimal): Code[20]
procedure Ship(OrderNo: Code[20]; QtyToShip: Decimal)
procedure Receive(OrderNo: Code[20]; QtyToReceive: Decimal)
procedure DirectTransfer(ItemNo: Code[20]; FromLocation: Code[10]; ToLocation: Code[10]; Quantity: Decimal)
procedure OnHand(ItemNo: Code[20]; LocationCode: Code[10]): Decimal

Rules:

  1. CreateOrder creates a genuine, unposted transfer order — a Transfer Header carrying the three location codes plus exactly one Transfer Line for the item and quantity — and returns the order's number. Creating an order writes no item ledger entries.
  2. Ship posts a transfer shipment of exactly QtyToShip, which may be less than the ordered quantity. Shipping a quantity q writes exactly two item ledger entries, both with Entry Type = Transfer: -q at FromLocation and +q at InTransitLocation — while underway the goods are at neither real warehouse.
  3. After a partial shipment the order's own line — the one with "Derived From Line No." = 0; posting may add derived bookkeeping lines, which the tests ignore — must read: Quantity = the ordered quantity, "Quantity Shipped" = what has been shipped so far, "Outstanding Quantity" = the rest, "Qty. in Transit" = shipped but not yet received.
  4. Receive posts a transfer receipt of exactly QtyToReceive and writes the mirroring two entries: -q at InTransitLocation and +q at ToLocation. A full ship-then-receive of quantity q therefore leaves exactly four Transfer entries on the item.
  5. Asking Receive for more than is currently in transit must raise an error with a message that contains You cannot receive more than, and must not post anything.
  6. DirectTransfer moves the quantity between two real locations in a single step and leaves exactly two Transfer entries: -q at FromLocation and +q at ToLocation — no in-transit stop, no other entries.
  7. OnHand returns the net sum of the item's item ledger Quantity at the given location, and 0 for a location the item has never touched.
  8. Entry types are part of the contract: a pair of item-journal adjustments produces the right on-hand totals with the wrong history. The tests count the item's Transfer entries and its total entries, so impersonating a transfer with Positive Adjmt./Negative Adjmt. postings fails.

Pick object IDs in the range 50100–50199 and reference other objects by name, never by ID.

What the tests check

The tests create fresh items and locations (two real ones plus an in-transit one), seed stock with their own journal postings, and drive your codeunit through the lifecycle with quantities generated at run time: that CreateOrder returns a real, unposted order; the exact two-entry and four-entry Transfer footprints with the exact signed quantity at each location; the order line's split fields after a partial Ship; that a partial Receive posts exactly the received quantity, leaves the remainder at the in-transit location and updates the line's "Qty. in Transit"; on-hand at all three locations after shipping and again after receiving; the You cannot receive more than error — and that the failed receipt posted nothing; the two-entry direct transfer; and OnHand against ledger entries the tests posted themselves.

Learn More

Hint 1
Nothing here is written into the ledger by hand — every entry the tests expect is the by-product of posting something real. Ask what document models 'goods on a truck between two warehouses' and what journal moves stock in one step: both post Transfer-type item ledger entries, which no pair of adjustments can imitate.
Hint 2
Build the document the way the base app does: insert a Transfer Header (an Insert(true) with a blank number pulls one from the transfer order number series), validate "Transfer-from Code", "Transfer-to Code" and "In-Transit Code", then insert a Transfer Line and validate its "Item No." and Quantity. Shipment and receipt each have a dedicated posting codeunit that releases the order by itself — avoid the Yes/No wrappers, whose confirmation dialog fails a graded run.
Hint 3
For Ship/Receive: Validate the line's "Qty. to Ship" / "Qty. to Receive", Modify(true), then run Codeunit "TransferOrder-Post Shipment" / "TransferOrder-Post Receipt" against the header — validating "Qty. to Receive" above what is in transit raises the exact 'You cannot receive more than...' error the tests expect. For DirectTransfer, post one item journal line with "Entry Type" = Transfer, a "Location Code" and a "New Location Code" through Codeunit "Item Jnl.-Post Line". (A direct-transfer order only matches the two-entry footprint when Inventory Setup's "Direct Transfer Posting" is set to "Direct Transfer" — under the default "Receipt and Shipment" it posts four entries and fails the tests, so the journal path is simpler.) OnHand is a filtered CalcSums over "Item Ledger Entry".Quantity.
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.