修复:按发货状态读取当前余量

This commit is contained in:
焦龙言
2026-07-27 10:50:25 +08:00
parent 4340d03a70
commit 85faef213e
2 changed files with 81 additions and 15 deletions
@@ -1,11 +1,12 @@
from datetime import datetime, time, timedelta
from typing import Any
from sqlalchemy import ColumnElement, Select, delete, false, or_, select, update
from sqlalchemy import ColumnElement, Select, and_, delete, false, or_, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm.attributes import InstrumentedAttribute
from common.vo import PageModel
from module_business.domain.enums import DocumentStatus, PalletFlowState
from module_business.entity.do.master_do import BizPallet
from module_business.entity.do.operation_do import (
BizFillingRecord,
@@ -656,19 +657,37 @@ class OperationDao:
if shipment is None:
return None
item_result = await db.execute(
select(BizShipmentItem, BizPalletInventory.remaining_packages)
.outerjoin(
BizPalletInventory,
BizPalletInventory.pallet_id == BizShipmentItem.pallet_id,
if shipment.status == DocumentStatus.DRAFT.value:
item_result = await db.execute(
select(BizShipmentItem, BizPalletInventory.remaining_packages)
.outerjoin(
BizPalletInventory,
and_(
BizPalletInventory.pallet_id == BizShipmentItem.pallet_id,
BizPalletInventory.warehouse_id == shipment.source_warehouse_id,
BizPalletInventory.flow_state == PalletFlowState.FILLED_IN_WAREHOUSE.value,
BizPalletInventory.site_id.is_(None),
BizPalletInventory.current_line_id.is_(None),
BizPalletInventory.active_filling_id.is_not(None),
BizPalletInventory.remaining_packages > 0,
),
)
.where(BizShipmentItem.shipment_id == shipment_id)
.order_by(BizShipmentItem.shipment_item_id)
)
.where(BizShipmentItem.shipment_id == shipment_id)
.order_by(BizShipmentItem.shipment_item_id)
)
items = []
for item, remaining_packages in item_result.all():
item.remaining_packages = remaining_packages
items.append(item)
items = []
for item, remaining_packages in item_result.all():
item.remaining_packages = remaining_packages
items.append(item)
else:
item_result = await db.execute(
select(BizShipmentItem)
.where(BizShipmentItem.shipment_id == shipment_id)
.order_by(BizShipmentItem.shipment_item_id)
)
items = list(item_result.scalars().all())
for item in items:
item.remaining_packages = None
shipment.items = items
return shipment
@@ -253,7 +253,7 @@ class ShipmentInfrastructureContractTest(unittest.TestCase):
class ShipmentDetailDaoTest(unittest.IsolatedAsyncioTestCase):
async def test_detail_loads_each_current_remaining_quantity_with_one_left_join(self) -> None:
async def test_draft_detail_loads_only_current_shippable_remaining_quantity_with_one_left_join(self) -> None:
db = MagicMock(spec=AsyncSession)
shipment = make_shipment(pallet_ids=(5, 2, 8))
order_result = MagicMock()
@@ -288,12 +288,59 @@ class ShipmentDetailDaoTest(unittest.IsolatedAsyncioTestCase):
)
self.assertIn(
'LEFT OUTER JOIN biz_pallet_inventory '
'ON biz_pallet_inventory.pallet_id = biz_shipment_item.pallet_id',
'ON biz_pallet_inventory.pallet_id = biz_shipment_item.pallet_id '
'AND biz_pallet_inventory.warehouse_id = 7 '
"AND biz_pallet_inventory.flow_state = 'FILLED_IN_WAREHOUSE' "
'AND biz_pallet_inventory.site_id IS NULL '
'AND biz_pallet_inventory.current_line_id IS NULL '
'AND biz_pallet_inventory.active_filling_id IS NOT NULL '
'AND biz_pallet_inventory.remaining_packages > 0',
item_sql,
)
self.assertIn('WHERE biz_shipment_item.shipment_id = 12', item_sql)
self.assertIn('ORDER BY biz_shipment_item.shipment_item_id', item_sql)
async def test_confirmed_detail_preserves_snapshot_and_does_not_read_current_inventory(self) -> None:
db = MagicMock(spec=AsyncSession)
shipment = make_shipment(status='CONFIRMED', pallet_ids=(5, 2))
shipment.items[0].active_filling_id = 105
shipment.items[0].package_qty = 13
shipment.items[1].active_filling_id = 102
shipment.items[1].package_qty = 6
order_result = MagicMock()
order_result.scalar_one_or_none.return_value = shipment
item_result = MagicMock()
item_result.all.return_value = [
(shipment.items[0], 1),
(shipment.items[1], 2),
]
item_result.scalars.return_value.all.return_value = shipment.items
db.execute.side_effect = [order_result, item_result]
actual = await OperationDao.get_shipment_by_id(
db,
12,
warehouse_ids=[7],
site_ids=[9],
)
self.assertIs(shipment, actual)
self.assertEqual(2, db.execute.await_count)
self.assertEqual([13, 6], [item.package_qty for item in shipment.items])
self.assertEqual([None, None], [item.remaining_packages for item in shipment.items])
item_statement = db.execute.await_args_list[1].args[0]
item_sql = ' '.join(
str(
item_statement.compile(
dialect=postgresql.dialect(),
compile_kwargs={'literal_binds': True},
)
).split()
)
self.assertNotIn('biz_pallet_inventory', item_sql)
self.assertIn('WHERE biz_shipment_item.shipment_id = 12', item_sql)
self.assertIn('ORDER BY biz_shipment_item.shipment_item_id', item_sql)
class ShipmentControllerBootstrapTest(unittest.TestCase):
def test_shipment_controller_module_exists(self) -> None: