75 lines
2.8 KiB
Python
75 lines
2.8 KiB
Python
"""The bulk calculator reports storage PER DAY, not per month.
|
|
|
|
Consuming it as a monthly figure understated storage 30x — $971 vs $29,127 per
|
|
30 days on one SKU, roughly $0.55/unit against a $1.36/unit unexplained profit
|
|
gap. It is an invisible error (the number looks plausible, just small), so it is
|
|
pinned here rather than left to a comment.
|
|
|
|
How it was established, so a future reader can re-derive it:
|
|
* `storageCharges` scales linearly with `inventory` and is completely
|
|
independent of `saleUnits` — identical at 100 / 1,000 / 10,000 / 43,007.
|
|
* Backing the rate out of item volume gives $0.7800/cu ft/month on two
|
|
independent SKUs to four decimal places, which is Amazon's exact
|
|
standard-size Jan-Sep FBA MONTHLY storage rate.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from dashboard.live_data import _storage_30d
|
|
|
|
|
|
class _Quote:
|
|
def __init__(self, storage):
|
|
self.storage_charges = storage
|
|
|
|
|
|
class _Svc:
|
|
"""Records the call and returns a fixed per-day storage charge."""
|
|
|
|
def __init__(self, storage=32.36, boom=False):
|
|
self.storage, self.boom, self.calls = storage, boom, []
|
|
|
|
def bulk_quote(self, sku, price, sale_units, inventory, marketing=0.0):
|
|
if self.boom:
|
|
raise RuntimeError("bulk calculator down")
|
|
self.calls.append({"sku": sku, "price": price, "sale_units": sale_units,
|
|
"inventory": inventory})
|
|
return _Quote(self.storage)
|
|
|
|
|
|
def test_daily_charge_is_scaled_to_the_window():
|
|
"""32.36/day over 30 days is 970.80 — not 32.36."""
|
|
svc = _Svc(storage=32.36)
|
|
assert _storage_30d(svc, "SKU", 26.07, 51_618, 43_007) == pytest.approx(970.80)
|
|
|
|
|
|
def test_the_real_world_case_that_exposed_the_bug():
|
|
"""$970.91/day of storage is $29,127 per 30 days, not $971."""
|
|
svc = _Svc(storage=970.91)
|
|
got = _storage_30d(svc, "UBMICROFIBERGUSSETPILLOWWHITEQUEEN", 26.07, 51_618, 43_007)
|
|
assert got == pytest.approx(29_127.30)
|
|
assert got - 970.91 == pytest.approx(28_156.39) # what the bug was hiding
|
|
|
|
|
|
@pytest.mark.parametrize("days", [7, 14, 30, 90, 180])
|
|
def test_scales_linearly_with_the_window(days):
|
|
svc = _Svc(storage=10.0)
|
|
assert _storage_30d(svc, "SKU", 20.0, 1000, 5000, days=days) == pytest.approx(10.0 * days)
|
|
|
|
|
|
def test_zero_storage_stays_zero():
|
|
assert _storage_30d(_Svc(storage=0.0), "SKU", 20.0, 1000, 5000) == 0.0
|
|
|
|
|
|
def test_a_failed_lookup_degrades_to_zero_rather_than_raising():
|
|
"""A storage lookup failure must not take down the whole analysis."""
|
|
assert _storage_30d(_Svc(boom=True), "SKU", 20.0, 1000, 5000) == 0.0
|
|
|
|
|
|
def test_inventory_and_units_are_floored_at_one():
|
|
"""The calculator rejects zero, and a new SKU legitimately has neither."""
|
|
svc = _Svc()
|
|
_storage_30d(svc, "SKU", 20.0, units_30d=0, inventory=0)
|
|
assert svc.calls[0]["sale_units"] == 1 and svc.calls[0]["inventory"] == 1
|