121 lines
6.2 KiB
Python
121 lines
6.2 KiB
Python
"""合法使用证明独立资料维护;不改变运输、额度、归档或库存。"""
|
|
from fastapi import HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from app.models.pl_models import PlPlan, PlPlanProduct, PlPlanLoad, PlPlanLoadProduct, PlPlanDelivery, PlPlanDeliveryProduct
|
|
from app.schemas.pl.legal_use_document import LegalDocumentsBody
|
|
from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope
|
|
from app.services.pl_plan_cert_perm import user_can_upload_plan_cert
|
|
from app.services.pl_quota_attachments import authorize_attachment, storage_base_url
|
|
|
|
|
|
def _plan(db, user, plan_id, lock=False):
|
|
query = db.query(PlPlan).filter_by(id=plan_id, tenant_no=user.tenant_no, del_flag=0)
|
|
if lock:
|
|
query = query.with_for_update().populate_existing()
|
|
row = query.first()
|
|
if row is None:
|
|
raise HTTPException(404, '计划不存在')
|
|
assert_pl_row_in_scope(db, row, PlPlan, user, user.tenant_no)
|
|
return row
|
|
|
|
|
|
def _products(db, user, plan_id, lock=False):
|
|
query = db.query(PlPlanProduct).filter_by(plan_id=plan_id, tenant_no=user.tenant_no, del_flag=0)
|
|
if lock:
|
|
query = query.with_for_update().populate_existing()
|
|
return query.order_by(PlPlanProduct.create_time.desc(), PlPlanProduct.id).all()
|
|
|
|
|
|
def _contexts(db, user, plan_id):
|
|
from app.services.pl_quota_contract_service import _contract
|
|
result, contracts = {}, {}
|
|
for side, model, parent, parent_field in (
|
|
('load', PlPlanLoadProduct, PlPlanLoad, 'load_id'),
|
|
('delivery', PlPlanDeliveryProduct, PlPlanDelivery, 'delivery_id'),
|
|
):
|
|
rows = db.query(model, parent).join(parent, getattr(model, parent_field) == parent.id).filter(
|
|
model.tenant_no == user.tenant_no, parent.tenant_no == user.tenant_no,
|
|
model.plan_id == plan_id, parent.plan_id == plan_id, model.del_flag == 0, parent.del_flag == 0,
|
|
).order_by(model.create_time.desc(), model.id).all()
|
|
for item, stop in rows:
|
|
if item.contract_id and item.contract_id not in contracts:
|
|
try:
|
|
contracts[item.contract_id] = _contract(db, user, item.contract_id).contract_no
|
|
except HTTPException as error:
|
|
if error.status_code not in (403, 404):
|
|
raise
|
|
contracts[item.contract_id] = None
|
|
result.setdefault(item.plan_product_id, []).append({
|
|
'stopType': side, 'stopProductId': item.id, 'partyName': stop.customer_name,
|
|
'address': stop.address, 'contractId': item.contract_id,
|
|
'contractNo': contracts.get(item.contract_id), 'contractProductId': item.contract_product_id})
|
|
return result
|
|
|
|
|
|
def get_documents(db, user, plan_id):
|
|
plan = _plan(db, user, plan_id)
|
|
contexts = _contexts(db, user, plan_id)
|
|
return {'id': plan.id, 'planNo': plan.plan_no, 'archiveStatus': plan.archive_status,
|
|
'transportStatus': plan.transport_status, 'products': [
|
|
{'id': r.id, 'productId': r.product_id, 'productName': r.product_name,
|
|
'model': r.model, 'contexts': contexts.get(r.id, []), 'legalUseCertUrl': r.legal_use_cert_url,
|
|
'documentStatus': 'uploaded' if r.legal_use_cert_url else 'pending'}
|
|
for r in _products(db, user, plan_id) if r.is_explosive == 1]}
|
|
|
|
|
|
def list_documents(db, user, keyword=None, status=None):
|
|
if status not in (None, 'pending', 'uploaded'):
|
|
raise HTTPException(400, '未知资料状态')
|
|
products = db.query(PlPlanProduct.plan_id).filter_by(tenant_no=user.tenant_no, del_flag=0, is_explosive=1)
|
|
if status == 'pending':
|
|
products = products.filter((PlPlanProduct.legal_use_cert_url.is_(None)) | (PlPlanProduct.legal_use_cert_url == ''))
|
|
elif status == 'uploaded':
|
|
products = products.filter(PlPlanProduct.legal_use_cert_url.isnot(None), PlPlanProduct.legal_use_cert_url != '')
|
|
query = db.query(PlPlan).filter(PlPlan.tenant_no == user.tenant_no, PlPlan.del_flag == 0, PlPlan.id.in_(products))
|
|
query = apply_pl_scope(query, PlPlan, user, db, user.tenant_no)
|
|
if keyword:
|
|
query = query.filter(PlPlan.plan_no.contains(keyword))
|
|
return [get_documents(db, user, r.id) for r in query.order_by(PlPlan.create_time.desc()).all()]
|
|
|
|
|
|
def save_documents(db, user, plan_id, data):
|
|
if not user_can_upload_plan_cert(db, user):
|
|
raise HTTPException(403, '无权维护合法使用证明')
|
|
payload = data.model_dump(exclude_unset=True) if isinstance(data, BaseModel) else data
|
|
body = LegalDocumentsBody(**payload)
|
|
_plan(db, user, plan_id, lock=True)
|
|
if 'products' not in body.model_fields_set:
|
|
return get_documents(db, user, plan_id)
|
|
if body.products is None:
|
|
raise HTTPException(400, '产品集合不可为null')
|
|
rows = {r.id: r for r in _products(db, user, plan_id, lock=True)}
|
|
seen, changes = set(), []
|
|
for item in body.products:
|
|
if item.id in seen or item.id not in rows:
|
|
raise HTTPException(400, '产品明细ID重复或不属于当前计划')
|
|
seen.add(item.id)
|
|
row = rows[item.id]
|
|
if row.is_explosive != 1:
|
|
raise HTTPException(400, '该产品不适用合法使用证明')
|
|
if 'legal_use_cert_url' not in item.model_fields_set:
|
|
continue
|
|
urls = list(dict.fromkeys(v.strip() for v in (item.legal_use_cert_url or '').split(',') if v.strip()))
|
|
old = {v.strip() for v in (row.legal_use_cert_url or '').split(',') if v.strip()}
|
|
for value in urls:
|
|
if value not in old:
|
|
authorize_attachment(db, user, value, storage_base_url())
|
|
normalized = ','.join(urls)
|
|
if normalized != (row.legal_use_cert_url or ''):
|
|
changes.append((row, normalized))
|
|
for row, value in changes:
|
|
row.legal_use_cert_url = value
|
|
row.update_by = user.id
|
|
if changes:
|
|
from app.services.pl_plan_service import record_plan_change
|
|
record_plan_change(db, tenant_no=user.tenant_no, plan_id=plan_id, change_type='legal_use_document',
|
|
change_content='维护合法使用证明:' + '、'.join(r.id for r, _ in changes),
|
|
user_id=user.id, user_name=getattr(user, 'name', None), dept_id=user.dept_id or '')
|
|
db.flush()
|
|
return get_documents(db, user, plan_id)
|