FAILURE MAP
← Case archive

FA-95256 / Warehouse slotting and bin packing / Open access

UOM breakdown: eaches consumed first · case 01

Whole cases are picked as loose eaches whenever each picking is allowed.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The unit hierarchy is iterated smallest unit first.

VERIFIED REPAIR

Iterate units from pallet down to each.

Unsuccessful approach: Trying inners before cases breaks full cases into inner packs.

Case contract

qty eaches; pack [eaches per inner, per case, per pallet] with 0 meaning the level does not exist; allowed unit codes for the pick location. Greedy from largest allowed defined unit: PAL, CS, IP, EA. Return [[unit, count]] for nonzero counts in that order, then ["UNPICKABLE", eaches] if a remainder cannot be expressed.

Why this case matters

Pick tasks are expressed in the largest unit a location can present.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(qty, pack, allowed):
    levels = [('EA', 1), ('IP', pack[0]), ('CS', pack[1]), ('PAL', pack[2])]
    rem = qty
    out = []
    for unit, size in levels:
        if unit not in allowed or size <= 0:
            continue
        n = rem // size
        if n:
            out.append([unit, n])
            rem -= n * size
    if rem:
        out.append(['UNPICKABLE', rem])
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['generated mix 4', [500, [4, 12, 120], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 4], ['CS', 1], ['IP', 2]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['generated mix 7', [3, [4, 48, 480], ['PAL', 'CS', 'IP']], [['UNPICKABLE', 3]]], ['generated mix 15', [250, [4, 24, 480], ['PAL', 'IP', 'EA']], [['IP', 62], ['EA', 2]]]], [['generated mix 12', [500, [6, 24, 0], ['CS', 'IP', 'EA']], [['CS', 20], ['IP', 3], ['EA', 2]]], ['generated mix 28', [50, [4, 48, 0], ['PAL', 'CS', 'IP', 'EA']], [['CS', 1], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['generated mix 9', [250, [6, 48, 480], ['PAL', 'CS']], [['CS', 5], ['UNPICKABLE', 10]]], ['generated mix 18', [50, [0, 24, 240], ['CS']], [['CS', 2], ['UNPICKABLE', 2]]]], [['generated mix 25', [50, [6, 24, 0], ['PAL', 'CS', 'IP', 'EA']], [['CS', 2], ['EA', 2]]], ['generated mix 60', [500, [4, 48, 0], ['PAL', 'CS', 'IP']], [['CS', 10], ['IP', 5]]], ['pallet not allowed', [500, [6, 24, 240], ['CS', 'EA']], [['CS', 20], ['EA', 20]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['generated mix 20', [0, [6, 12, 0], ['PAL', 'CS', 'IP']], []], ['generated mix 29', [500, [4, 12, 0], ['CS', 'IP', 'EA']], [['CS', 41], ['IP', 2]]]], [['generated mix 36', [25, [4, 48, 0], ['PAL', 'CS', 'IP', 'EA']], [['IP', 6], ['EA', 1]]], ['generated mix 80', [50, [4, 12, 240], ['PAL', 'CS', 'IP', 'EA']], [['CS', 4], ['EA', 2]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['zero qty', [0, [6, 24, 240], ['EA']], []], ['pallet not allowed', [500, [6, 24, 240], ['CS', 'EA']], [['CS', 20], ['EA', 20]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['generated mix 31', [500, [0, 48, 960], ['EA']], [['EA', 500]]], ['generated mix 40', [3, [4, 48, 480], ['IP']], [['UNPICKABLE', 3]]]], [['generated mix 48', [50, [6, 48, 0], ['CS', 'IP']], [['CS', 1], ['UNPICKABLE', 2]]], ['generated mix 12', [500, [6, 24, 0], ['CS', 'IP', 'EA']], [['CS', 20], ['IP', 3], ['EA', 2]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['zero qty', [0, [6, 24, 240], ['EA']], []], ['generated mix 43', [120, [6, 24, 240], ['PAL', 'CS', 'EA']], [['CS', 5]]], ['generated mix 52', [50, [4, 48, 0], ['EA']], [['EA', 50]]]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
full breakdown[['EA', 251]][['PAL', 1], ['IP', 1], ['EA', 5]]Failed
generated mix 4[['EA', 500]][['PAL', 4], ['CS', 1], ['IP', 2]]Failed
case and each[['EA', 50]][['CS', 2], ['EA', 2]]Failed
undefined inner[['EA', 13]][['EA', 13]]Passed
inner only[['IP', 3], ['UNPICKABLE', 1]][['IP', 3], ['UNPICKABLE', 1]]Passed
no each picking[['IP', 8], ['UNPICKABLE', 2]][['CS', 2], ['UNPICKABLE', 2]]Failed
generated mix 7[['UNPICKABLE', 3]][['UNPICKABLE', 3]]Passed
generated mix 15[['EA', 250]][['IP', 62], ['EA', 2]]Failed

SHA-256 / a6f5f2ed42d4e54412564eecb28a609fdc13c9da6c1b841e8da379305f730728

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(qty, pack, allowed):
    levels = [('PAL', pack[2]), ('IP', pack[0]), ('CS', pack[1]), ('EA', 1)]
    rem = qty
    out = []
    for unit, size in levels:
        if unit not in allowed or size <= 0:
            continue
        n = rem // size
        if n:
            out.append([unit, n])
            rem -= n * size
    if rem:
        out.append(['UNPICKABLE', rem])
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['generated mix 4', [500, [4, 12, 120], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 4], ['CS', 1], ['IP', 2]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['generated mix 7', [3, [4, 48, 480], ['PAL', 'CS', 'IP']], [['UNPICKABLE', 3]]], ['generated mix 15', [250, [4, 24, 480], ['PAL', 'IP', 'EA']], [['IP', 62], ['EA', 2]]]], [['generated mix 12', [500, [6, 24, 0], ['CS', 'IP', 'EA']], [['CS', 20], ['IP', 3], ['EA', 2]]], ['generated mix 28', [50, [4, 48, 0], ['PAL', 'CS', 'IP', 'EA']], [['CS', 1], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['generated mix 9', [250, [6, 48, 480], ['PAL', 'CS']], [['CS', 5], ['UNPICKABLE', 10]]], ['generated mix 18', [50, [0, 24, 240], ['CS']], [['CS', 2], ['UNPICKABLE', 2]]]], [['generated mix 25', [50, [6, 24, 0], ['PAL', 'CS', 'IP', 'EA']], [['CS', 2], ['EA', 2]]], ['generated mix 60', [500, [4, 48, 0], ['PAL', 'CS', 'IP']], [['CS', 10], ['IP', 5]]], ['pallet not allowed', [500, [6, 24, 240], ['CS', 'EA']], [['CS', 20], ['EA', 20]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['generated mix 20', [0, [6, 12, 0], ['PAL', 'CS', 'IP']], []], ['generated mix 29', [500, [4, 12, 0], ['CS', 'IP', 'EA']], [['CS', 41], ['IP', 2]]]], [['generated mix 36', [25, [4, 48, 0], ['PAL', 'CS', 'IP', 'EA']], [['IP', 6], ['EA', 1]]], ['generated mix 80', [50, [4, 12, 240], ['PAL', 'CS', 'IP', 'EA']], [['CS', 4], ['EA', 2]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['zero qty', [0, [6, 24, 240], ['EA']], []], ['pallet not allowed', [500, [6, 24, 240], ['CS', 'EA']], [['CS', 20], ['EA', 20]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['generated mix 31', [500, [0, 48, 960], ['EA']], [['EA', 500]]], ['generated mix 40', [3, [4, 48, 480], ['IP']], [['UNPICKABLE', 3]]]], [['generated mix 48', [50, [6, 48, 0], ['CS', 'IP']], [['CS', 1], ['UNPICKABLE', 2]]], ['generated mix 12', [500, [6, 24, 0], ['CS', 'IP', 'EA']], [['CS', 20], ['IP', 3], ['EA', 2]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['zero qty', [0, [6, 24, 240], ['EA']], []], ['generated mix 43', [120, [6, 24, 240], ['PAL', 'CS', 'EA']], [['CS', 5]]], ['generated mix 52', [50, [4, 48, 0], ['EA']], [['EA', 50]]]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
full breakdown[['PAL', 1], ['IP', 1], ['EA', 5]][['PAL', 1], ['IP', 1], ['EA', 5]]Passed
generated mix 4[['PAL', 4], ['IP', 5]][['PAL', 4], ['CS', 1], ['IP', 2]]Failed
case and each[['CS', 2], ['EA', 2]][['CS', 2], ['EA', 2]]Passed
undefined inner[['EA', 13]][['EA', 13]]Passed
inner only[['IP', 3], ['UNPICKABLE', 1]][['IP', 3], ['UNPICKABLE', 1]]Passed
no each picking[['IP', 8], ['UNPICKABLE', 2]][['CS', 2], ['UNPICKABLE', 2]]Failed
generated mix 7[['UNPICKABLE', 3]][['UNPICKABLE', 3]]Passed
generated mix 15[['IP', 62], ['EA', 2]][['IP', 62], ['EA', 2]]Passed

SHA-256 / fcb37366c6d3843035efc9a6460993fefbc6e5dec9042280dd13c4fb26cfd1d9

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(qty, pack, allowed):
    levels = [('PAL', pack[2]), ('CS', pack[1]), ('IP', pack[0]), ('EA', 1)]
    rem = qty
    out = []
    for unit, size in levels:
        if unit not in allowed or size <= 0:
            continue
        n = rem // size
        if n:
            out.append([unit, n])
            rem -= n * size
    if rem:
        out.append(['UNPICKABLE', rem])
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['generated mix 4', [500, [4, 12, 120], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 4], ['CS', 1], ['IP', 2]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['generated mix 7', [3, [4, 48, 480], ['PAL', 'CS', 'IP']], [['UNPICKABLE', 3]]], ['generated mix 15', [250, [4, 24, 480], ['PAL', 'IP', 'EA']], [['IP', 62], ['EA', 2]]]], [['generated mix 12', [500, [6, 24, 0], ['CS', 'IP', 'EA']], [['CS', 20], ['IP', 3], ['EA', 2]]], ['generated mix 28', [50, [4, 48, 0], ['PAL', 'CS', 'IP', 'EA']], [['CS', 1], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['generated mix 9', [250, [6, 48, 480], ['PAL', 'CS']], [['CS', 5], ['UNPICKABLE', 10]]], ['generated mix 18', [50, [0, 24, 240], ['CS']], [['CS', 2], ['UNPICKABLE', 2]]]], [['generated mix 25', [50, [6, 24, 0], ['PAL', 'CS', 'IP', 'EA']], [['CS', 2], ['EA', 2]]], ['generated mix 60', [500, [4, 48, 0], ['PAL', 'CS', 'IP']], [['CS', 10], ['IP', 5]]], ['pallet not allowed', [500, [6, 24, 240], ['CS', 'EA']], [['CS', 20], ['EA', 20]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['undefined inner', [13, [0, 12, 0], ['IP', 'EA']], [['EA', 13]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['generated mix 20', [0, [6, 12, 0], ['PAL', 'CS', 'IP']], []], ['generated mix 29', [500, [4, 12, 0], ['CS', 'IP', 'EA']], [['CS', 41], ['IP', 2]]]], [['generated mix 36', [25, [4, 48, 0], ['PAL', 'CS', 'IP', 'EA']], [['IP', 6], ['EA', 1]]], ['generated mix 80', [50, [4, 12, 240], ['PAL', 'CS', 'IP', 'EA']], [['CS', 4], ['EA', 2]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['zero qty', [0, [6, 24, 240], ['EA']], []], ['pallet not allowed', [500, [6, 24, 240], ['CS', 'EA']], [['CS', 20], ['EA', 20]]], ['case and each', [50, [6, 24, 0], ['CS', 'EA']], [['CS', 2], ['EA', 2]]], ['generated mix 31', [500, [0, 48, 960], ['EA']], [['EA', 500]]], ['generated mix 40', [3, [4, 48, 480], ['IP']], [['UNPICKABLE', 3]]]], [['generated mix 48', [50, [6, 48, 0], ['CS', 'IP']], [['CS', 1], ['UNPICKABLE', 2]]], ['generated mix 12', [500, [6, 24, 0], ['CS', 'IP', 'EA']], [['CS', 20], ['IP', 3], ['EA', 2]]], ['inner only', [13, [4, 12, 0], ['IP']], [['IP', 3], ['UNPICKABLE', 1]]], ['full breakdown', [251, [6, 24, 240], ['PAL', 'CS', 'IP', 'EA']], [['PAL', 1], ['IP', 1], ['EA', 5]]], ['no each picking', [50, [6, 24, 0], ['CS', 'IP']], [['CS', 2], ['UNPICKABLE', 2]]], ['zero qty', [0, [6, 24, 240], ['EA']], []], ['generated mix 43', [120, [6, 24, 240], ['PAL', 'CS', 'EA']], [['CS', 5]]], ['generated mix 52', [50, [4, 48, 0], ['EA']], [['EA', 50]]]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
full breakdown[['PAL', 1], ['IP', 1], ['EA', 5]][['PAL', 1], ['IP', 1], ['EA', 5]]Passed
generated mix 4[['PAL', 4], ['CS', 1], ['IP', 2]][['PAL', 4], ['CS', 1], ['IP', 2]]Passed
case and each[['CS', 2], ['EA', 2]][['CS', 2], ['EA', 2]]Passed
undefined inner[['EA', 13]][['EA', 13]]Passed
inner only[['IP', 3], ['UNPICKABLE', 1]][['IP', 3], ['UNPICKABLE', 1]]Passed
no each picking[['CS', 2], ['UNPICKABLE', 2]][['CS', 2], ['UNPICKABLE', 2]]Passed
generated mix 7[['UNPICKABLE', 3]][['UNPICKABLE', 3]]Passed
generated mix 15[['IP', 62], ['EA', 2]][['IP', 62], ['EA', 2]]Passed

SHA-256 / 19b5223dba8f9017d06e9753453f1cd71394a3a75e37c5d97d756894cb23b666

Verification & scope

Greedy stipulated conversion with integer pack sizes. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:52:12.030493+00:00.

Case digest / c34a64817b6ca59ef7cb050316722a6520a2af91681901e1369f9004ef1deee6