Class: Shipping::BoxCatalog

Inherits:
Object
  • Object
show all
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

  1. 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).

  2. 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:

  1. 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)
  2. Volume (ascending) — minimises dimensional-weight billing within each tier
  3. 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

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