test_pl_dibang_date_sync_on_plan_save.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. """计划保存时同步地磅出入库日期 — 单元测试"""
  2. from datetime import date, datetime
  3. from decimal import Decimal
  4. from unittest.mock import MagicMock, patch
  5. import pytest
  6. from app.models.db_models import DbInboundOutbound, DbPurchasePlan
  7. from app.models.pl_models import PlPlan, PlPlanDelivery, PlPlanLoad, PlTransportDbLink
  8. from app.services.pl_dibang_bridge_service import sync_linked_dibang_dates_for_plan
  9. def _make_query_router(mapping):
  10. def query_router(model):
  11. mock = MagicMock()
  12. handler = mapping.get(model)
  13. if handler is None:
  14. return MagicMock()
  15. return handler(mock)
  16. return query_router
  17. @patch("app.services.pl_dibang_bridge_service._inventory_has_weighing_slip", return_value=False)
  18. def test_sync_same_month_updates_inventory_date_only(_mock_weighing):
  19. plan = PlPlan(
  20. id="plan-1",
  21. plan_no="PO1",
  22. plan_type="outbound",
  23. plan_date=date(2026, 8, 31),
  24. pickup_time=datetime(2026, 8, 31, 0, 0),
  25. tenant_no="t1",
  26. )
  27. load = PlPlanLoad(
  28. id="load-1",
  29. plan_id="plan-1",
  30. load_time=datetime(2026, 8, 31, 0, 0),
  31. tenant_no="t1",
  32. del_flag=0,
  33. )
  34. db_plan = DbPurchasePlan(
  35. id="dbp-1",
  36. tenant_no="t1",
  37. plan_no="SX-OLD",
  38. plan_date=date(2026, 8, 15),
  39. start_date=date(2026, 8, 1),
  40. end_date=date(2026, 8, 31),
  41. business_type="出库",
  42. product_id="prod-1",
  43. product="硫酸",
  44. planned_quantity=31.0,
  45. customer_name="客户A",
  46. del_flag=0,
  47. )
  48. inventory = DbInboundOutbound(
  49. id="inv-1",
  50. tenant_no="t1",
  51. date=date(2026, 8, 15),
  52. partner_company="客户A",
  53. business_type="出库",
  54. product_id="prod-1",
  55. product="硫酸",
  56. specification="98%",
  57. quantity=31.0,
  58. plan_no="SX-OLD",
  59. status="待出库",
  60. purchase_plan_id="dbp-1",
  61. del_flag=0,
  62. )
  63. link = PlTransportDbLink(
  64. id="link-1",
  65. tenant_no="t1",
  66. plan_id="plan-1",
  67. transport_id="tr-1",
  68. stop_type="load",
  69. stop_id="load-1",
  70. db_purchase_plan_id="dbp-1",
  71. db_inventory_id="inv-1",
  72. contributed_qty=Decimal("31"),
  73. del_flag=0,
  74. )
  75. db = MagicMock()
  76. def link_q(m):
  77. m.filter.return_value.all.return_value = [link]
  78. return m
  79. def load_q(m):
  80. m.filter.return_value.first.return_value = load
  81. return m
  82. def delivery_q(m):
  83. m.filter.return_value.order_by.return_value.all.return_value = []
  84. return m
  85. def inv_q(m):
  86. m.filter.return_value.first.return_value = inventory
  87. return m
  88. def plan_q(m):
  89. m.filter.return_value.first.return_value = db_plan
  90. return m
  91. db.query.side_effect = _make_query_router(
  92. {
  93. PlTransportDbLink: link_q,
  94. PlPlanLoad: load_q,
  95. PlPlanDelivery: delivery_q,
  96. DbInboundOutbound: inv_q,
  97. DbPurchasePlan: plan_q,
  98. }
  99. )
  100. warnings = sync_linked_dibang_dates_for_plan(
  101. db,
  102. tenant_no="t1",
  103. plan=plan,
  104. user_id="u1",
  105. dept_id="d1",
  106. )
  107. assert warnings == []
  108. assert inventory.date == date(2026, 8, 31)
  109. assert inventory.update_by == "u1"
  110. assert inventory.purchase_plan_id == "dbp-1"
  111. assert db_plan.planned_quantity == 31.0
  112. @patch("app.services.pl_dibang_bridge_service._inventory_has_weighing_slip", return_value=False)
  113. @patch("app.services.pl_dibang_bridge_service.find_monthly_db_plan")
  114. @patch("app.services.pl_dibang_bridge_service._create_db_plan")
  115. def test_sync_cross_month_repoints_purchase_plan(mock_create, mock_find, _mock_weighing):
  116. plan = PlPlan(
  117. id="plan-1",
  118. plan_no="PO20260830002",
  119. plan_type="outbound",
  120. plan_date=date(2026, 9, 1),
  121. pickup_time=datetime(2026, 9, 1, 0, 0),
  122. tenant_no="t1",
  123. )
  124. load = PlPlanLoad(
  125. id="load-1",
  126. plan_id="plan-1",
  127. load_time=datetime(2026, 9, 1, 0, 0),
  128. tenant_no="t1",
  129. del_flag=0,
  130. )
  131. old_db_plan = DbPurchasePlan(
  132. id="dbp-aug",
  133. tenant_no="t1",
  134. plan_no="SX-AUG",
  135. plan_date=date(2026, 8, 31),
  136. start_date=date(2026, 8, 1),
  137. end_date=date(2026, 8, 31),
  138. business_type="出库",
  139. product_id="prod-1",
  140. product="硫酸",
  141. planned_quantity=31.0,
  142. customer_name="客户A",
  143. del_flag=0,
  144. )
  145. new_db_plan = DbPurchasePlan(
  146. id="dbp-sep",
  147. tenant_no="t1",
  148. plan_no="SX-SEP",
  149. plan_date=date(2026, 9, 1),
  150. start_date=date(2026, 9, 1),
  151. end_date=date(2026, 9, 30),
  152. business_type="出库",
  153. product_id="prod-1",
  154. product="硫酸",
  155. planned_quantity=10.0,
  156. customer_name="客户A",
  157. del_flag=0,
  158. )
  159. inventory = DbInboundOutbound(
  160. id="inv-1",
  161. tenant_no="t1",
  162. date=date(2026, 8, 31),
  163. partner_company="客户A",
  164. business_type="出库",
  165. product_id="prod-1",
  166. product="硫酸",
  167. specification="98%",
  168. quantity=31.0,
  169. plan_no="SX-AUG",
  170. status="待出库",
  171. purchase_plan_id="dbp-aug",
  172. del_flag=0,
  173. )
  174. link = PlTransportDbLink(
  175. id="link-1",
  176. tenant_no="t1",
  177. plan_id="plan-1",
  178. transport_id="tr-1",
  179. stop_type="load",
  180. stop_id="load-1",
  181. db_purchase_plan_id="dbp-aug",
  182. db_inventory_id="inv-1",
  183. contributed_qty=Decimal("31"),
  184. del_flag=0,
  185. )
  186. mock_find.return_value = new_db_plan
  187. mock_create.side_effect = AssertionError("should reuse existing September plan")
  188. db = MagicMock()
  189. def link_q(m):
  190. m.filter.return_value.all.return_value = [link]
  191. return m
  192. def load_q(m):
  193. m.filter.return_value.first.return_value = load
  194. return m
  195. def delivery_q(m):
  196. m.filter.return_value.order_by.return_value.all.return_value = []
  197. return m
  198. def inv_q(m):
  199. m.filter.return_value.first.return_value = inventory
  200. return m
  201. def plan_q(m):
  202. m.filter.return_value.first.return_value = old_db_plan
  203. return m
  204. db.query.side_effect = _make_query_router(
  205. {
  206. PlTransportDbLink: link_q,
  207. PlPlanLoad: load_q,
  208. PlPlanDelivery: delivery_q,
  209. DbInboundOutbound: inv_q,
  210. DbPurchasePlan: plan_q,
  211. }
  212. )
  213. warnings = sync_linked_dibang_dates_for_plan(
  214. db,
  215. tenant_no="t1",
  216. plan=plan,
  217. user_id="u1",
  218. dept_id="d1",
  219. )
  220. assert warnings == []
  221. assert inventory.date == date(2026, 9, 1)
  222. assert inventory.purchase_plan_id == "dbp-sep"
  223. assert inventory.plan_no == "SX-SEP"
  224. assert link.db_purchase_plan_id == "dbp-sep"
  225. assert old_db_plan.planned_quantity == 0.0
  226. assert new_db_plan.planned_quantity == 41.0
  227. @patch("app.services.pl_dibang_bridge_service._inventory_has_weighing_slip", return_value=True)
  228. def test_sync_skips_when_weighing_exists(_mock_weighing):
  229. plan = PlPlan(
  230. id="plan-1",
  231. plan_no="PO1",
  232. plan_type="outbound",
  233. plan_date=date(2026, 9, 1),
  234. pickup_time=datetime(2026, 9, 1, 0, 0),
  235. tenant_no="t1",
  236. )
  237. load = PlPlanLoad(
  238. id="load-1",
  239. plan_id="plan-1",
  240. load_time=datetime(2026, 9, 1, 0, 0),
  241. tenant_no="t1",
  242. del_flag=0,
  243. )
  244. inventory = DbInboundOutbound(
  245. id="inv-1",
  246. tenant_no="t1",
  247. date=date(2026, 8, 31),
  248. partner_company="客户A",
  249. business_type="出库",
  250. product="硫酸",
  251. quantity=31.0,
  252. batch_no="CRKDH0002400",
  253. status="待出库",
  254. del_flag=0,
  255. )
  256. link = PlTransportDbLink(
  257. id="link-1",
  258. tenant_no="t1",
  259. plan_id="plan-1",
  260. transport_id="tr-1",
  261. stop_type="load",
  262. stop_id="load-1",
  263. db_purchase_plan_id="dbp-1",
  264. db_inventory_id="inv-1",
  265. contributed_qty=Decimal("31"),
  266. del_flag=0,
  267. )
  268. db = MagicMock()
  269. def link_q(m):
  270. m.filter.return_value.all.return_value = [link]
  271. return m
  272. def load_q(m):
  273. m.filter.return_value.first.return_value = load
  274. return m
  275. def delivery_q(m):
  276. m.filter.return_value.order_by.return_value.all.return_value = []
  277. return m
  278. def inv_q(m):
  279. m.filter.return_value.first.return_value = inventory
  280. return m
  281. db.query.side_effect = _make_query_router(
  282. {
  283. PlTransportDbLink: link_q,
  284. PlPlanLoad: load_q,
  285. PlPlanDelivery: delivery_q,
  286. DbInboundOutbound: inv_q,
  287. }
  288. )
  289. warnings = sync_linked_dibang_dates_for_plan(
  290. db,
  291. tenant_no="t1",
  292. plan=plan,
  293. user_id="u1",
  294. dept_id="d1",
  295. )
  296. assert len(warnings) == 1
  297. assert "CRKDH0002400" in warnings[0]
  298. assert inventory.date == date(2026, 8, 31)