Class: Shipping::BoxCatalog
- Inherits:
-
Object
- Object
- Shipping::BoxCatalog
- Defined in:
- app/services/shipping/box_catalog.rb
Overview
Provides an ordered catalog of real warehouse carton boxes for use in
PackingCalculator and ShippingBoxCalculator.
== Sources
-
US_STANDARD_BOXES / CA_STANDARD_BOXES — empirically derived from ~58,000
authoritative packing records (origin: from_delivery / from_shipment) in
production, joined to the originating warehouse address to distinguish the
two fulfilment centres. Only box sizes used ≥ 50 times by that warehouse
are included; this represents regularly stocked inventory.Box lists were last refreshed from production data in February 2026.
Update when new stock arrives (Uline / Stephen Gould invoices are the
canonical future source of truth). -
WarehousePackage catalog — supplements the standard list with any
additional carton sizes in the warehouse-package table not already covered.
== Ordering
Within the merged catalog, boxes are sorted to minimise carrier surcharges:
- Surcharge score (ascending) — counts dimension-based carrier fees:
length > 48" → FedEx/UPS/Purolator/Canpar additional handling (+1)
width > 30" → FedEx/UPS additional handling (+1)
L+G > 130" → FedEx/UPS/Purolator/Canpar large-package surcharge (+1)
cubic > 17280 → FedEx cubic oversize (+1) - Volume (ascending) — minimises dimensional-weight billing within each tier
- Longest dimension (ascending) — tiebreaker to avoid length-based fees
The result is memoized per process per warehouse. Call BoxCatalog.reset! in
tests or after changing warehouse package inventory.
Constant Summary collapse
- HANDLING_LENGTH =
Carrier surcharge thresholds — dimension-based and weight-based.
48- HANDLING_WIDTH =
FedEx / UPS / Purolator / Canpar additional handling
30- LARGE_PKG_LG =
FedEx / UPS additional handling
130- FEDEX_CUBIC_OS =
FedEx / UPS / Purolator / Canpar large-package surcharge
17_280- HANDLING_WEIGHT =
FedEx / UPS "additional handling by weight": packages over 50 lbs incur the
same additional-handling surcharge as dimension-triggered packages. 50- US_STANDARD_BOXES =
US warehouse box inventory
Boxes used ≥ 50 times by the US fulfilment centre (Northbrook, IL).
Boxes that are predominantly CA (> 80 % CA usage) are listed under
CA_STANDARD_BOXES instead. [ # ── Small boxes (length ≤ 20") ─────────────────────────────────────────── [ 7, 5, 4], # 293 uses [ 9, 5, 5], # 173 uses [10, 7, 4], # 804 uses [10, 8, 5], # 257 uses [10, 10, 5], # 403 uses [10, 10, 8], # 338 uses [11, 8, 5], # 262 uses [11, 11, 9], # 410 uses [12, 7, 7], # 273 uses [14, 13, 10], # 218 uses [14, 14, 2], # 530 uses — flat [14, 14, 4], # 527 uses [14, 14, 14], # 370 uses [15, 15, 3], # 255 uses [15, 15, 5], # 399 uses [15, 15, 12], # 1,134 uses [15, 15, 15], # 278 uses [18, 16, 8], # 2,470 uses [18, 18, 10], # 1,980 uses [19, 17, 9], # 4,901 uses — #1 most-used US box [19, 19, 11], # 412 uses [19, 19, 13], # 2,214 uses [20, 5, 5], # 380 uses [20, 10, 4], # 2,516 uses [20, 10, 6], # 3,428 uses — #2 most-used US box [20, 10, 8], # 2,823 uses [20, 10, 10], # 2,154 uses [20, 16, 14], # 3,547 uses # ── Medium boxes (length 21–36") ───────────────────────────────────────── [21, 11, 5], # 1,015 uses [21, 11, 7], # 1,053 uses [21, 11, 9], # 786 uses [21, 11, 11], # 195 uses [21, 17, 15], # 1,341 uses [24, 6, 6], # 305 uses [24, 12, 12], # 92 uses (also stocked in CA — 212 CA uses) [24, 18, 4], # 237 uses [27, 7, 7], # 176 uses [27, 13, 13], # 230 uses [27, 19, 13], # 320 uses [27, 27, 7], # 312 uses [28, 12, 6], # 455 uses [28, 16, 12], # 1,094 uses [29, 13, 7], # 347 uses [29, 17, 13], # 279 uses [29, 19, 13], # 291 uses [34, 26, 3], # 290 uses — flat panel (also CA) [34, 27, 3], # 252 uses — flat panel [35, 28, 5], # 677 uses [35, 30, 5], # 297 uses [36, 8, 8], # 1,161 uses [36, 24, 3], # 318 uses — flat [36, 24, 5], # 403 uses [36, 24, 7], # 291 uses [36, 30, 5], # 1,874 uses # ── Longer boxes (length 37–48") ───────────────────────────────────────── [37, 9, 9], # 569 uses [37, 22, 10], # 266 uses [37, 25, 7], # 386 uses [37, 27, 4], # 331 uses [37, 28, 7], # 362 uses [37, 30, 6], # 400 uses [37, 31, 6], # 1,308 uses — ⚠ width=31 → additional-handling surcharge [38, 10, 10], # 2,287 uses [38, 12, 12], # 300 uses [38, 22, 10], # 175 uses [39, 11, 11], # 585 uses [39, 15, 15], # 150 uses [40, 6, 6], # 825 uses (also major CA stock) [40, 9, 9], # 177 uses [40, 12, 12], # 1,375 uses [41, 7, 7], # 1,334 uses [41, 9, 9], # 469 uses [41, 11, 11], # 300 uses [41, 13, 13], # 1,284 uses [44, 16, 16], # 1,173 uses [44, 17, 17], # 1,436 uses [44, 22, 4], # 201 uses — wide flat [48, 6, 6], # 807 uses [48, 12, 12], # 152 uses [48, 24, 2], # 642 uses — thin panel [48, 24, 3], # 388 uses [48, 24, 4], # 385 uses [48, 24, 5], # 417 uses # ── Long boxes (length > 48") — all trigger additional-handling surcharge ─ [61, 12, 12], # 235 uses [63, 12, 12], # 172 uses [73, 12, 12], # 424 uses [74, 12, 12], # 334 uses ].freeze
- CA_STANDARD_BOXES =
CA warehouse box inventory
Boxes used ≥ 50 times by the CA fulfilment centre.
Shared boxes (used in both warehouses) are included here too. [ # ── Small boxes (length ≤ 20") ─────────────────────────────────────────── [ 6, 6, 4], # 382 CA uses — CA staple, rarely US [ 9, 8, 6], # 109 CA uses [ 9, 9, 4], # 51 CA uses [10, 7, 4], # 8 CA uses — shared small [10, 10, 5], # 28 CA uses — shared [10, 10, 8], # 77 CA uses [12, 12, 4], # 98 CA uses [12, 12, 6], # 474 CA uses — CA staple [12, 12, 8], # 291 CA uses [12, 12, 10], # 187 CA uses [12, 12, 12], # 109 CA uses [14, 14, 4], # 111 CA uses (also US) [14, 14, 6], # 282 CA uses — CA staple [14, 14, 10], # 58 CA uses [14, 14, 14], # 50 CA uses (also US) [16, 16, 6], # 143 CA uses [16, 16, 10], # 193 CA uses [16, 16, 12], # 389 CA uses — CA staple [20, 10, 4], # 383 CA uses (also major US) [20, 10, 6], # 508 CA uses — #1 shared box [20, 10, 8], # 493 CA uses (also major US) [20, 10, 10], # 40 CA uses — shared [20, 12, 10], # 461 CA uses — CA staple [20, 16, 14], # 165 CA uses (also US) [20, 20, 6], # 135 CA uses # ── Medium boxes (length 21–36") ───────────────────────────────────────── [24, 6, 6], # 289 CA uses (also US) [24, 12, 12], # 212 CA uses (also some US) [26, 8, 8], # 72 CA uses [28, 12, 6], # 58 CA uses (also US) [28, 12, 8], # 95 CA uses [28, 16, 12], # 74 CA uses (also US) [33, 25, 2], # 215 CA uses — flat panel [33, 26, 3], # 53 CA uses — flat panel [33, 27, 4], # 69 CA uses [34, 25, 2], # 51 CA uses — flat [34, 26, 2], # 54 CA uses — flat [34, 26, 3], # 107 CA uses — flat panel (also US) [35, 25, 2], # 99 CA uses — flat [36, 24, 5], # 35 CA uses (also US) [36, 24, 8], # 82 CA uses [36, 30, 5], # 221 CA uses (also US) # ── Longer boxes (length 37–48") ───────────────────────────────────────── [37, 6, 6], # 51 CA uses [37, 25, 7], # 35 CA uses (also US) [40, 6, 6], # 1,021 CA uses — #1 CA box (also major US) [40, 8, 8], # 608 CA uses — CA staple [40, 10, 10], # 473 CA uses — CA staple [40, 20, 20], # 175 CA uses [40, 29, 4], # 50 CA uses — wide flat [43, 21, 2], # 79 CA uses — flat [48, 12, 12], # 167 CA uses (also some US) [48, 16, 16], # 60 CA uses [48, 24, 2], # 83 CA uses — flat panel (also US) [48, 24, 3], # 27 CA uses (also US) [48, 24, 5], # 12 CA uses (also US) # ── Long boxes (length > 48") — all trigger additional-handling surcharge ─ [60, 6, 6], # 53 CA uses [73, 12, 12], # 1 CA use — available if needed for long items ].freeze
Class Method Summary collapse
-
.parcel_boxes(country: :us) ⇒ Object
Returns the catalog for +country+ (:us or :ca), sorted for that carrier mix.
- .reset! ⇒ Object
-
.surcharge_score(dims, country: :us, weight: nil) ⇒ Object
Returns the number of avoidable dimension-based carrier surcharges for a box.
Class Method Details
.parcel_boxes(country: :us) ⇒ Object
Returns the catalog for +country+ (:us or :ca), sorted for that carrier mix.
210 211 212 213 214 215 216 |
# File 'app/services/shipping/box_catalog.rb', line 210 def self.parcel_boxes(country: :us) if country == :ca @ca_parcel_boxes ||= load_parcel_boxes(CA_STANDARD_BOXES, country: :ca) else @us_parcel_boxes ||= load_parcel_boxes(US_STANDARD_BOXES, country: :us) end end |
.reset! ⇒ Object
218 219 220 221 |
# File 'app/services/shipping/box_catalog.rb', line 218 def self.reset! @us_parcel_boxes = nil @ca_parcel_boxes = nil end |
.surcharge_score(dims, country: :us, weight: nil) ⇒ Object
Returns the number of avoidable dimension-based carrier surcharges for a box.
The scoring is carrier-mix-aware:
:us — FedEx (73%) + UPS (15%)
Four conditions checked — all four carriers share these thresholds:
+1 length > 48" FedEx + UPS additional handling
+1 width > 30" FedEx + UPS additional handling
+1 L+G > 130" FedEx large-package + UPS large-package surcharge
+1 cubic > 17280 FedEx oversize (cubic) — FedEx-specific but dominant carrier
:ca — UPS (46%) + Canpar (35%) + Purolator (9%) + Canada Post (10%)
Three conditions checked — cubic dropped (FedEx is only 5% of CA volume):
+1 length > 48" UPS + Canpar over-length + Purolator oversize ($14/pkg)
+1 width > 30" UPS additional handling only (Canpar/Purolator have no width rule)
+1 L+G > 130" UPS + Canpar oversize + Purolator large-package ($65/pkg)
Note: the Purolator large-package fee ($65) is the most expensive dimension-based
surcharge in either country — makes L+G > 130" especially costly for CA shipments.
Returns the number of avoidable carrier surcharges for a box, including a
weight-based additional-handling check. Pass +weight:+ (lbs) to enable it.
Used externally by PackingCalculator to score and guard bins at assignment time.
246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'app/services/shipping/box_catalog.rb', line 246 def self.surcharge_score(dims, country: :us, weight: nil) return 0 if dims.nil? l, w, h = Array(dims).sort.reverse lg = l + (2 * (w + h)) score = 0 score += 1 if l > HANDLING_LENGTH score += 1 if w > HANDLING_WIDTH score += 1 if lg > LARGE_PKG_LG score += 1 if country == :us && (l * w * h) > FEDEX_CUBIC_OS score += 1 if weight.present? && weight.to_f > HANDLING_WEIGHT score end |