| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- """地磅 ORM → API Read 模型映射。"""
- from __future__ import annotations
- import json
- from datetime import date, datetime, time
- from typing import Iterable, Optional
- from sqlalchemy.orm import Session
- from app.core.utils import format_datetime
- from app.models.models import SysUser
- # 三方预生成出入库单「日期未填」占位(原系统 date="",DDL 为 NOT NULL DATE)
- EMPTY_INVENTORY_DATE = date(1970, 1, 1)
- from app.models.db_models import DbInboundOutbound, DbPurchasePlan, DbWeighingSlip
- from app.schemas.db.inventory import InboundOutboundRead
- from app.schemas.db.plans import PurchasePlanRead, PurchasePlanWithFillLink
- from app.schemas.db.weighing import WeighingSlipRead
- def fmt_date(value: date | datetime | str | None) -> str | None:
- if value is None:
- return None
- if isinstance(value, datetime):
- return value.strftime("%Y-%m-%d")
- if isinstance(value, date):
- return value.strftime("%Y-%m-%d")
- return str(value)
- def fmt_datetime(value: datetime | str | None) -> str | None:
- if value is None:
- return None
- if isinstance(value, datetime):
- return format_datetime(value)
- return str(value)
- def load_user_display_name_map(
- db: Session, user_ids: Iterable[Optional[str]]
- ) -> dict[str, str]:
- """批量解析用户展示名(昵称优先,否则账号)。"""
- ids = {(uid or "").strip() for uid in user_ids if (uid or "").strip()}
- if not ids:
- return {}
- rows = (
- db.query(SysUser.id, SysUser.nick_name, SysUser.user_no)
- .filter(SysUser.id.in_(ids), SysUser.del_flag == 0)
- .all()
- )
- return {u.id: (u.nick_name or u.user_no or "").strip() for u in rows}
- def _create_by_name(
- create_by: Optional[str], user_name_map: dict[str, str] | None
- ) -> Optional[str]:
- uid = (create_by or "").strip()
- if not uid or user_name_map is None:
- return None
- name = (user_name_map.get(uid) or "").strip()
- return name or None
- def plan_to_read(
- row: DbPurchasePlan,
- *,
- executed_quantity: float | None = None,
- user_name_map: dict[str, str] | None = None,
- db: Session | None = None,
- ) -> PurchasePlanRead:
- name_map = user_name_map
- if name_map is None and db is not None:
- name_map = load_user_display_name_map(db, [row.create_by])
- return PurchasePlanRead(
- id=row.id,
- planNo=row.plan_no,
- planDate=fmt_date(row.plan_date) or "",
- startDate=fmt_date(row.start_date),
- endDate=fmt_date(row.end_date),
- businessType=row.business_type,
- productId=row.product_id,
- product=row.product,
- specification=row.specification,
- plannedQuantity=row.planned_quantity,
- vehicleNo=row.vehicle_no or "",
- customerName=row.customer_name,
- remarks=row.remark,
- executionStatus=row.execution_status,
- executedAt=fmt_datetime(row.executed_at),
- lastWeighingSlipId=row.last_weighing_slip_id,
- executedQuantity=(
- executed_quantity if executed_quantity is not None else (row.executed_quantity or 0)
- ),
- autoGenerateEnabled=row.auto_generate_enabled or 0,
- autoGenerateEndMonth=row.auto_generate_end_month,
- autoGenerateLastMonth=row.auto_generate_last_month,
- isDeleted=row.del_flag,
- createdAt=format_datetime(row.create_time) or "",
- createBy=row.create_by,
- createByName=_create_by_name(row.create_by, name_map),
- )
- def plan_to_read_with_fill_link(
- row: DbPurchasePlan,
- *,
- executed_quantity: float | None = None,
- fill_link_inventory_id: str | None = None,
- user_name_map: dict[str, str] | None = None,
- db: Session | None = None,
- ) -> PurchasePlanWithFillLink:
- base = plan_to_read(
- row, executed_quantity=executed_quantity, user_name_map=user_name_map, db=db
- )
- return PurchasePlanWithFillLink(
- **base.model_dump(by_alias=False),
- fill_link_inventory_id=fill_link_inventory_id,
- )
- def is_empty_inventory_date(value: date | None) -> bool:
- return value is None or value == EMPTY_INVENTORY_DATE
- def inventory_date_to_api(value: date | None) -> str:
- if is_empty_inventory_date(value):
- return ""
- return fmt_date(value) or ""
- def _parse_site_feedback_images(raw: str | None) -> list[str] | None:
- if not raw:
- return None
- try:
- parsed = json.loads(raw)
- if isinstance(parsed, list):
- items = [str(x) for x in parsed if x]
- return items or None
- except (TypeError, json.JSONDecodeError):
- pass
- items = [part.strip() for part in raw.split(",") if part.strip()]
- return items or None
- def inventory_to_read(
- row: DbInboundOutbound,
- *,
- weighing: DbWeighingSlip | None = None,
- user_name_map: dict[str, str] | None = None,
- db: Session | None = None,
- ) -> InboundOutboundRead:
- gross_weight = None
- tare_weight = None
- deduction = None
- net_weight = None
- gross_weight_time = None
- tare_weight_time = None
- has_weighing_slip = False
- if weighing:
- gross_weight = weighing.gross_weight
- tare_weight = weighing.tare_weight
- deduction = weighing.deduction
- net_weight = weighing.net_weight
- gross_weight_time = fmt_datetime(weighing.gross_weight_time)
- tare_time = fmt_datetime(weighing.tare_weight_time)
- tare_weight_time = tare_time if tare_time and tare_time.strip() else None
- has_weighing_slip = True
- name_map = user_name_map
- if name_map is None and db is not None:
- name_map = load_user_display_name_map(db, [row.create_by])
- return InboundOutboundRead(
- id=row.id,
- date=inventory_date_to_api(row.date),
- partnerCompany=row.partner_company,
- businessType=row.business_type,
- productId=row.product_id,
- product=row.product,
- specification=row.specification,
- quantity=row.quantity,
- planNo=row.plan_no,
- batchNo=row.batch_no,
- vehicleNo=row.vehicle_no,
- status=row.status,
- remarks=row.remark,
- purchasePlanId=row.purchase_plan_id,
- isDeleted=row.del_flag,
- createdAt=format_datetime(row.create_time) or "",
- createBy=row.create_by,
- createByName=_create_by_name(row.create_by, name_map),
- grossWeight=gross_weight,
- tareWeight=tare_weight,
- deduction=deduction,
- netWeight=net_weight,
- grossWeightTime=gross_weight_time,
- tareWeightTime=tare_weight_time,
- hasWeighingSlip=has_weighing_slip,
- siteFeedbackRemark=row.site_feedback_remark,
- siteFeedbackImages=_parse_site_feedback_images(row.site_feedback_images),
- siteFeedbackTime=format_datetime(row.site_feedback_time) or None,
- )
- def weighing_to_read(
- row: DbWeighingSlip,
- *,
- inventory_date: str | None = None,
- user_name_map: dict[str, str] | None = None,
- db: Session | None = None,
- ) -> WeighingSlipRead:
- name_map = user_name_map
- if name_map is None and db is not None:
- name_map = load_user_display_name_map(db, [row.create_by])
- return WeighingSlipRead(
- id=row.id,
- vehicleNo=row.vehicle_no,
- planNo=row.plan_no,
- productId=row.product_id,
- cargoName=row.cargo_name,
- specification=row.specification,
- businessType=row.business_type,
- batchNo=row.batch_no,
- supplier=row.supplier,
- consignee=row.consignee,
- grossWeight=row.gross_weight,
- grossWeightTime=fmt_datetime(row.gross_weight_time) or "",
- tareWeight=row.tare_weight,
- tareWeightTime=fmt_datetime(row.tare_weight_time) or "",
- deduction=row.deduction,
- netWeight=row.net_weight,
- plannedQuantity=row.planned_quantity,
- executedQuantity=row.executed_quantity,
- purchasePlanId=row.purchase_plan_id,
- inboundOutboundId=row.inbound_outbound_id,
- weighingPoint=row.weighing_point,
- remarks=row.remark,
- currentQuantity=row.current_quantity,
- isVoided=row.is_voided,
- createdAt=format_datetime(row.create_time) or "",
- createBy=row.create_by,
- createByName=_create_by_name(row.create_by, name_map),
- inventoryDate=inventory_date,
- )
- def datetime_end_of_day(value: date) -> datetime:
- return datetime.combine(value, time(23, 59, 59))
- def parse_date_str(value: str | None) -> date | None:
- if not value:
- return None
- try:
- return datetime.strptime(value[:10], "%Y-%m-%d").date()
- except ValueError:
- return None
- def parse_datetime_str(value: str | None) -> datetime | None:
- if not value:
- return None
- for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d"):
- try:
- return datetime.strptime(value[:19] if fmt != "%Y-%m-%d" else value[:10], fmt)
- except ValueError:
- continue
- return None
|