"""装箱单 build_context 纯逻辑测试(不连数据库)。 字段集(v3):序号/产品名称/产品型号/量程/数量/位号/备注。 产品名称取自合同表 [客户名称](历史遗留命名,实为产品名称)。 """ from datetime import datetime import pytest from reports.packing_list.transform import ( EmptyBoxError, build_context, ) # —— 真实样本:R04398/箱1,3 项,含位号,合计 4+4+5=13 —— ROWS_R04398 = [ { "paichan_no": "R04398", "box_no": 1, "created_at": datetime(2026, 5, 26, 8, 58, 44, 473000), "zongpai_no": "26B14077", "box_qty": 4, "product_name": "压力表(不锈钢压力表)", "model": "YTH-100.A0.531.M201.M08|BP-088.2312.M08.0A3|WHP.70X20X1.3", "range_": "0-1.6MPa", "weihao": "PG-01105,PG-01106,PG-01107,PG-01108", "remark": "", "order_no": "26YB0410175", }, { "paichan_no": "R04398", "box_no": 1, "created_at": datetime(2026, 5, 26, 8, 58, 44, 473000), "zongpai_no": "26B14078", "box_qty": 4, "product_name": "耐震压力表(不锈钢耐震压力表)", "model": "YTHN-100.A0.531.M201.M05.Y3|BP-088.2312.M05.0A3|WHP.70X20X1.3", "range_": "0-0.4MPa", "weihao": "PG-00401A,PG-00401B,PG-00405A,PG-00405B", "remark": "", "order_no": "26YB0410175", }, { "paichan_no": "R04398", "box_no": 1, "created_at": datetime(2026, 5, 26, 8, 58, 44, 473000), "zongpai_no": "26B14079", "box_qty": 5, "product_name": "耐震压力表(不锈钢耐震压力表)", "model": "YTHN-100.A0.531.M201.M07.Y3|BP-088.2312.M07.0A3|WHP.70X20X1.3", "range_": "0-1MPa", "weihao": "PG-00801A,PG-01201,PG-01202,PG-01203A,PG-01203B", "remark": "", "order_no": "26YB0410175", }, ] def test_box_header_fields(): ctx = build_context(ROWS_R04398) assert ctx["paichan_no"] == "R04398" assert ctx["box_no"] == 1 assert ctx["pack_date"] == "2026-05-26" assert ctx["order_no"] == "26YB0410175" def test_total_qty_is_sum(): ctx = build_context(ROWS_R04398) assert ctx["total_qty"] == 13 # 4 + 4 + 5 def test_rows_flattened_with_sequence(): """明细行预展开为 r0_/r1_/r2_,序号从 1 起。""" ctx = build_context(ROWS_R04398) assert ctx["r0_seq"] == "1" assert ctx["r1_seq"] == "2" assert ctx["r2_seq"] == "3" assert ctx["r0_qty"] == "4" assert ctx["r2_qty"] == "5" def test_product_name_from_kehu_mingcheng(): """产品名称取自 product_name(即合同表 [客户名称])。""" ctx = build_context(ROWS_R04398) assert ctx["r0_product_name"] == "压力表(不锈钢压力表)" assert ctx["r1_product_name"] == "耐震压力表(不锈钢耐震压力表)" def test_row_show_flags(): ctx = build_context(ROWS_R04398) assert ctx["r0_show"] == "1" assert ctx["r2_show"] == "1" assert ctx["r3_show"] == "" # 第 4 行无数据 def test_weihao_populated(): ctx = build_context(ROWS_R04398) assert ctx["r0_weihao"] == "PG-01105,PG-01106,PG-01107,PG-01108" def test_remark_blank_when_empty(): """备注为空时留白。""" ctx = build_context(ROWS_R04398) assert ctx["r0_remark"] == "" def test_weihao_and_remark_blank(): rows = [ { "paichan_no": "R07425", "box_no": 1, "created_at": datetime(2026, 5, 26, 8, 58, 44), "zongpai_no": "26B24529", "box_qty": 1, "product_name": "耐震压力表", "model": "YTHN-100.A0.531.M201.M09.Y3|BP-088.2312.M09.0p3", "range_": "0-2.5MPa", "weihao": "", "remark": "", "order_no": "26YB0210489", } ] ctx = build_context(rows) assert ctx["r0_weihao"] == "" assert ctx["r0_remark"] == "" assert ctx["total_qty"] == 1 def test_none_values_cleaned_to_blank(): rows = [ { "paichan_no": "X001", "box_no": 2, "created_at": None, "zongpai_no": "26B1", "box_qty": None, "product_name": None, "model": None, "range_": None, "weihao": None, "remark": None, "order_no": None, } ] ctx = build_context(rows) assert ctx["pack_date"] == "" assert ctx["order_no"] == "" assert ctx["r0_product_name"] == "" assert ctx["r0_model"] == "" assert ctx["r0_qty"] == "0" assert ctx["total_qty"] == 0 def test_iso_string_date(): rows = [ { "paichan_no": "X002", "box_no": 1, "created_at": "2026-07-30T10:00:00Z", "zongpai_no": "26B2", "box_qty": 7, "product_name": "P", "model": "M", "range_": "R", "weihao": "W", "remark": "RM", "order_no": "O", } ] ctx = build_context(rows) assert ctx["pack_date"] == "2026-07-30" def test_empty_rows_raise(): with pytest.raises(EmptyBoxError): build_context([]) def test_row_heights_estimated(): ctx = build_context(ROWS_R04398) assert len(ctx["row_heights"]) == 8 assert all(h > 0 for h in ctx["row_heights"][:3]) assert ctx["row_heights"][3] == 0.0