Carton Sizing — units per full middle carton¶
One question, one answer: for a given product, how many units are in a full middle carton (the inner case)? That number drives retailer B2B carton sizing (Camel prepack / B2B order build) and PopForge synthetic-order quantities.
Source of truth — NAV (authoritative)
The authoritative source is NAV — the [PopSockets Live$Item Unit of Measure] table. This page is a generated snapshot, not a hand-maintained list. Camel reads NAV live at runtime; this page and its machine-readable twin are derivations, regenerated by scripts/gen_carton_qty.py in the PopForge repo so they can't silently drift from NAV. Don't hand-edit the quantities here — fix NAV, then regenerate.
The rule¶
carton_qty= the 2nd-highest Qty-per-UoM among the UoM tiers that carry a GTIN — the barcoded tier directly below the master CASE.
NAV stores several Unit-of-Measure tiers per item (EACH, INNER PACK, INNER CASE, CASE, WMT PK2, …), each with a "quantity per UoM." On the common shape the master CASE is the highest tier and the carton we ship to retailers is the GTIN-barcoded tier just below it — so filtering to GTIN-barcoded tiers only and taking the 2nd-highest quantity lands on that carton. Gating on GTIN also discards NAV's junk placeholder tiers for free (artifact A below).
The positional rule is a heuristic, not a universal law — cross-check the model table
"2nd-highest barcoded tier" assumes the carton always sits below a master CASE. That holds for every current B2B and PopForge SKU, but it is not universal: where a model stores its carton at the top CASE tier with a smaller real tier beneath it (the MS POPCASE carton-at-CASE cohort — artifact B), the rule undershoots and returns the smaller tier (4 instead of 24). No purely-positional read generalizes. The authoritative answer to "what carton is this model?" is the model → qty table above; run the per-SKU tier read and the model table as cross-checks on each other, and treat any mismatch as a NAV-setup error to fix.
Model → carton qty (full map)¶
NAV's Product Model is a clean predictor of carton size — a small model → qty lookup sizes almost every model with one constant. The full vetted map, mirrored here from the gist CSV, is 49 models with a derivable carton qty plus 8 non-cartonable models (bulk / components / parts — no carton tier). 26 of the 49 ship retailer-B2B today; the other 23 were derived from the broader SKU universe (a 1,194-SKU master list + 1 yr of 940 history = 2,822 SKUs) for when they do.
At a glance: 40 is the most common carton, 24 next, POWERPACK alone at 12, and only the bare MAGSAFE model is mixed (24 and 40 — sized per-SKU). SKUs = how many SKUs carry that model in NAV; blank Notes = uniform qty, no tier issue.
Ships retailer-B2B today (26 models)¶
| Model | Carton qty | SKUs | Notes |
|---|---|---|---|
| MAGSAFE | 24 / 40 | 143 | Mixed — size per-SKU; model too coarse |
| POPGRIP | 40 | 122 | |
| MS POPCASE AND SLIDE | 24 | 41 | Level drift + junk tiers — 24 at CASE and INNER PACK (artifacts A & B) |
| TOP AND CHAIN LITE | 24 | 40 | |
| MAGSAFE POPWALLET+ | 40 | 26 | |
| PLANT | 40 | 20 | |
| KICK-OUT POPGRIP | 40 | 11 | |
| MS KINDLE CASE | 24 | 8 | |
| MAGSAFE POPWALLET+KO | 24 | 6 | |
| POWERPACK | 12 | 5 | |
| SUCK-UP POPGRIP | 24 | 5 | |
| MS KICK-OUT BASE | 24 | 4 | |
| POPWALLET+ | 40 | 2 | |
| BACKSPIN | 40 | 2 | |
| MAGSAFE POPWALLET | 40 | 2 | |
| POPMOUNT FLEX | 24 | 2 | |
| SLIDE INSERTS | 24 | 2 | |
| WRISTLETS | 40 | 2 | |
| POPWALLET | 40 | 1 | |
| POPMOUNT DASH | 24 | 1 | |
| POPMOUNT SURFACE | 40 | 1 | |
| POPMOUNT VENT | 40 | 1 | |
| SLIDE STRETCH | 40 | 1 | |
| POPMOUNT MS VENT | 24 | 1 | |
| POPMOUNT MS SURFACE | 24 | 1 | |
| POPMOUNT2 SUCTION | 24 | 1 |
Mapped, not yet shipping B2B (23 models)¶
Derived from the full NAV item master for when they ship retailer-B2B.
| Model | Carton qty | SKUs | Notes |
|---|---|---|---|
| POPTOP | 40 | 58 | ~98% consistent (2 SKUs show a different qty) |
| POPCASE AND SLIDE | 24 | 30 | |
| SLIDE | 40 | 28 | |
| POPPUCK STARTER KIT | 24 | 23 | |
| STRAPS | 24 | 13 | |
| SCREEN PROTECTOR | 40 | 9 | |
| PHONE MOUNT LAPTOP | 40 | 6 | |
| POPCASE | 24 | 4 | |
| CAN HOLDER TALL | 40 | 4 | |
| STASH | 40 | 4 | |
| POPCHAIN | 40 | 3 | |
| BOTTLE OPENER | 40 | 3 | |
| ARPD HLR AND PCHN | 40 | 3 | |
| LOW-PRO | 24 | 3 | |
| MAGSAFE ADAPTER | 40 | 2 | |
| CAN HOLDER | 40 | 2 | |
| JUMPSTART | 24 | 2 | |
| FIDGET TRAIN | 40 | 1 | |
| CAN HOLDER SLIM | 40 | 1 | |
| CUP SLEEVE | 40 | 1 | |
| ARPD HLR PR AND PCHN | 40 | 1 | |
| POPMOUNT2 PHOTO | 40 | 1 | |
| GRIP AND VNT MNT | 40 | 1 |
Non-cartonable (8 models — no carton tier)¶
Bulk packs, loose components and parts — no inner-case / case / inner-pack tier, so the rule correctly returns nothing. If one ever needs to ship B2B that's an ops decision, not something NAV can answer today.
BULK (17 SKUs) · POPPACK (7) · PLATFORM COMPONENT (6) · LIPS (6) · POCKETABLE PLATFORM (4) · POPPUCK BOOSTER PACK (2) · BULK CAN HOLDER (2) · COMPONENT (1)
NAV data wrinkles¶
Three real issues make a naïve "read one fixed UoM tier" rule unsafe. MAGSAFE is a sizing ambiguity; the other two — A (junk tiers) and B (level drift) — are NAV tier-storage artifacts that happen to co-occur on the same models. The robust read is barcoded tiers only, cross-checked against the model table — the two together, never either alone. All three are worth fixing at the NAV source.
MAGSAFE mixes 24 and 40 — and no NAV field separates them
The bare MAGSAFE model (143 SKUs) carries two carton sizes — most at 40 (standard slim grips) and ~22 at 24 — and no NAV category field distinguishes them (Model / Family / Category all read MAGSAFE / GRIPS for both). The 24s aren't a type; they're physically bulky items mixed in — the real BASE MSC bases plus oversized novelty grips (Plush Hello Kitty, Squishy Archie, Death Star, Resin Ring Pop, Airbag) plus a Costco 2-pack. The determinant lives in packaging, not category: a standard grip has a real inner-case = 40; every 24-packer has no inner-case tier. So MAGSAFE is sized per-SKU — has a barcoded inner-case → 40, else → 24 — never by model. (The named MAGSAFE POPWALLET+ / POPWALLET / POPWALLET+KO are separate, uniform models — 40 / 40 / 24 — not part of this mix.)
Artifact A — junk degenerate tiers (no GTIN), widespread
NAV carries placeholder UoM tiers with quantity 1 and no GTIN — INNER CASE = 1, WMT PK2 = 1, WMT CS2 = 1 — observed directly on TOP AND CHAIN LITE and MS POPCASE AND SLIDE (and not unique to them). A rule that reads a tier by name — e.g. "read the INNER CASE tier" — returns 1. The GTIN filter skips these for free (they carry no barcode), which is exactly why the rule above is GTIN-gated. Live rows (prod NAV):
TOP AND CHAIN LITE 117116: barcoded EACH=1 · WMT CS2=12 · INNER PACK=24 · CASE=96
junk INNER CASE=1 · WMT PK2=1 (no GTIN)
TOP AND CHAIN LITE 807948: barcoded EACH=1 · INNER PACK=24 · CASE=96
junk INNER CASE=1 · WMT CS2=1 · WMT PK2=1 (no GTIN)
MS POPCASE 112835–842: barcoded EACH=1 · INNER PACK=4 · CASE=24
junk INNER CASE=1 · WMT CS2=1 · WMT PK2=1 (no GTIN)
Artifact B — carton qty stored at an inconsistent UoM level (level drift)
MS POPCASE AND SLIDE stores its real 24 carton at different levels across its SKUs: ~23 SKUs at the CASE (cs=24, with a smaller real INNER PACK=4 beneath) and ~18 newer ones at the INNER PACK (ip=24, with a cs=48 master above). No single positional read is right for both cohorts — "read the CASE" returns 48 for the inner-pack cohort, while "2nd-highest barcoded tier" returns 4 for the carton-at-CASE cohort. Only the model table (MS POPCASE → 24) is authoritative here. A NAV data-hygiene item: same carton, inconsistent setup.
The robust read = barcoded tiers + model table
The per-SKU barcoded-tier rule sizes every current B2B and PopForge SKU correctly and discards junk (artifact A) for free, but it can't see through level drift (artifact B). Pair it with the model → qty table: derive per-SKU, compare to the model's expected qty, and flag mismatches as NAV-setup errors. That cross-check is what catches MS POPCASE before it ships wrong. (PopForge's gen_carton_qty.py uses this barcoded-tier rule — correct for all current PF SKUs, none of which are MS POPCASE — and the limitation is noted in the script.)
Retailer order policy¶
Carton size is a SKU property. How much a retailer orders per line is a separate, retailer property (from Kevin, 2026-06-16):
| Retailer | Orders in | Per line |
|---|---|---|
| Target | full cartons | 1 inner case |
| Best Buy | full cartons | 1 inner case |
| Amazon | eaches | small per-unit quantities |
PF test-SKU snapshot¶
Concrete examples pulled live from prod NAV on 2026-06-17 — the current twin snapshot:
| SKU | Model | Description | Carton qty | Test use |
|---|---|---|---|---|
800470 |
POPGRIP | PG-Black-BK BK | 40 | Target |
802693 |
POPMOUNT VENT | PMNT2-G1-Vent-Black | 40 | Target |
805136 |
POPGRIP | PG-Clear-CLR CLR | 40 | Target / Amazon |
805572 |
POPGRIP | PG-Mama Bear-OW OW | 40 | Target |
806290 |
POPGRIP | PG-Lace Noir-(Gls)-BK BK | 40 | Target |
117116 |
TOP AND CHAIN LITE | CMB PT PCL-PKMN-…-Squishy Gengar Shadow Ball | 24 | Best Buy |
807695 |
KICK-OUT POPGRIP | PGKO MSC-Alum-Radial Silver-CLR SL | 40 | Best Buy |
807948 |
TOP AND CHAIN LITE | CMB PT PCL-Clear Glitter-CLRG | 24 | Best Buy |
806827 |
MAGSAFE | PG MSC-Clear-CLR CLR | 40 | Amazon (eaches) |
806828 |
MAGSAFE | PG MSC-Black-BK BK | 40 | NAV-direct default |
Both MAGSAFE SKUs above land at 40 because they carry the barcoded inner-case tier — but MAGSAFE org-wide still spans 24 and 40, so it's always resolved per-SKU (see the MAGSAFE wrinkle). Don't read "MAGSAFE = 40" off this snapshot.
Machine-readable twin¶
The canonical machine-readable copy lives in the PopForge repo — this page and that file are siblings, both regenerated from NAV. Keep them in sync.
- Data:
data/carton_qty.json— the generated snapshot - Generator:
scripts/gen_carton_qty.py— reads NAV, applies the rule, writes the JSON
JSON shape:
{
"skus": { // per-SKU carton_qty (the PF test-SKU set today; regenerable to any SKU list)
"806827": { "model": "MAGSAFE", "description": "PG MSC-Clear-CLR CLR", "carton_qty": 40 }
},
"retailer_order_policy": { // how each retailer orders (carton vs each)
"target": { "unit": "carton" }, "bestbuy": { "unit": "carton" }, "amazon": { "unit": "each" }
},
"model_carton_qty": { "POPGRIP": 40, "TOP AND CHAIN LITE": 24 }, // model → qty rollup
"model_rollup": { // model → { carton_qty, skus[], consistent } for drift detection
"POPGRIP": { "carton_qty": 40, "skus": ["800470", "805136"], "consistent": true }
}
}
To refresh: run scripts/gen_carton_qty.py against prod NAV and commit the diff (in the PopForge repo), then regenerate this page to match. Ping integrations/popforge for a fresh regen.
Related¶
- EDI Pipeline — carton sizing feeds the prepack / B2B order build (stages 7–8).
- Cirro B2B by Vendor — Overview — per-retailer label + order shape; carton qty sets the prepack carton contents, and the retailer order policy above mirrors the per-vendor order shape (Amazon eaches vs Target / Best Buy full cartons).
- Bootcamp — PopForge Primer — the test-order factory that consumes these quantities.
Per-SKU snapshot generated from NAV prod [PopSockets Live$Item Unit of Measure], 2026-06-17. The full model→carton map is mirrored from the gist CSV (prod NAV [PopSockets Live$Item] Product Model + UoM, crossed with 1 yr of retailer-B2B 940s). Source of truth is NAV; refresh the per-SKU set via gen_carton_qty.py (PopForge repo) and the model map from the gist — do not hand-edit values here.