0012

Pallets, Boxes, and Loose Units

Basics
Easy
div
mod
integer-division
rounding
item

Pallets, Boxes, and Loose Units

The warehouse ships every item in boxes, and the boxes are stacked on pallets. A pick list should read "2 pallets, 2 boxes, 7 loose units" — but the current calculator tells the forklift driver to bring 4 boxes for 7 units that come in boxes of 2. Its author reached for /, and in AL / always yields a Decimal, which is silently rounded to the nearest whole number the moment it is assigned to an Integer. The right tools for splitting a quantity are the integer operators div (the whole-number quotient) and mod (the remainder).

Requirements

The starter already contains a table extension "Item Packing Ext" on the Item table with two Integer fields, "Units per Box" and "Boxes per Pallet". Leave it as it is — the tests write both fields directly.

Complete the codeunit "Packing Calculator" with these two public procedures:

procedure SplitQuantity(ItemNo: Code[20]; Quantity: Decimal; var Pallets: Integer; var Boxes: Integer; var Loose: Integer)
procedure PalletsNeeded(ItemNo: Code[20]; Quantity: Decimal): Integer

Rules:

  1. A pallet holds "Boxes per Pallet" × "Units per Box" units. SplitQuantity fills as many full pallets as possible, then as many full boxes as possible from what is left, and reports the rest as loose units. So Boxes is always less than "Boxes per Pallet", Loose is always less than "Units per Box", and Pallets × (units per pallet) + Boxes × "Units per Box" + Loose adds back up to Quantity. A quantity of 0 splits into 0, 0, 0.
  2. PalletsNeeded returns how many pallets it takes to carry the whole quantity: the full pallets, plus one more if any box or loose unit is left over. It rounds up — a single unit still needs one pallet — and a quantity of 0 needs 0 pallets.
  3. Quantity is a Decimal because that is how Business Central stores quantities, but only whole units can be packed. A quantity with a fractional part (12.5) must raise an error with a message that contains the words whole number (lowercase, exactly as written) — never round or truncate it into something packable. Do this check before any arithmetic: div and mod happily accept a Decimal operand and would hide the problem.
  4. If the item's "Units per Box" or "Boxes per Pallet" is 0, raise an error with a message that names that field (Units per Box or Boxes per Pallet). A raw "Attempted to divide by zero" is not acceptable. Item.TestField on the empty field produces exactly such a message.
  5. Both procedures apply rules 3 and 4. The tests never pass a negative quantity and always pass the number of an existing item.

Keep object and field IDs in the range 50100–50199, and reference other objects by name, never by ID. Captions are good practice but not graded.

What the tests check

The grading tests create items with the standard inventory library, set the two packing fields, and call your procedures. SplitQuantity is checked on an exact pallet multiple (no boxes, no loose), a mixed quantity (pallets, boxes and loose all non-zero), a quantity below one box, the 7-units-in-boxes-of-2 case (3 boxes and 1 loose, not 4 boxes), zero, and a generated quantity with generated box and pallet sizes, where the three parts must add back up to the quantity and respect the "less than" bounds from rule 1. PalletsNeeded is checked on an exact multiple, a partial pallet, a single unit, zero, and a generated quantity that must fit on the returned number of pallets but not on one fewer. Six tests use asserterror: a non-whole quantity through each procedure must fail with whole number in the message, and a zero "Units per Box" or a zero "Boxes per Pallet" (both procedures) must fail with the field name in the message. All comparisons are exact.

Learn More

Hint 1
/ always produces a Decimal, and assigning a Decimal to an Integer rounds it to the nearest whole number — 3.5 boxes becomes 4. AL has two operators that stay in whole numbers: one gives the quotient, the other the remainder.
Hint 2
Work top-down: pallets first from the quantity and the units per pallet, then boxes from what is left after the pallets, then loose units from what is left after the boxes. div gives you each level's count and mod what is left for the next level.
Hint 3
Refuse a fractional quantity by comparing it with itself rounded to precision 1 (Round) before any division, and let TestField on each packing field produce the field-naming error for a zero. PalletsNeeded is the full pallets plus one when the split leaves any boxes or loose units.
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.