diff --git a/tests/test_purchases.py b/tests/test_purchases.py index 9768bc8..7cc2f73 100644 --- a/tests/test_purchases.py +++ b/tests/test_purchases.py @@ -418,6 +418,200 @@ class PurchaseLogTests(unittest.TestCase): self.assertEqual("1", rows[0]["normalized_quantity"]) self.assertEqual("each", rows[0]["normalized_quantity_unit"]) + def test_build_purchase_rows_derives_effective_price_for_known_cases(self): + fieldnames = enrich_costco.OUTPUT_FIELDS + + def base_row(): + return {field: "" for field in fieldnames} + + giant_banana = base_row() + giant_banana.update( + { + "retailer": "giant", + "order_id": "g1", + "line_no": "1", + "normalized_row_id": "giant:g1:1", + "normalized_item_id": "gnorm:banana", + "order_date": "2026-03-01", + "item_name": "FRESH BANANA", + "item_name_norm": "BANANA", + "retailer_item_id": "100", + "qty": "1", + "unit": "LB", + "normalized_quantity": "1.68", + "normalized_quantity_unit": "lb", + "line_total": "0.99", + "unit_price": "0.99", + "measure_type": "weight", + "price_per_lb": "0.5893", + "raw_order_path": "data/giant-web/raw/g1.json", + "is_discount_line": "false", + "is_coupon_line": "false", + "is_fee": "false", + } + ) + + costco_banana = base_row() + costco_banana.update( + { + "retailer": "costco", + "order_id": "c1", + "line_no": "1", + "normalized_row_id": "costco:c1:1", + "normalized_item_id": "cnorm:banana", + "order_date": "2026-03-12", + "item_name": "BANANAS 3 LB / 1.36 KG", + "item_name_norm": "BANANA", + "retailer_item_id": "30669", + "qty": "1", + "unit": "E", + "normalized_quantity": "3", + "normalized_quantity_unit": "lb", + "line_total": "2.98", + "net_line_total": "1.49", + "unit_price": "2.98", + "size_value": "3", + "size_unit": "lb", + "measure_type": "weight", + "price_per_lb": "0.4967", + "raw_order_path": "data/costco-web/raw/c1.json", + "is_discount_line": "false", + "is_coupon_line": "false", + "is_fee": "false", + } + ) + + giant_ice = base_row() + giant_ice.update( + { + "retailer": "giant", + "order_id": "g2", + "line_no": "1", + "normalized_row_id": "giant:g2:1", + "normalized_item_id": "gnorm:ice", + "order_date": "2026-03-02", + "item_name": "SB BAGGED ICE 20LB", + "item_name_norm": "BAGGED ICE", + "retailer_item_id": "101", + "qty": "2", + "unit": "EA", + "normalized_quantity": "40", + "normalized_quantity_unit": "lb", + "line_total": "9.98", + "unit_price": "4.99", + "size_value": "20", + "size_unit": "lb", + "measure_type": "weight", + "price_per_lb": "0.2495", + "raw_order_path": "data/giant-web/raw/g2.json", + "is_discount_line": "false", + "is_coupon_line": "false", + "is_fee": "false", + } + ) + + costco_patty = base_row() + costco_patty.update( + { + "retailer": "costco", + "order_id": "c2", + "line_no": "1", + "normalized_row_id": "costco:c2:1", + "normalized_item_id": "cnorm:patty", + "order_date": "2026-03-03", + "item_name": "BEEF PATTIES 6# BAG", + "item_name_norm": "BEEF PATTIES 6# BAG", + "retailer_item_id": "777", + "qty": "1", + "unit": "E", + "normalized_quantity": "1", + "normalized_quantity_unit": "each", + "line_total": "26.99", + "net_line_total": "26.99", + "unit_price": "26.99", + "measure_type": "each", + "raw_order_path": "data/costco-web/raw/c2.json", + "is_discount_line": "false", + "is_coupon_line": "false", + "is_fee": "false", + } + ) + + giant_patty = base_row() + giant_patty.update( + { + "retailer": "giant", + "order_id": "g3", + "line_no": "1", + "normalized_row_id": "giant:g3:1", + "normalized_item_id": "gnorm:patty", + "order_date": "2026-03-04", + "item_name": "80% PATTIES PK12", + "item_name_norm": "80% PATTIES PK12", + "retailer_item_id": "102", + "qty": "1", + "unit": "LB", + "normalized_quantity": "", + "normalized_quantity_unit": "", + "line_total": "10.05", + "unit_price": "10.05", + "measure_type": "weight", + "price_per_lb": "7.7907", + "raw_order_path": "data/giant-web/raw/g3.json", + "is_discount_line": "false", + "is_coupon_line": "false", + "is_fee": "false", + } + ) + + rows, _links = build_purchases.build_purchase_rows( + [giant_banana, giant_ice, giant_patty], + [costco_banana, costco_patty], + [], + [], + [], + [], + [], + ) + + rows_by_item = {row["normalized_item_id"]: row for row in rows} + self.assertEqual("0.5893", rows_by_item["gnorm:banana"]["effective_price"]) + self.assertEqual("0.4967", rows_by_item["cnorm:banana"]["effective_price"]) + self.assertEqual("0.2495", rows_by_item["gnorm:ice"]["effective_price"]) + self.assertEqual("26.99", rows_by_item["cnorm:patty"]["effective_price"]) + self.assertEqual("", rows_by_item["gnorm:patty"]["effective_price"]) + + def test_build_purchase_rows_leaves_effective_price_blank_without_valid_denominator(self): + fieldnames = enrich_costco.OUTPUT_FIELDS + row = {field: "" for field in fieldnames} + row.update( + { + "retailer": "giant", + "order_id": "g1", + "line_no": "1", + "normalized_row_id": "giant:g1:1", + "normalized_item_id": "gnorm:blank", + "order_date": "2026-03-01", + "item_name": "MYSTERY ITEM", + "item_name_norm": "MYSTERY ITEM", + "retailer_item_id": "100", + "qty": "1", + "unit": "EA", + "normalized_quantity": "0", + "normalized_quantity_unit": "each", + "line_total": "3.50", + "unit_price": "3.50", + "measure_type": "each", + "raw_order_path": "data/giant-web/raw/g1.json", + "is_discount_line": "false", + "is_coupon_line": "false", + "is_fee": "false", + } + ) + + rows, _links = build_purchases.build_purchase_rows([row], [], [], [], [], [], []) + self.assertEqual("", rows[0]["effective_price"]) + if __name__ == "__main__": unittest.main()