from typing import Optional from .inventory_pallet_fit_data import PALLETS from .inventory_pallet_fit_types import Carton, PalletFit, PalletType def _cartons_per_layer(length: int, width: int, a: int, b: int) -> int: """Cartons per layer on a ``length`` x ``width`` deck, base ``a`` x ``b``, as the best two-block pattern: one split across the length or the width, each block a grid with the carton one way round. """ best = 0 i = 0 while i * a <= length: best = max(best, i * (width // b) + ((length - i * a) // b) * (width // a)) i += 1 j = 0 while j * b <= width: best = max(best, j * (length // a) + ((width - j * b) // a) * (length // b)) j += 1 return best def _positive(name: str, value: int) -> None: if isinstance(value, bool) or not isinstance(value, int) or value <= 0: raise ValueError("%s must be a whole number greater than zero, received %r" % (name, value)) def pallet_fit( carton: Carton, pallet: PalletType, max_load_height_mm: int, max_load_weight_grams: Optional[int], upright: bool ) -> PalletFit: """The best stack of identical cartons on a UK or EUR pallet within the limits.""" _positive("lengthMm", carton.length_mm) _positive("widthMm", carton.width_mm) _positive("heightMm", carton.height_mm) wg = carton.weight_grams if isinstance(wg, bool) or not isinstance(wg, int) or wg < 0: raise ValueError("weightGrams must be a whole number, not negative, received %r" % (wg,)) _positive("maxLoadHeightMm", max_load_height_mm) mw = max_load_weight_grams if mw is not None and (isinstance(mw, bool) or not isinstance(mw, int) or mw < 0): raise ValueError("maxLoadWeightGrams must be a whole number, not negative, received %r" % (mw,)) deck = next((p for p in PALLETS if p.code == pallet), None) if deck is None: raise ValueError('unknown pallet "%s": expected uk or euro' % (pallet,)) l, w, h = carton.length_mm, carton.width_mm, carton.height_mm choices = [(h, l, w)] if upright else [(h, l, w), (w, l, h), (l, w, h)] best: Optional[PalletFit] = None for vertical, a, b in choices: per_layer = _cartons_per_layer(deck.length_mm, deck.width_mm, a, b) layers = max_load_height_mm // vertical per_pallet = per_layer * layers limited_by = "footprint" if per_layer == 0 else "height" if per_layer > 0 and mw is not None and wg > 0: by_weight = mw // wg if by_weight < per_pallet: per_pallet = by_weight limited_by = "weight" fit = PalletFit( pallet_length_mm=deck.length_mm, pallet_width_mm=deck.width_mm, carton_height_mm=vertical, per_layer=per_layer, layers=layers, per_pallet=per_pallet, limited_by=limited_by, load_height_mm=0 if per_layer == 0 else -(-per_pallet // per_layer) * vertical, load_weight_grams=per_pallet * wg, ) if best is None or fit.per_pallet > best.per_pallet: best = fit assert best is not None return best