Add tenant-scoped teams and projects, order attribution across four businesses, conversion inheritance, scoped statistics and audited reassignment. Reuse existing Web and mobile UI patterns; team regions and hospitals remain informational. Include incremental SQL and isolated regression coverage. Actual MySQL and browser/WebView integration validation remains pending; relates to issue #13.
855 lines
26 KiB
Python
855 lines
26 KiB
Python
"""移动市场预签单服务:通用数据权限与现场范围始终组合生效。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import List, Optional, Tuple
|
|
|
|
from fastapi import HTTPException
|
|
from sqlalchemy import func, or_
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.services.project_assignment import (
|
|
create_assignment,
|
|
guard_project_edit,
|
|
filter_assignment,
|
|
)
|
|
from app.crud import presign_order as presign_crud
|
|
from app.models.models import (
|
|
BizCellSampleType,
|
|
BizCollectionHospital,
|
|
BizCollectionRegion,
|
|
BizCollectionRegionStaff,
|
|
BizPresignOrder,
|
|
BizPresignOrderSampleType,
|
|
BizPresignTransfer,
|
|
)
|
|
from app.schemas.market_mobile import (
|
|
MarketPresignOrderBody,
|
|
MarketPresignTransferBody,
|
|
)
|
|
from app.services.market_field_scope import (
|
|
MarketFieldContext,
|
|
apply_presign_scope,
|
|
assert_presign_access,
|
|
)
|
|
from app.services.market_support import validate_attachment_refs
|
|
|
|
|
|
_MONEY_QUANTUM = Decimal('0.01')
|
|
|
|
|
|
def _money_text(value: Optional[Decimal]) -> Optional[str]:
|
|
if value is None:
|
|
return None
|
|
return format(Decimal(str(value)).quantize(_MONEY_QUANTUM), 'f')
|
|
|
|
|
|
def _mobile_transfer_to_dict(
|
|
db: Session, row: BizPresignTransfer, tenant_no: str
|
|
) -> dict:
|
|
data = presign_crud._transfer_to_dict(db, row, tenant_no)
|
|
data['transferAmount'] = _money_text(row.transfer_amount)
|
|
return data
|
|
|
|
|
|
def _reference_data(
|
|
db: Session, rows: List[BizPresignOrder], tenant_no: str
|
|
) -> dict:
|
|
order_ids = [row.id for row in rows]
|
|
region_ids = {row.region_id for row in rows if row.region_id}
|
|
hospital_ids = {row.hospital_id for row in rows if row.hospital_id}
|
|
region_names = {}
|
|
if region_ids:
|
|
region_names = {
|
|
str(row.id): str(row.region_name or '').strip()
|
|
for row in db.query(BizCollectionRegion).filter(
|
|
BizCollectionRegion.id.in_(region_ids),
|
|
BizCollectionRegion.tenant_no == tenant_no,
|
|
)
|
|
}
|
|
hospital_names = {}
|
|
if hospital_ids:
|
|
hospital_names = {
|
|
str(row.id): str(row.hospital_name or '').strip()
|
|
for row in db.query(BizCollectionHospital).filter(
|
|
BizCollectionHospital.id.in_(hospital_ids),
|
|
BizCollectionHospital.tenant_no == tenant_no,
|
|
)
|
|
}
|
|
type_ids_by_order = {order_id: [] for order_id in order_ids}
|
|
if order_ids:
|
|
links = (
|
|
db.query(BizPresignOrderSampleType)
|
|
.filter(
|
|
BizPresignOrderSampleType.presign_order_id.in_(order_ids),
|
|
BizPresignOrderSampleType.tenant_no == tenant_no,
|
|
BizPresignOrderSampleType.del_flag == 0,
|
|
)
|
|
.order_by(
|
|
BizPresignOrderSampleType.order_num.asc(),
|
|
BizPresignOrderSampleType.create_time.asc(),
|
|
BizPresignOrderSampleType.id.asc(),
|
|
)
|
|
.all()
|
|
)
|
|
for link in links:
|
|
if link.sample_type_id:
|
|
type_ids_by_order.setdefault(link.presign_order_id, []).append(
|
|
str(link.sample_type_id)
|
|
)
|
|
type_ids = {
|
|
type_id
|
|
for order_type_ids in type_ids_by_order.values()
|
|
for type_id in order_type_ids
|
|
}
|
|
type_names = {}
|
|
if type_ids:
|
|
type_names = {
|
|
str(row.id): str(row.type_name or '').strip()
|
|
for row in db.query(BizCellSampleType).filter(
|
|
BizCellSampleType.id.in_(type_ids),
|
|
BizCellSampleType.tenant_no == tenant_no,
|
|
)
|
|
}
|
|
return {
|
|
'regionNames': region_names,
|
|
'hospitalNames': hospital_names,
|
|
'sampleTypeIds': type_ids_by_order,
|
|
'sampleTypeNames': {
|
|
order_id: [type_names.get(type_id, '') for type_id in order_type_ids]
|
|
for order_id, order_type_ids in type_ids_by_order.items()
|
|
},
|
|
}
|
|
|
|
|
|
def _mobile_order_to_dict(
|
|
db: Session,
|
|
row: BizPresignOrder,
|
|
tenant_no: str,
|
|
*,
|
|
with_children: bool,
|
|
deposit_amount: Optional[Decimal] = None,
|
|
user_name_map: Optional[dict] = None,
|
|
reference_data: Optional[dict] = None,
|
|
) -> dict:
|
|
deposit = (
|
|
deposit_amount
|
|
if deposit_amount is not None
|
|
else presign_crud.confirmed_deposit_amount(
|
|
db, presign_order_id=row.id, tenant_no=tenant_no
|
|
)
|
|
)
|
|
data = presign_crud.order_to_dict(
|
|
db,
|
|
row,
|
|
tenant_no,
|
|
with_children=False,
|
|
deposit_amount=deposit,
|
|
user_name_map=user_name_map,
|
|
)
|
|
data['contractAmount'] = _money_text(row.contract_amount)
|
|
data['confirmedDepositAmount'] = _money_text(deposit)
|
|
references = reference_data or _reference_data(db, [row], tenant_no)
|
|
data['regionName'] = references['regionNames'].get(row.region_id or '', '')
|
|
data['hospitalName'] = references['hospitalNames'].get(
|
|
row.hospital_id or '', ''
|
|
)
|
|
data['sampleTypeIds'] = references['sampleTypeIds'].get(row.id, [])
|
|
data['sampleTypeNames'] = references['sampleTypeNames'].get(row.id, [])
|
|
if with_children:
|
|
transfer_rows = (
|
|
db.query(BizPresignTransfer)
|
|
.filter(
|
|
BizPresignTransfer.presign_order_id == row.id,
|
|
BizPresignTransfer.tenant_no == tenant_no,
|
|
BizPresignTransfer.del_flag == 0,
|
|
)
|
|
.order_by(BizPresignTransfer.create_time.asc())
|
|
.all()
|
|
)
|
|
data['transfers'] = [
|
|
_mobile_transfer_to_dict(db, transfer, tenant_no)
|
|
for transfer in transfer_rows
|
|
]
|
|
return data
|
|
|
|
|
|
def _dump_body(body: MarketPresignOrderBody) -> dict:
|
|
return body.model_dump(by_alias=False, exclude_unset=True)
|
|
|
|
|
|
def _dump_transfer_body(body: MarketPresignTransferBody) -> dict:
|
|
return body.model_dump(by_alias=False, exclude_unset=True)
|
|
|
|
|
|
def _reject_direct_conversion(fields: dict) -> None:
|
|
if str(fields.get('order_status') or '').strip() == 'converted':
|
|
raise ValueError('预签约订单必须通过转正式操作进入已转正式状态')
|
|
|
|
|
|
def _assert_transfer_order_writable(order: BizPresignOrder) -> None:
|
|
if (order.order_status or '').strip() not in ('', 'draft', 'active'):
|
|
raise ValueError('当前状态的预签约订单不可操作转账记录')
|
|
|
|
|
|
def _scoped_order_for_update(
|
|
db: Session,
|
|
order_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> BizPresignOrder:
|
|
row = (
|
|
apply_presign_scope(
|
|
db.query(BizPresignOrder), db, ctx, user_info=user_info
|
|
)
|
|
.filter(BizPresignOrder.id == order_id)
|
|
.populate_existing()
|
|
.with_for_update()
|
|
.first()
|
|
)
|
|
if row is None:
|
|
raise HTTPException(status_code=404, detail='数据不存在或无权访问')
|
|
return row
|
|
|
|
|
|
def _current_dept_id(user_info: dict) -> str:
|
|
"""兼容 API 用户上下文与测试/内部调用的部门字段命名。"""
|
|
if not isinstance(user_info, dict):
|
|
return ""
|
|
return str(
|
|
user_info.get("dept_id")
|
|
or user_info.get("deptId")
|
|
or user_info.get("region_id")
|
|
or ""
|
|
).strip()
|
|
|
|
|
|
def _manager_assignment_allowed(
|
|
db: Session,
|
|
ctx: MarketFieldContext,
|
|
*,
|
|
region_id: Optional[str],
|
|
region_manager_user_id: Optional[str],
|
|
) -> bool:
|
|
region = (region_id or "").strip()
|
|
if not region:
|
|
return False
|
|
return (
|
|
db.query(BizCollectionRegionStaff.id)
|
|
.filter(
|
|
BizCollectionRegionStaff.tenant_no == ctx.tenant_no,
|
|
BizCollectionRegionStaff.del_flag == 0,
|
|
BizCollectionRegionStaff.user_id == ctx.user_id,
|
|
BizCollectionRegionStaff.is_leader == 1,
|
|
BizCollectionRegionStaff.region_id == region,
|
|
)
|
|
.first()
|
|
is not None
|
|
)
|
|
|
|
|
|
def _ensure_manager_assignment(
|
|
db: Session,
|
|
ctx: MarketFieldContext,
|
|
*,
|
|
region_id: Optional[str],
|
|
region_manager_user_id: Optional[str],
|
|
) -> None:
|
|
if not _manager_assignment_allowed(
|
|
db,
|
|
ctx,
|
|
region_id=region_id,
|
|
region_manager_user_id=region_manager_user_id,
|
|
):
|
|
raise ValueError("预签约订单归属不在当前地区经理范围内")
|
|
|
|
|
|
def _validate_master_data(
|
|
db: Session,
|
|
ctx: MarketFieldContext,
|
|
*,
|
|
region_id: Optional[str],
|
|
hospital_id: Optional[str],
|
|
sample_type_ids: Optional[List[str]],
|
|
) -> None:
|
|
region = (region_id or "").strip()
|
|
hospital = (hospital_id or "").strip()
|
|
if region:
|
|
region_exists = (
|
|
db.query(BizCollectionRegion.id)
|
|
.filter(
|
|
BizCollectionRegion.id == region,
|
|
BizCollectionRegion.tenant_no == ctx.tenant_no,
|
|
BizCollectionRegion.del_flag == 0,
|
|
)
|
|
.first()
|
|
is not None
|
|
)
|
|
if not region_exists:
|
|
raise ValueError("采集地区不存在或不属于当前租户")
|
|
if hospital:
|
|
hospital_exists = (
|
|
db.query(BizCollectionHospital.id)
|
|
.filter(
|
|
BizCollectionHospital.id == hospital,
|
|
BizCollectionHospital.region_id == region,
|
|
BizCollectionHospital.tenant_no == ctx.tenant_no,
|
|
BizCollectionHospital.del_flag == 0,
|
|
)
|
|
.first()
|
|
is not None
|
|
)
|
|
if not hospital_exists:
|
|
raise ValueError("医院不存在、不属于当前租户或与采集地区不匹配")
|
|
if sample_type_ids is not None:
|
|
requested = {
|
|
str(sample_type_id).strip()
|
|
for sample_type_id in sample_type_ids
|
|
if str(sample_type_id or "").strip()
|
|
}
|
|
if requested:
|
|
existing = {
|
|
str(row[0])
|
|
for row in db.query(BizCellSampleType.id)
|
|
.filter(
|
|
BizCellSampleType.id.in_(requested),
|
|
BizCellSampleType.tenant_no == ctx.tenant_no,
|
|
BizCellSampleType.del_flag == 0,
|
|
)
|
|
.all()
|
|
}
|
|
if existing != requested:
|
|
raise ValueError("样本类型不存在或不属于当前租户")
|
|
|
|
|
|
def list_presign_orders(
|
|
db: Session,
|
|
*,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
page: int = 1,
|
|
page_size: int = 10,
|
|
keyword: Optional[str] = None,
|
|
order_status: Optional[str] = None,
|
|
project_id: Optional[str] = None,
|
|
team_id: Optional[str] = None,
|
|
) -> Tuple[List[dict], int]:
|
|
query = apply_presign_scope(
|
|
db.query(BizPresignOrder), db, ctx, user_info=user_info
|
|
)
|
|
query = filter_assignment(
|
|
query, BizPresignOrder, db, ctx.tenant_no, project_id, team_id
|
|
)
|
|
status = (order_status or "").strip()
|
|
if status:
|
|
query = query.filter(BizPresignOrder.order_status == status)
|
|
search = (keyword or "").strip()
|
|
if search:
|
|
like = f"%{search}%"
|
|
query = query.filter(
|
|
or_(
|
|
BizPresignOrder.protocol_no.like(like),
|
|
BizPresignOrder.mother_name.like(like),
|
|
)
|
|
)
|
|
total = (
|
|
query.order_by(None)
|
|
.with_entities(func.count(BizPresignOrder.id))
|
|
.scalar()
|
|
or 0
|
|
)
|
|
safe_page = max(1, page)
|
|
safe_page_size = min(max(1, page_size), 100)
|
|
rows = (
|
|
query.order_by(BizPresignOrder.create_time.desc())
|
|
.offset((safe_page - 1) * safe_page_size)
|
|
.limit(safe_page_size)
|
|
.all()
|
|
)
|
|
order_ids = [row.id for row in rows]
|
|
deposit_map = presign_crud.confirmed_deposit_amount_map(
|
|
db, presign_order_ids=order_ids, tenant_no=ctx.tenant_no
|
|
)
|
|
person_ids = [
|
|
user_id
|
|
for row in rows
|
|
for user_id in (
|
|
row.collector_user_id,
|
|
row.sales_user_id,
|
|
row.region_manager_user_id,
|
|
)
|
|
if user_id
|
|
]
|
|
user_name_map = presign_crud._user_nick_name_map(
|
|
db, person_ids, ctx.tenant_no
|
|
)
|
|
references = _reference_data(db, rows, ctx.tenant_no)
|
|
return [
|
|
_mobile_order_to_dict(
|
|
db,
|
|
row,
|
|
ctx.tenant_no,
|
|
with_children=False,
|
|
deposit_amount=deposit_map.get(row.id, 0),
|
|
user_name_map=user_name_map,
|
|
reference_data=references,
|
|
)
|
|
for row in rows
|
|
], int(total)
|
|
|
|
|
|
def get_presign_order(
|
|
db: Session,
|
|
*,
|
|
order_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
row = assert_presign_access(
|
|
db, order_id, ctx, user_info=user_info
|
|
)
|
|
references = _reference_data(db, [row], ctx.tenant_no)
|
|
return _mobile_order_to_dict(
|
|
db,
|
|
row,
|
|
ctx.tenant_no,
|
|
with_children=True,
|
|
reference_data=references,
|
|
)
|
|
|
|
|
|
def create_presign_order(
|
|
db: Session,
|
|
*,
|
|
body: MarketPresignOrderBody,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
fields = _dump_body(body)
|
|
fields["project_id"] = create_assignment(
|
|
db, ctx.tenant_no, user_info, body.project_id, "presign"
|
|
)
|
|
_reject_direct_conversion(fields)
|
|
sample_type_ids = fields.pop("sample_type_ids", None)
|
|
transfers = fields.pop("transfers", None)
|
|
if transfers and (
|
|
fields.get('order_status') or 'active'
|
|
).strip() not in ('', 'draft', 'active'):
|
|
raise ValueError('当前状态的预签约订单不可操作转账记录')
|
|
_validate_master_data(
|
|
db,
|
|
ctx,
|
|
region_id=fields.get("region_id"),
|
|
hospital_id=fields.get("hospital_id"),
|
|
sample_type_ids=sample_type_ids,
|
|
)
|
|
if ctx.scope_kind == "specialist":
|
|
fields["sales_user_id"] = ctx.user_id
|
|
else:
|
|
_ensure_manager_assignment(
|
|
db,
|
|
ctx,
|
|
region_id=fields.get("region_id"),
|
|
region_manager_user_id=fields.get("region_manager_user_id"),
|
|
)
|
|
presign_crud.validate_presign_person_ids(
|
|
db,
|
|
tenant_no=ctx.tenant_no,
|
|
collector_user_id=fields.get("collector_user_id") or ctx.user_id,
|
|
sales_user_id=fields.get("sales_user_id"),
|
|
region_manager_user_id=fields.get("region_manager_user_id"),
|
|
)
|
|
transfer_fields = [
|
|
item.model_dump(by_alias=False, exclude_unset=True)
|
|
if hasattr(item, "model_dump")
|
|
else item
|
|
for item in (transfers or [])
|
|
]
|
|
try:
|
|
row = presign_crud.create_presign_order(
|
|
db,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
fields=fields,
|
|
sample_type_ids=sample_type_ids,
|
|
transfers=transfer_fields,
|
|
create_dept_id=_current_dept_id(user_info),
|
|
commit=False,
|
|
)
|
|
created_transfers = (
|
|
db.query(BizPresignTransfer)
|
|
.filter(
|
|
BizPresignTransfer.presign_order_id == row.id,
|
|
BizPresignTransfer.tenant_no == ctx.tenant_no,
|
|
BizPresignTransfer.del_flag == 0,
|
|
)
|
|
.all()
|
|
)
|
|
for transfer in created_transfers:
|
|
if transfer.transfer_attachments:
|
|
transfer.transfer_attachments = validate_attachment_refs(
|
|
db,
|
|
transfer.transfer_attachments,
|
|
module='presign',
|
|
ctx=ctx,
|
|
creating=True,
|
|
business_id=row.id,
|
|
)
|
|
db.add(transfer)
|
|
db.flush()
|
|
row = assert_presign_access(
|
|
db, row.id, ctx, user_info=user_info
|
|
)
|
|
db.commit()
|
|
db.refresh(row)
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
return _mobile_order_to_dict(
|
|
db, row, ctx.tenant_no, with_children=True
|
|
)
|
|
|
|
|
|
def update_presign_order(
|
|
db: Session,
|
|
*,
|
|
order_id: str,
|
|
body: MarketPresignOrderBody,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
try:
|
|
current = _scoped_order_for_update(
|
|
db, order_id, ctx, user_info
|
|
)
|
|
if (current.order_status or '').strip() == 'converted':
|
|
raise ValueError('当前状态的预签约订单不可修改')
|
|
fields = _dump_body(body)
|
|
guard_project_edit(current, fields)
|
|
fields.pop("project_id", None)
|
|
_reject_direct_conversion(fields)
|
|
sample_type_ids = fields.pop("sample_type_ids", None)
|
|
fields.pop("transfers", None)
|
|
resulting_region_id = fields.get("region_id", current.region_id)
|
|
resulting_hospital_id = fields.get("hospital_id", current.hospital_id)
|
|
_validate_master_data(
|
|
db,
|
|
ctx,
|
|
region_id=resulting_region_id,
|
|
hospital_id=resulting_hospital_id,
|
|
sample_type_ids=sample_type_ids,
|
|
)
|
|
if ctx.scope_kind == "specialist":
|
|
fields["sales_user_id"] = ctx.user_id
|
|
elif str(resulting_region_id or '').strip() != str(current.region_id or '').strip():
|
|
_ensure_manager_assignment(
|
|
db,
|
|
ctx,
|
|
region_id=resulting_region_id,
|
|
region_manager_user_id=fields.get(
|
|
"region_manager_user_id", current.region_manager_user_id
|
|
),
|
|
)
|
|
presign_crud.validate_presign_person_ids(
|
|
db,
|
|
tenant_no=ctx.tenant_no,
|
|
collector_user_id=fields.get(
|
|
"collector_user_id", current.collector_user_id
|
|
),
|
|
sales_user_id=fields.get("sales_user_id", current.sales_user_id),
|
|
region_manager_user_id=fields.get(
|
|
"region_manager_user_id", current.region_manager_user_id
|
|
),
|
|
)
|
|
row = presign_crud.update_presign_order(
|
|
db,
|
|
order_id=order_id,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
fields=fields,
|
|
sample_type_ids=sample_type_ids,
|
|
create_dept_id=_current_dept_id(user_info),
|
|
commit=False,
|
|
)
|
|
row = assert_presign_access(
|
|
db, row.id, ctx, user_info=user_info
|
|
)
|
|
result = _mobile_order_to_dict(
|
|
db, row, ctx.tenant_no, with_children=True
|
|
)
|
|
db.commit()
|
|
return result
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def delete_presign_order(
|
|
db: Session,
|
|
order_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> None:
|
|
try:
|
|
row = _scoped_order_for_update(db, order_id, ctx, user_info)
|
|
if (row.order_status or '').strip() == 'converted':
|
|
raise ValueError('已转正式的预签约订单不可删除')
|
|
presign_crud.soft_delete_presign_order(
|
|
db,
|
|
order_id=order_id,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
)
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def _scoped_transfer(
|
|
db: Session,
|
|
transfer_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> BizPresignTransfer:
|
|
probe = (
|
|
db.query(BizPresignTransfer)
|
|
.filter(
|
|
BizPresignTransfer.id == transfer_id,
|
|
BizPresignTransfer.tenant_no == ctx.tenant_no,
|
|
BizPresignTransfer.del_flag == 0,
|
|
)
|
|
.first()
|
|
)
|
|
if probe is None:
|
|
raise HTTPException(status_code=404, detail='转账记录不存在或无权访问')
|
|
order = _scoped_order_for_update(
|
|
db, probe.presign_order_id, ctx, user_info
|
|
)
|
|
_assert_transfer_order_writable(order)
|
|
row = (
|
|
db.query(BizPresignTransfer)
|
|
.filter(
|
|
BizPresignTransfer.id == transfer_id,
|
|
BizPresignTransfer.tenant_no == ctx.tenant_no,
|
|
BizPresignTransfer.del_flag == 0,
|
|
)
|
|
.populate_existing()
|
|
.with_for_update()
|
|
.first()
|
|
)
|
|
if row is None:
|
|
raise HTTPException(status_code=404, detail='转账记录不存在或无权访问')
|
|
return row
|
|
|
|
|
|
def _update_transfer_if_status(
|
|
db: Session,
|
|
*,
|
|
transfer_id: str,
|
|
tenant_no: str,
|
|
expected_status: str,
|
|
values: dict,
|
|
extra_conditions: tuple = (),
|
|
) -> None:
|
|
updates = dict(values)
|
|
changed = (
|
|
db.query(BizPresignTransfer)
|
|
.filter(
|
|
BizPresignTransfer.id == transfer_id,
|
|
BizPresignTransfer.tenant_no == tenant_no,
|
|
BizPresignTransfer.del_flag == 0,
|
|
BizPresignTransfer.pay_status == expected_status,
|
|
*extra_conditions,
|
|
)
|
|
.update(updates, synchronize_session=False)
|
|
)
|
|
if changed != 1:
|
|
raise ValueError('转账状态已变化,请刷新后重试')
|
|
|
|
|
|
def create_presign_transfer(
|
|
db: Session,
|
|
order_id: str,
|
|
body: MarketPresignTransferBody,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
try:
|
|
order = _scoped_order_for_update(
|
|
db, order_id, ctx, user_info
|
|
)
|
|
_assert_transfer_order_writable(order)
|
|
fields = _dump_transfer_body(body)
|
|
if 'transfer_attachments' in fields:
|
|
fields['transfer_attachments'] = validate_attachment_refs(
|
|
db,
|
|
fields['transfer_attachments'],
|
|
module='presign',
|
|
ctx=ctx,
|
|
creating=True,
|
|
business_id=order.id,
|
|
)
|
|
row = presign_crud.add_transfer(
|
|
db,
|
|
presign_order_id=order.id,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
fields=fields,
|
|
create_dept_id=_current_dept_id(user_info),
|
|
commit=False,
|
|
)
|
|
result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
|
|
db.commit()
|
|
return result
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def update_presign_transfer(
|
|
db: Session,
|
|
transfer_id: str,
|
|
body: MarketPresignTransferBody,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
try:
|
|
current = _scoped_transfer(db, transfer_id, ctx, user_info)
|
|
if (current.pay_status or '').strip() != 'dfj':
|
|
raise ValueError('仅待缴费状态可编辑意向金转账笔')
|
|
fields = _dump_transfer_body(body)
|
|
if 'transfer_attachments' in fields:
|
|
fields['transfer_attachments'] = validate_attachment_refs(
|
|
db,
|
|
fields['transfer_attachments'],
|
|
module='presign',
|
|
ctx=ctx,
|
|
creating=False,
|
|
existing_refs=current.transfer_attachments,
|
|
business_id=current.presign_order_id,
|
|
)
|
|
row = presign_crud.update_transfer_draft(
|
|
db,
|
|
transfer_id=transfer_id,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
fields=fields,
|
|
commit=False,
|
|
)
|
|
result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
|
|
db.commit()
|
|
return result
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def delete_presign_transfer(
|
|
db: Session,
|
|
transfer_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> None:
|
|
try:
|
|
row = _scoped_transfer(db, transfer_id, ctx, user_info)
|
|
if (row.pay_status or '').strip() != 'dfj':
|
|
raise ValueError('仅待缴费状态可删除意向金转账笔')
|
|
_update_transfer_if_status(
|
|
db,
|
|
transfer_id=transfer_id,
|
|
tenant_no=ctx.tenant_no,
|
|
expected_status='dfj',
|
|
values={
|
|
BizPresignTransfer.del_flag: 1,
|
|
BizPresignTransfer.update_by: ctx.user_id,
|
|
BizPresignTransfer.update_time: datetime.now(),
|
|
},
|
|
)
|
|
db.commit()
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def submit_presign_transfer(
|
|
db: Session,
|
|
transfer_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
try:
|
|
_scoped_transfer(db, transfer_id, ctx, user_info)
|
|
row = presign_crud.submit_receipt_finance(
|
|
db,
|
|
transfer_id=transfer_id,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
commit=False,
|
|
)
|
|
result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
|
|
db.commit()
|
|
return result
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def withdraw_presign_transfer(
|
|
db: Session,
|
|
transfer_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
try:
|
|
_scoped_transfer(db, transfer_id, ctx, user_info)
|
|
row = presign_crud.withdraw_receipt(
|
|
db,
|
|
transfer_id=transfer_id,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
commit=False,
|
|
)
|
|
result = _mobile_transfer_to_dict(db, row, ctx.tenant_no)
|
|
db.commit()
|
|
return result
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def convert_presign_order(
|
|
db: Session,
|
|
*,
|
|
order_id: str,
|
|
ctx: MarketFieldContext,
|
|
user_info: dict,
|
|
) -> dict:
|
|
try:
|
|
row = _scoped_order_for_update(db, order_id, ctx, user_info)
|
|
if (row.order_status or '').strip() not in ('', 'draft', 'active'):
|
|
raise ValueError('当前状态的预签约订单不可转正式协议')
|
|
presign_crud.validate_presign_person_ids(
|
|
db,
|
|
tenant_no=ctx.tenant_no,
|
|
collector_user_id=row.collector_user_id or ctx.user_id,
|
|
sales_user_id=row.sales_user_id,
|
|
region_manager_user_id=row.region_manager_user_id,
|
|
)
|
|
result = presign_crud.convert_to_protocol(
|
|
db,
|
|
order_id=order_id,
|
|
tenant_no=ctx.tenant_no,
|
|
user_id=ctx.user_id,
|
|
create_dept_id=_current_dept_id(user_info),
|
|
)
|
|
if isinstance(result.get('presignOrder'), dict):
|
|
result = dict(result)
|
|
result['presignOrder'] = _mobile_order_to_dict(
|
|
db, row, ctx.tenant_no, with_children=True
|
|
)
|
|
return result
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|