功能:隔离五类测试业务链路
This commit is contained in:
@@ -8,7 +8,7 @@ from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import CrudResponseModel, PageModel
|
||||
from exceptions.exception import PermissionException, ServiceException
|
||||
from exceptions.exception import PermissionException, ServiceException, ServiceWarning
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_business.dao.access_dao import AccessDao
|
||||
from module_business.dao.inventory_dao import InventoryDao
|
||||
@@ -16,6 +16,7 @@ from module_business.dao.master_dao import MasterDao
|
||||
from module_business.dao.operation_dao import OperationDao
|
||||
from module_business.domain.enums import DocumentStatus, PalletEventType, PalletFlowState
|
||||
from module_business.domain.pallet_state_machine import PalletStateMachine
|
||||
from module_business.domain.test_batch_scope import TestBatchScope
|
||||
from module_business.entity.do.master_do import BizPallet
|
||||
from module_business.entity.do.operation_do import (
|
||||
BizFillingRecord,
|
||||
@@ -36,6 +37,7 @@ from module_business.entity.vo.operation_vo import (
|
||||
ShipmentUpdateModel,
|
||||
)
|
||||
from module_business.service.access_service import AccessService
|
||||
from module_business.service.test_batch_access_service import TestBatchAccessService
|
||||
|
||||
|
||||
class ShipmentService:
|
||||
@@ -97,15 +99,16 @@ class ShipmentService:
|
||||
source_warehouse_id: int,
|
||||
target_site_id: int,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> tuple[Any, Any]:
|
||||
await AccessService.assert_warehouse_access(query_db, current_user, source_warehouse_id)
|
||||
warehouse = await MasterDao.get_warehouse(query_db, source_warehouse_id)
|
||||
warehouse = await MasterDao.get_warehouse(query_db, source_warehouse_id, test_scope)
|
||||
if warehouse is None:
|
||||
raise ServiceException(message='来源仓库不存在')
|
||||
if warehouse.status != '0':
|
||||
raise ServiceException(message='来源仓库已停用')
|
||||
await AccessService.assert_site_access(query_db, current_user, target_site_id)
|
||||
site = await MasterDao.get_site(query_db, target_site_id)
|
||||
site = await MasterDao.get_site(query_db, target_site_id, test_scope)
|
||||
if site is None:
|
||||
raise ServiceException(message='目标现场不存在')
|
||||
if site.status != '0':
|
||||
@@ -158,10 +161,11 @@ class ShipmentService:
|
||||
query_db: AsyncSession,
|
||||
pallet_ids: list[int],
|
||||
source_warehouse_id: int,
|
||||
test_scope: TestBatchScope,
|
||||
) -> tuple[list[BizPalletInventory], list[BizPallet], list[BizFillingRecord]]:
|
||||
ordered_ids = cls._validate_pallet_ids(pallet_ids)
|
||||
inventories = await InventoryDao.get_inventories_for_update(query_db, ordered_ids)
|
||||
pallets = await InventoryDao.get_pallets_by_ids(query_db, ordered_ids)
|
||||
inventories = await InventoryDao.get_inventories_for_update(query_db, ordered_ids, test_scope)
|
||||
pallets = await InventoryDao.get_pallets_by_ids(query_db, ordered_ids, test_scope)
|
||||
if (
|
||||
len(inventories) != len(ordered_ids)
|
||||
or len(pallets) != len(ordered_ids)
|
||||
@@ -181,7 +185,7 @@ class ShipmentService:
|
||||
|
||||
fillings = []
|
||||
for filling_id in sorted(set(active_filling_ids)):
|
||||
filling = await OperationDao.get_active_filling_for_update(query_db, filling_id)
|
||||
filling = await OperationDao.get_active_filling_for_update(query_db, filling_id, test_scope)
|
||||
if filling is None:
|
||||
raise ServiceException(message='托盘活动装填周期不存在或已关闭')
|
||||
fillings.append(filling)
|
||||
@@ -207,6 +211,7 @@ class ShipmentService:
|
||||
pallet_ids: list[int],
|
||||
user_name: str,
|
||||
current_time: datetime,
|
||||
test_batch_id: int | None,
|
||||
) -> list[BizShipmentItem]:
|
||||
return [
|
||||
BizShipmentItem(
|
||||
@@ -216,6 +221,7 @@ class ShipmentService:
|
||||
package_qty=None,
|
||||
create_by=user_name,
|
||||
create_time=current_time,
|
||||
test_batch_id=test_batch_id,
|
||||
)
|
||||
for pallet_id in sorted(pallet_ids)
|
||||
]
|
||||
@@ -239,6 +245,7 @@ class ShipmentService:
|
||||
query_db: AsyncSession,
|
||||
query_object: ShipmentPageQueryModel,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
is_page: bool = True,
|
||||
) -> PageModel | list[ShipmentModel]:
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
@@ -268,9 +275,10 @@ class ShipmentService:
|
||||
result = await OperationDao.get_shipment_list(
|
||||
query_db,
|
||||
query_object,
|
||||
is_page,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
scope=test_scope,
|
||||
is_page=is_page,
|
||||
)
|
||||
rows = result.rows if isinstance(result, PageModel) else result
|
||||
models = [ShipmentModel.model_validate(record) for record in rows]
|
||||
@@ -284,11 +292,13 @@ class ShipmentService:
|
||||
query_db: AsyncSession,
|
||||
shipment_id: int,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> ShipmentModel:
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
shipment = await OperationDao.get_shipment_by_id(
|
||||
query_db,
|
||||
shipment_id,
|
||||
test_scope,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
@@ -301,12 +311,14 @@ class ShipmentService:
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> list[BusinessOptionModel]:
|
||||
warehouse_ids, _ = await cls._scopes(query_db, current_user)
|
||||
allowed_ids = None if warehouse_ids is None else set(warehouse_ids)
|
||||
warehouses = await MasterDao.list_warehouse(
|
||||
query_db,
|
||||
WarehousePageQueryModel(status='0'),
|
||||
test_scope,
|
||||
False,
|
||||
)
|
||||
options = []
|
||||
@@ -330,12 +342,14 @@ class ShipmentService:
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> list[BusinessOptionModel]:
|
||||
_, site_ids = await cls._scopes(query_db, current_user)
|
||||
allowed_ids = None if site_ids is None else set(site_ids)
|
||||
sites = await MasterDao.list_site(
|
||||
query_db,
|
||||
SitePageQueryModel(status='0'),
|
||||
test_scope,
|
||||
False,
|
||||
)
|
||||
options = []
|
||||
@@ -388,13 +402,14 @@ class ShipmentService:
|
||||
qr_token: str,
|
||||
query_object: ShipmentScanQueryModel,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> PalletScanResponseModel:
|
||||
await AccessService.assert_warehouse_access(
|
||||
query_db,
|
||||
current_user,
|
||||
query_object.source_warehouse_id,
|
||||
)
|
||||
warehouse = await MasterDao.get_warehouse(query_db, query_object.source_warehouse_id)
|
||||
warehouse = await MasterDao.get_warehouse(query_db, query_object.source_warehouse_id, test_scope)
|
||||
if warehouse is None:
|
||||
raise ServiceException(message='来源仓库不存在')
|
||||
if warehouse.status != '0':
|
||||
@@ -404,16 +419,16 @@ class ShipmentService:
|
||||
current_user,
|
||||
query_object.target_site_id,
|
||||
)
|
||||
site = await MasterDao.get_site(query_db, query_object.target_site_id)
|
||||
site = await MasterDao.get_site(query_db, query_object.target_site_id, test_scope)
|
||||
if site is None:
|
||||
raise ServiceException(message='目标现场不存在')
|
||||
if site.status != '0':
|
||||
raise ServiceException(message='目标现场已停用')
|
||||
|
||||
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, qr_token)
|
||||
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, qr_token, test_scope)
|
||||
if pallet is None:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id)
|
||||
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id, test_scope)
|
||||
if inventory is None:
|
||||
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
|
||||
if inventory.pallet_id != pallet.pallet_id or inventory.warehouse_id != query_object.source_warehouse_id:
|
||||
@@ -422,7 +437,7 @@ class ShipmentService:
|
||||
cls._raise_pallet_not_accessible(current_user, '托盘已在客户现场')
|
||||
if inventory.active_filling_id is None:
|
||||
raise ServiceException(message='托盘不存在活动装填周期')
|
||||
filling = await OperationDao.get_filling_by_id(query_db, inventory.active_filling_id)
|
||||
filling = await OperationDao.get_filling_by_id(query_db, inventory.active_filling_id, test_scope)
|
||||
if filling is None:
|
||||
raise ServiceException(message='托盘活动装填周期不存在')
|
||||
cls._validate_scan_pallet(
|
||||
@@ -455,20 +470,24 @@ class ShipmentService:
|
||||
query_db: AsyncSession,
|
||||
page_object: ShipmentCreateModel,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> CrudResponseModel:
|
||||
try:
|
||||
await TestBatchAccessService.lock_active_batch(query_db, test_scope)
|
||||
_, user_name = cls._current_identity(current_user)
|
||||
await cls._validate_active_route(
|
||||
query_db,
|
||||
page_object.source_warehouse_id,
|
||||
page_object.target_site_id,
|
||||
current_user,
|
||||
test_scope,
|
||||
)
|
||||
ordered_ids = cls._validate_pallet_ids(page_object.pallet_ids)
|
||||
await cls._lock_and_validate_pallets(
|
||||
query_db,
|
||||
ordered_ids,
|
||||
page_object.source_warehouse_id,
|
||||
test_scope,
|
||||
)
|
||||
await cls._lock_and_authorize_routes(
|
||||
query_db,
|
||||
@@ -478,6 +497,7 @@ class ShipmentService:
|
||||
hidden_warehouse_ids=set(),
|
||||
hidden_site_ids=set(),
|
||||
require_active=True,
|
||||
test_scope=test_scope,
|
||||
)
|
||||
|
||||
current_time = datetime.now()
|
||||
@@ -490,12 +510,13 @@ class ShipmentService:
|
||||
create_time=current_time,
|
||||
update_by=user_name,
|
||||
update_time=current_time,
|
||||
test_batch_id=test_scope.batch_id,
|
||||
)
|
||||
shipment = await OperationDao.add_shipment(query_db, shipment)
|
||||
await OperationDao.replace_shipment_items(
|
||||
query_db,
|
||||
shipment.shipment_id,
|
||||
cls._build_items(shipment.shipment_id, ordered_ids, user_name, current_time),
|
||||
cls._build_items(shipment.shipment_id, ordered_ids, user_name, current_time, test_scope.batch_id),
|
||||
)
|
||||
await query_db.commit()
|
||||
return cls._success('新增成功')
|
||||
@@ -512,13 +533,16 @@ class ShipmentService:
|
||||
query_db: AsyncSession,
|
||||
page_object: ShipmentUpdateModel,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> CrudResponseModel:
|
||||
try:
|
||||
await TestBatchAccessService.lock_active_batch(query_db, test_scope)
|
||||
_, user_name = cls._current_identity(current_user)
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
shipment = await OperationDao.get_shipment_for_update(
|
||||
query_db,
|
||||
page_object.shipment_id,
|
||||
test_scope,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
@@ -534,12 +558,14 @@ class ShipmentService:
|
||||
page_object.source_warehouse_id,
|
||||
page_object.target_site_id,
|
||||
current_user,
|
||||
test_scope,
|
||||
)
|
||||
ordered_ids = cls._validate_pallet_ids(page_object.pallet_ids)
|
||||
await cls._lock_and_validate_pallets(
|
||||
query_db,
|
||||
ordered_ids,
|
||||
page_object.source_warehouse_id,
|
||||
test_scope,
|
||||
)
|
||||
await cls._lock_and_authorize_routes(
|
||||
query_db,
|
||||
@@ -555,6 +581,7 @@ class ShipmentService:
|
||||
hidden_warehouse_ids={shipment.source_warehouse_id},
|
||||
hidden_site_ids={shipment.target_site_id},
|
||||
require_active=True,
|
||||
test_scope=test_scope,
|
||||
)
|
||||
|
||||
current_time = datetime.now()
|
||||
@@ -566,7 +593,7 @@ class ShipmentService:
|
||||
await OperationDao.replace_shipment_items(
|
||||
query_db,
|
||||
shipment.shipment_id,
|
||||
cls._build_items(shipment.shipment_id, ordered_ids, user_name, current_time),
|
||||
cls._build_items(shipment.shipment_id, ordered_ids, user_name, current_time, test_scope.batch_id),
|
||||
)
|
||||
await query_db.commit()
|
||||
return cls._success('修改成功')
|
||||
@@ -583,53 +610,9 @@ class ShipmentService:
|
||||
query_db: AsyncSession,
|
||||
shipment_ids: list[int],
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> CrudResponseModel:
|
||||
try:
|
||||
if not shipment_ids or any(
|
||||
type(shipment_id) is not int or shipment_id <= 0 for shipment_id in shipment_ids
|
||||
):
|
||||
raise ServiceException(message='发货单ID必须为正整数')
|
||||
if len(shipment_ids) != len(set(shipment_ids)):
|
||||
raise ServiceException(message='发货单ID不能重复')
|
||||
|
||||
ordered_ids = sorted(shipment_ids)
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
locked_shipments = []
|
||||
for shipment_id in ordered_ids:
|
||||
shipment = await OperationDao.get_shipment_for_update(
|
||||
query_db,
|
||||
shipment_id,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
if shipment is None:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
if shipment.status != DocumentStatus.DRAFT.value:
|
||||
raise ServiceException(message='已确认发货单不允许删除')
|
||||
locked_shipments.append(shipment)
|
||||
|
||||
route_warehouse_ids = {shipment.source_warehouse_id for shipment in locked_shipments}
|
||||
route_site_ids = {shipment.target_site_id for shipment in locked_shipments}
|
||||
await cls._lock_and_authorize_routes(
|
||||
query_db,
|
||||
current_user,
|
||||
warehouse_ids=route_warehouse_ids,
|
||||
site_ids=route_site_ids,
|
||||
hidden_warehouse_ids=route_warehouse_ids,
|
||||
hidden_site_ids=route_site_ids,
|
||||
require_active=False,
|
||||
)
|
||||
|
||||
for shipment_id in ordered_ids:
|
||||
await OperationDao.delete_shipment(query_db, shipment_id)
|
||||
await query_db.commit()
|
||||
return cls._success('删除成功')
|
||||
except IntegrityError:
|
||||
await query_db.rollback()
|
||||
raise ServiceException(message='删除发货单失败,单据状态或关联数据已变化') from None
|
||||
except Exception:
|
||||
await query_db.rollback()
|
||||
raise
|
||||
raise ServiceWarning(message='不支持单条物理删除,请通过测试数据批次清理')
|
||||
|
||||
@staticmethod
|
||||
def _is_exact_confirmed(
|
||||
@@ -656,6 +639,7 @@ class ShipmentService:
|
||||
hidden_warehouse_ids: set[int],
|
||||
hidden_site_ids: set[int],
|
||||
require_active: bool,
|
||||
test_scope: TestBatchScope,
|
||||
) -> None:
|
||||
warehouses: dict[int, Any] = {}
|
||||
warehouse_permission_errors: dict[int, PermissionException] = {}
|
||||
@@ -663,6 +647,7 @@ class ShipmentService:
|
||||
warehouse = await MasterDao.get_warehouse(
|
||||
query_db,
|
||||
warehouse_id,
|
||||
test_scope,
|
||||
for_update=True,
|
||||
)
|
||||
warehouses[warehouse_id] = warehouse
|
||||
@@ -682,6 +667,7 @@ class ShipmentService:
|
||||
site = await MasterDao.get_site(
|
||||
query_db,
|
||||
site_id,
|
||||
test_scope,
|
||||
for_update=True,
|
||||
)
|
||||
sites[site_id] = site
|
||||
@@ -727,6 +713,7 @@ class ShipmentService:
|
||||
shipment: BizShipmentOrder,
|
||||
current_user: CurrentUserModel,
|
||||
*,
|
||||
test_scope: TestBatchScope,
|
||||
require_active: bool = True,
|
||||
) -> None:
|
||||
await cls._lock_and_authorize_routes(
|
||||
@@ -737,6 +724,7 @@ class ShipmentService:
|
||||
hidden_warehouse_ids={shipment.source_warehouse_id},
|
||||
hidden_site_ids={shipment.target_site_id},
|
||||
require_active=require_active,
|
||||
test_scope=test_scope,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -747,12 +735,14 @@ class ShipmentService:
|
||||
idempotency_key: str,
|
||||
request_hash: str,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> CrudResponseModel:
|
||||
try:
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
current = await OperationDao.get_shipment_by_id(
|
||||
query_db,
|
||||
shipment_id,
|
||||
test_scope,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
@@ -761,6 +751,7 @@ class ShipmentService:
|
||||
recovered = await OperationDao.get_shipment_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
idempotency_key,
|
||||
test_scope,
|
||||
warehouse_ids=[current.source_warehouse_id],
|
||||
site_ids=[current.target_site_id],
|
||||
)
|
||||
@@ -776,6 +767,7 @@ class ShipmentService:
|
||||
locked_recovered = await OperationDao.get_shipment_for_update(
|
||||
query_db,
|
||||
shipment_id,
|
||||
test_scope,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
@@ -792,6 +784,7 @@ class ShipmentService:
|
||||
query_db,
|
||||
locked_recovered,
|
||||
current_user,
|
||||
test_scope=test_scope,
|
||||
require_active=False,
|
||||
)
|
||||
return cls._success('确认成功')
|
||||
@@ -805,13 +798,16 @@ class ShipmentService:
|
||||
shipment_id: int,
|
||||
page_object: ConfirmCommandModel,
|
||||
current_user: CurrentUserModel,
|
||||
test_scope: TestBatchScope,
|
||||
) -> CrudResponseModel:
|
||||
try:
|
||||
await TestBatchAccessService.lock_active_batch(query_db, test_scope)
|
||||
user_id, user_name = cls._current_identity(current_user)
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
shipment = await OperationDao.get_shipment_by_id(
|
||||
query_db,
|
||||
shipment_id,
|
||||
test_scope,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
@@ -823,6 +819,7 @@ class ShipmentService:
|
||||
existing = await OperationDao.get_shipment_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
idempotency_key,
|
||||
test_scope,
|
||||
warehouse_ids=[shipment.source_warehouse_id],
|
||||
site_ids=[shipment.target_site_id],
|
||||
)
|
||||
@@ -837,6 +834,7 @@ class ShipmentService:
|
||||
locked_shipment = await OperationDao.get_shipment_for_update(
|
||||
query_db,
|
||||
shipment_id,
|
||||
test_scope,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
@@ -853,6 +851,7 @@ class ShipmentService:
|
||||
query_db,
|
||||
locked_shipment,
|
||||
current_user,
|
||||
test_scope=test_scope,
|
||||
require_active=False,
|
||||
)
|
||||
await query_db.rollback()
|
||||
@@ -874,8 +873,9 @@ class ShipmentService:
|
||||
query_db,
|
||||
ordered_ids,
|
||||
locked_shipment.source_warehouse_id,
|
||||
test_scope,
|
||||
)
|
||||
await cls._lock_and_reauthorize_route(query_db, locked_shipment, current_user)
|
||||
await cls._lock_and_reauthorize_route(query_db, locked_shipment, current_user, test_scope=test_scope)
|
||||
|
||||
for inventory in inventories:
|
||||
PalletStateMachine.ensure_transition(
|
||||
@@ -930,6 +930,7 @@ class ShipmentService:
|
||||
occurred_at=current_time,
|
||||
idempotency_key=idempotency_key,
|
||||
request_hash=request_hash,
|
||||
test_batch_id=test_scope.batch_id,
|
||||
)
|
||||
await InventoryDao.add_event(query_db, inventory, event)
|
||||
|
||||
@@ -943,6 +944,7 @@ class ShipmentService:
|
||||
page_object.idempotency_key,
|
||||
cls._build_confirm_request_hash(shipment_id),
|
||||
current_user,
|
||||
test_scope,
|
||||
)
|
||||
except Exception:
|
||||
await query_db.rollback()
|
||||
|
||||
Reference in New Issue
Block a user