提交
This commit is contained in:
1 parent
ebf006eeff
commit
16986f443e
30 files changed
+2319
No files matched your search
@@ -0,0 +1,313 @@
|
||||
"""运输计划归档导出:按装货/送货段 × 段内产品展开,与列表展示规则一致。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from typing import Optional, Union
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.pl_models import PlPlan, PlPlanProduct, PlTransport
|
||||
from app.schemas.pl.plan import PlanDeliveryRead, PlanLoadRead, PlanStopProductRead
|
||||
from app.services.pl_internal_customer import is_internal_customer_name
|
||||
from app.services.pl_plan_stop_service import load_plan_stops_grouped
|
||||
|
||||
StopKind = str # "load" | "delivery"
|
||||
|
||||
PLAN_TYPE_LABELS = {
|
||||
"direct": "直销单",
|
||||
"inbound": "入库单",
|
||||
"outbound": "出库单",
|
||||
}
|
||||
|
||||
ARCHIVE_STATUS_LABELS = {
|
||||
"archived": "已归档",
|
||||
"unarchived": "未归档",
|
||||
}
|
||||
|
||||
FOLLOW_STATUS_LABELS = {
|
||||
"wait_notice": "等通知",
|
||||
"pending_arrange": "待安排",
|
||||
"arranged": "已安排",
|
||||
"completed": "已完成",
|
||||
"cancelled": "已取消",
|
||||
"draft": "待安排",
|
||||
"processing": "进行中",
|
||||
}
|
||||
|
||||
FREIGHT_TYPE_LABELS = {
|
||||
"by_weight": "按重量",
|
||||
"by_trip": "按趟",
|
||||
}
|
||||
|
||||
|
||||
def resolve_archive_list_io_column(kind: StopKind, customer_name: Optional[str]) -> str:
|
||||
"""装货/送货段入库出库列方向(与前端 resolveArchiveListIoColumn 一致)。"""
|
||||
internal = is_internal_customer_name(customer_name)
|
||||
if kind == "load":
|
||||
return "outbound" if internal else "inbound"
|
||||
return "inbound" if internal else "outbound"
|
||||
|
||||
|
||||
def _format_date_value(value: Optional[Union[date, datetime, str]]) -> str:
|
||||
if value is None:
|
||||
return ""
|
||||
return str(value)[:10]
|
||||
|
||||
|
||||
def _format_plate_display(license_plate: Optional[str], trailer_no: Optional[str]) -> str:
|
||||
plate = (license_plate or "").strip()
|
||||
trailer = (trailer_no or "").strip()
|
||||
if not plate and not trailer:
|
||||
return ""
|
||||
if plate and trailer:
|
||||
return f"{plate}{trailer}"
|
||||
return plate or trailer
|
||||
|
||||
|
||||
def _get_stop_archive_actual_qty(product: PlanStopProductRead, kind: StopKind) -> Optional[Decimal]:
|
||||
return product.inbound_qty if kind == "load" else product.outbound_qty
|
||||
|
||||
|
||||
def _get_stop_archive_unit_price(product: PlanStopProductRead, kind: StopKind) -> Optional[Decimal]:
|
||||
return product.inbound_price if kind == "load" else product.outbound_price
|
||||
|
||||
|
||||
def _get_stop_archive_amount(product: PlanStopProductRead, kind: StopKind) -> Optional[Decimal]:
|
||||
stored = product.inbound_amount if kind == "load" else product.outbound_amount
|
||||
if stored is not None:
|
||||
return stored
|
||||
qty = _get_stop_archive_actual_qty(product, kind)
|
||||
price = _get_stop_archive_unit_price(product, kind)
|
||||
if qty is not None and price is not None:
|
||||
return qty * price
|
||||
return None
|
||||
|
||||
|
||||
def _stop_kind_label(kind: StopKind, delivery_seq: Optional[int] = None) -> str:
|
||||
if kind == "load":
|
||||
return "装货"
|
||||
return f"送货 #{delivery_seq or 1}"
|
||||
|
||||
|
||||
def _stop_time(kind: StopKind, stop: Union[PlanLoadRead, PlanDeliveryRead]) -> str:
|
||||
if kind == "load":
|
||||
return _format_date_value(getattr(stop, "load_time", None))
|
||||
return _format_date_value(getattr(stop, "delivery_time", None))
|
||||
|
||||
|
||||
def _load_legacy_products_by_plan(
|
||||
db: Session, tenant_no: str, plan_ids: list[str]
|
||||
) -> dict[str, list[PlPlanProduct]]:
|
||||
if not plan_ids:
|
||||
return {}
|
||||
products = (
|
||||
db.query(PlPlanProduct)
|
||||
.filter(
|
||||
PlPlanProduct.tenant_no == tenant_no,
|
||||
PlPlanProduct.del_flag == 0,
|
||||
PlPlanProduct.plan_id.in_(plan_ids),
|
||||
)
|
||||
.order_by(PlPlanProduct.order_num)
|
||||
.all()
|
||||
)
|
||||
grouped: dict[str, list[PlPlanProduct]] = {}
|
||||
for product in products:
|
||||
grouped.setdefault(product.plan_id, []).append(product)
|
||||
return grouped
|
||||
|
||||
|
||||
def _plan_common_export_fields(
|
||||
plan: PlPlan,
|
||||
transport: Optional[PlTransport],
|
||||
) -> dict:
|
||||
freight_type_raw = transport.freight_type if transport else "by_weight"
|
||||
freight_type_label = FREIGHT_TYPE_LABELS.get(freight_type_raw, freight_type_raw or "")
|
||||
freight_price = transport.freight_price if transport else None
|
||||
freight_amount = plan.freight_amount
|
||||
if freight_amount is None and transport is not None:
|
||||
freight_amount = transport.freight_amount
|
||||
plan_date = _format_date_value(plan.plan_date)
|
||||
return {
|
||||
"planNo": plan.plan_no or "",
|
||||
"planDate": plan_date,
|
||||
"archiveStatus": ARCHIVE_STATUS_LABELS.get(plan.archive_status or "", plan.archive_status or ""),
|
||||
"followStatus": FOLLOW_STATUS_LABELS.get(plan.follow_status or "", plan.follow_status or ""),
|
||||
"planType": PLAN_TYPE_LABELS.get(plan.plan_type or "", plan.plan_type or ""),
|
||||
"licensePlate": _format_plate_display(plan.license_plate, plan.trailer_no),
|
||||
"erpEntered": "是" if int(plan.erp_entered or 0) == 1 else "否",
|
||||
"freightType": freight_type_label,
|
||||
"freightPrice": freight_price,
|
||||
"freightAmount": freight_amount,
|
||||
"remark": plan.remark or "",
|
||||
}
|
||||
|
||||
|
||||
def _empty_product_fields() -> dict:
|
||||
return {
|
||||
"productName": "",
|
||||
"model": "",
|
||||
"packageSpec": "",
|
||||
"unit": "",
|
||||
"planQty": None,
|
||||
"actualQty": None,
|
||||
"inboundQty": None,
|
||||
"outboundQty": None,
|
||||
"unitPrice": None,
|
||||
"amount": None,
|
||||
}
|
||||
|
||||
|
||||
def _product_row_from_stop_product(
|
||||
product: PlanStopProductRead,
|
||||
kind: StopKind,
|
||||
customer_name: Optional[str],
|
||||
) -> dict:
|
||||
actual_qty = _get_stop_archive_actual_qty(product, kind)
|
||||
unit_price = _get_stop_archive_unit_price(product, kind)
|
||||
amount = _get_stop_archive_amount(product, kind)
|
||||
io_col = resolve_archive_list_io_column(kind, customer_name)
|
||||
inbound_qty = actual_qty if io_col == "inbound" else None
|
||||
outbound_qty = actual_qty if io_col == "outbound" else None
|
||||
return {
|
||||
"productName": product.product_name or "",
|
||||
"model": product.model or "",
|
||||
"packageSpec": product.package_spec or "",
|
||||
"unit": product.unit or "",
|
||||
"planQty": product.plan_qty,
|
||||
"actualQty": actual_qty,
|
||||
"inboundQty": inbound_qty,
|
||||
"outboundQty": outbound_qty,
|
||||
"unitPrice": unit_price,
|
||||
"amount": amount,
|
||||
}
|
||||
|
||||
|
||||
def _append_stop_product_rows(
|
||||
rows: list[dict],
|
||||
row_no: int,
|
||||
plan_common: dict,
|
||||
kind: StopKind,
|
||||
stop: Union[PlanLoadRead, PlanDeliveryRead],
|
||||
delivery_seq: Optional[int],
|
||||
products: list[PlanStopProductRead],
|
||||
) -> int:
|
||||
customer_name = stop.customer_name or ""
|
||||
stop_common = {
|
||||
**plan_common,
|
||||
"stopKind": _stop_kind_label(kind, delivery_seq),
|
||||
"stopCustomer": customer_name,
|
||||
"stopAddress": stop.address or "",
|
||||
"stopTime": _stop_time(kind, stop),
|
||||
}
|
||||
product_rows = products or [None]
|
||||
for product in product_rows:
|
||||
row_no += 1
|
||||
row = {
|
||||
**stop_common,
|
||||
"rowNo": row_no,
|
||||
**_empty_product_fields(),
|
||||
}
|
||||
if product is not None:
|
||||
row.update(_product_row_from_stop_product(product, kind, customer_name))
|
||||
rows.append(row)
|
||||
return row_no
|
||||
|
||||
|
||||
def _append_legacy_plan_product_rows(
|
||||
rows: list[dict],
|
||||
row_no: int,
|
||||
plan: PlPlan,
|
||||
plan_common: dict,
|
||||
products: list[PlPlanProduct],
|
||||
) -> int:
|
||||
"""无段表数据时回退计划产品级导出(兼容旧数据)。"""
|
||||
stop_common = {
|
||||
**plan_common,
|
||||
"stopKind": "",
|
||||
"stopCustomer": plan.customer_name or plan.supplier_name or "",
|
||||
"stopAddress": plan.destination_name or "",
|
||||
"stopTime": _format_date_value(plan.delivery_time),
|
||||
}
|
||||
product_rows = products or [None]
|
||||
for product in product_rows:
|
||||
row_no += 1
|
||||
row = {
|
||||
**stop_common,
|
||||
"rowNo": row_no,
|
||||
**_empty_product_fields(),
|
||||
}
|
||||
if product is not None:
|
||||
row.update(
|
||||
{
|
||||
"productName": product.product_name or "",
|
||||
"model": product.model or "",
|
||||
"packageSpec": product.package_spec or "",
|
||||
"unit": product.unit or "",
|
||||
"planQty": product.quantity,
|
||||
"actualQty": product.actual_qty,
|
||||
"inboundQty": product.inbound_qty,
|
||||
"outboundQty": product.outbound_qty,
|
||||
"unitPrice": product.inbound_price or product.outbound_price,
|
||||
"amount": product.inbound_amount or product.outbound_amount,
|
||||
}
|
||||
)
|
||||
rows.append(row)
|
||||
return row_no
|
||||
|
||||
|
||||
def build_plan_archive_export_rows(
|
||||
db: Session,
|
||||
tenant_no: str,
|
||||
plans: list[PlPlan],
|
||||
transports_by_id: dict[str, PlTransport],
|
||||
) -> list[dict]:
|
||||
"""构建归档导出数据行:每计划 × 每段次 × 每段产品一行。"""
|
||||
if not plans:
|
||||
return []
|
||||
|
||||
plan_ids = [plan.id for plan in plans]
|
||||
stops_by_plan = load_plan_stops_grouped(db, tenant_no, plan_ids)
|
||||
legacy_products_by_plan = _load_legacy_products_by_plan(db, tenant_no, plan_ids)
|
||||
|
||||
rows: list[dict] = []
|
||||
row_no = 0
|
||||
for plan in plans:
|
||||
transport = transports_by_id.get(plan.transport_id) if plan.transport_id else None
|
||||
plan_common = _plan_common_export_fields(plan, transport)
|
||||
load_read, delivery_reads = stops_by_plan.get(plan.id, (None, []))
|
||||
|
||||
has_stops = load_read is not None or bool(delivery_reads)
|
||||
if not has_stops:
|
||||
row_no = _append_legacy_plan_product_rows(
|
||||
rows,
|
||||
row_no,
|
||||
plan,
|
||||
plan_common,
|
||||
legacy_products_by_plan.get(plan.id, []),
|
||||
)
|
||||
continue
|
||||
|
||||
if load_read is not None:
|
||||
row_no = _append_stop_product_rows(
|
||||
rows,
|
||||
row_no,
|
||||
plan_common,
|
||||
"load",
|
||||
load_read,
|
||||
None,
|
||||
load_read.products or [],
|
||||
)
|
||||
for idx, delivery_read in enumerate(delivery_reads):
|
||||
delivery_seq = delivery_read.seq or (idx + 1)
|
||||
row_no = _append_stop_product_rows(
|
||||
rows,
|
||||
row_no,
|
||||
plan_common,
|
||||
"delivery",
|
||||
delivery_read,
|
||||
delivery_seq,
|
||||
delivery_read.products or [],
|
||||
)
|
||||
|
||||
return rows
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_py", "label": "plan.py", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_resolve_list_product_name", "label": "_resolve_list_product_name()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L145"}, {"id": "plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_resolve_create_date", "label": "_resolve_create_date()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L161"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_generate_plan_no", "label": "_generate_plan_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L165"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_format_subpack_slip_no", "label": "_format_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L197"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_normalize_subpack_slip_no", "label": "_normalize_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L201"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_subpack_slip_no_seq", "label": "_subpack_slip_no_seq()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L226"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_ensure_subpack_slip_no", "label": "_ensure_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L234"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_determine_plan_type", "label": "_determine_plan_type()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L266"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_get_plan_type_label", "label": "_get_plan_type_label()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L271"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_generate_notify_message", "label": "_generate_notify_message()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L280"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_plan_product_name_exists", "label": "_plan_product_name_exists()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L300"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_plan_plate_match", "label": "_plan_plate_match()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L314"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_list_plans", "label": "list_plans()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L324"}, {"id": "dbusercontext", "label": "DbUserContext", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "planlistresponse", "label": "PlanListResponse", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_load_transports_by_id", "label": "_load_transports_by_id()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L577"}, {"id": "pltransport", "label": "PlTransport", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_planexportbody", "label": "PlanExportBody", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L592"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_export_plans", "label": "export_plans()", "file_type": "code", "source_file": "aLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_py", "label": "pl_plan_contract_gap.py", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_stop_contract_gap", "label": "stop_contract_gap()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L45"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_side_is_exempt", "label": "side_is_exempt()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L50"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_compute_contract_gap_sides", "label": "compute_contract_gap_sides()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L54"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_compute_plan_gap_sides_from_stops", "label": "compute_plan_gap_sides_from_stops()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L69"}, {"id": "planloadread", "label": "PlanLoadRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "plandeliveryread", "label": "PlanDeliveryRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_stop_contract_exists", "label": "_stop_contract_exists()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L103"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_load_stop_gap_expr", "label": "_load_stop_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L120"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_delivery_stop_gap_expr", "label": "_delivery_stop_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L143"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_legacy_supplier_gap_expr", "label": "_legacy_supplier_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L166"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_legacy_demander_gap_expr", "label": "_legacy_demander_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L192"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_plan_has_contract_gap_exists", "label": "plan_has_contract_gap_exists()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L218"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_supplier_gap_expr", "label": "_supplier_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L228"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_demander_gap_expr", "label": "_demander_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L232"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_build_plan_contract_gap_read", "label": "build_plan_contract_gap_read()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L236"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "plancontractlink", "label": "PlanContractLink", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "planproductread", "label": "PlanProductRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "plancontractgapread", "label": "PlanContractGapRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_load_products_by_plan_ids", "label": "_load_productsLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_py", "label": "plan.py", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_resolve_list_product_name", "label": "_resolve_list_product_name()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L150"}, {"id": "plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_resolve_create_date", "label": "_resolve_create_date()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L166"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_generate_plan_no", "label": "_generate_plan_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L170"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_format_subpack_slip_no", "label": "_format_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L202"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_normalize_subpack_slip_no", "label": "_normalize_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L206"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_subpack_slip_no_seq", "label": "_subpack_slip_no_seq()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L231"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_ensure_subpack_slip_no", "label": "_ensure_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L239"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_determine_plan_type", "label": "_determine_plan_type()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L271"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_get_plan_type_label", "label": "_get_plan_type_label()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L276"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_generate_notify_message", "label": "_generate_notify_message()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L285"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_plan_product_name_exists", "label": "_plan_product_name_exists()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L305"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_plan_plate_match", "label": "_plan_plate_match()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L319"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_list_plans", "label": "list_plans()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L329"}, {"id": "dbusercontext", "label": "DbUserContext", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "planlistresponse", "label": "PlanListResponse", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_load_transports_by_id", "label": "_load_transports_by_id()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L582"}, {"id": "pltransport", "label": "PlTransport", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_planexportbody", "label": "PlanExportBody", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L597"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_export_plans", "label": "export_plans()", "file_type": "code", "source_file": "aLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_py", "label": "pl_plan_archive_stop_service.py", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_require_qty", "label": "_require_qty()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L28"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_apply_stop_product_archive", "label": "_apply_stop_product_archive()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L37"}, {"id": "plplanloadproduct", "label": "PlPlanLoadProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "plplandeliveryproduct", "label": "PlPlanDeliveryProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "planarchivestopproductitem", "label": "PlanArchiveStopProductItem", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_brand_rows_from_item", "label": "_brand_rows_from_item()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L64"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_persist_stop_brand_qtys", "label": "_persist_stop_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L80"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_apply_archive_stop_quantities", "label": "apply_archive_stop_quantities()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L114"}, {"id": "planarchiveloadblock", "label": "PlanArchiveLoadBlock", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "planarchivedeliveryblock", "label": "PlanArchiveDeliveryBlock", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_sync_plan_products_from_stop_archive", "label": "sync_plan_products_from_stop_archive()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L216"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_sum_delivery_outbound_qty", "label": "sum_delivery_outbound_qty()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L295"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_iter_stop_archive_inventory_lines", "label": "iter_stop_archive_inventory_lines()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L311"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_rationale_223", "label": "\u5c06\u6bb5\u7ea7\u5f52\u6863\u6570\u91cf\u6c47\u603b\u56de\u5199 pl_plan_product\uff08_row_to_dict \u6743\u5a01\u6765\u6e90\uff09\u3002", "file_type": "rationale", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L223"}], "edges": [{"source": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_py", "target": "collections", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_py", "label": "plan.py", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductbase", "label": "PlanProductBase", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L16"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductcreate", "label": "PlanProductCreate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L55"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductupdate", "label": "PlanProductUpdate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L59"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planarchivebrandqtyitem", "label": "PlanArchiveBrandQtyItem", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L81"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductread", "label": "PlanProductRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L89"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planstopproductbody", "label": "PlanStopProductBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L106"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planstopproductread", "label": "PlanStopProductRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L124"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planloadbody", "label": "PlanLoadBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L143"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandeliverybody", "label": "PlanDeliveryBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L162"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planloadread", "label": "PlanLoadRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L182"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandeliveryread", "label": "PlanDeliveryRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L193"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planbase", "label": "PlanBase", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L209"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plancontractlink", "label": "PlanContractLink", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L260"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plancreate", "label": "PlanCreate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L267"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plantransportcreateblock", "label": "PlanTransportCreateBlock", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L284"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planupdate", "label": "PlanUpdate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L298"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_hazardcertreadybody", "label": "HazardCertReadyBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L350"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_erpenteredbody", "label": "ErpEnteredBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L355"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandocumentattachmentitem", "label": "PlanDocumentAttachmentItem", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L360"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planread", "label": "PlanRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L366"}, {"id": "planresourcesnapshotmixin", "label": "PlanResourceSnapshotMixin", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planwithproducts", "label": "PlanWithProducts", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L401"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_deliveryprintcustomeroption", "label": "DeliLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_py", "label": "pl_brand_archive.py", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_is_plan_level_row", "label": "_is_plan_level_row()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L12"}, {"id": "plplanproductbrandqty", "label": "PlPlanProductBrandQty", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_load_brand_qtys_by_plan_product_ids", "label": "load_brand_qtys_by_plan_product_ids()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L16"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_load_brand_qtys_by_stop_product_ids", "label": "load_brand_qtys_by_stop_product_ids()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L55"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_validate_brand_qty_rows", "label": "validate_brand_qty_rows()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L93"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_persist_plan_product_brand_qtys", "label": "persist_plan_product_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L111"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_persist_stop_product_brand_qtys", "label": "persist_stop_product_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L166"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_22", "label": "\u6309\u8ba1\u5212\u4ea7\u54c1\u660e\u7ec6 ID \u6279\u91cf\u52a0\u8f7d\u8ba1\u5212\u7ea7\u5f52\u6863\u54c1\u724c\u6570\u91cf\uff08brandId/brandName/qty\uff09\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L22"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_61", "label": "\u6309\u88c5\u8d27/\u9001\u8d27\u6bb5\u4ea7\u54c1\u884c ID \u6279\u91cf\u52a0\u8f7d\u6bb5\u7ea7\u5f52\u6863\u54c1\u724c\u6570\u91cf\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L61"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_122", "label": "\u8f6f\u5220\u8ba1\u5212\u7ea7\u65e7\u54c1\u724c\u91cf\u884c\u540e\u6309\u5f52\u6863\u7ed3\u679c\u63d2\u5165\uff1b\u65e0\u54c1\u724c\u7ea6\u5b9a\u5219\u53ea\u8f6f\u5220\u4e0d\u63d2\u5165\u3002\u4e0d\u89e6\u78b0\u6bb5\u7ea7\u884c\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L122"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_179", "label": "\u8f6f\u5220\u8be5\u6bb5\u4ea7\u54c1\u884c\u65e7\u54c1\u724c\u91cf\u540e\u5199\u5165\uff1b\u65e0\u6709\u6548\u6570\u91cf\u5219\u53ea\u8f6f\u5220\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L179"}], "edges": [{"source": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_py", "target": "decimal", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "app/services/pl_brand_archive.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_py", "target": "typing", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "app/services/pl_brand_archive.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_py", "target": "sqlalchemy", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "app/services/pl_brand_archive.py", "source_location": "L5", "Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_py", "label": "plan.py", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_resolve_list_product_name", "label": "_resolve_list_product_name()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L144"}, {"id": "plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_resolve_create_date", "label": "_resolve_create_date()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L160"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_generate_plan_no", "label": "_generate_plan_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L164"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_format_subpack_slip_no", "label": "_format_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L196"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_normalize_subpack_slip_no", "label": "_normalize_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L200"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_subpack_slip_no_seq", "label": "_subpack_slip_no_seq()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L225"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_ensure_subpack_slip_no", "label": "_ensure_subpack_slip_no()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L233"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_determine_plan_type", "label": "_determine_plan_type()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L265"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_get_plan_type_label", "label": "_get_plan_type_label()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L270"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_generate_notify_message", "label": "_generate_notify_message()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L279"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_plan_product_name_exists", "label": "_plan_product_name_exists()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L299"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_plan_plate_match", "label": "_plan_plate_match()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L313"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_list_plans", "label": "list_plans()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L323"}, {"id": "dbusercontext", "label": "DbUserContext", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "planlistresponse", "label": "PlanListResponse", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_format_plan_delivery_date_for_export", "label": "_format_plan_delivery_date_for_export()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L574"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_load_transports_by_id", "label": "_load_transports_by_id()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_location": "L580"}, {"id": "pltransport", "label": "PlTransport", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/api/pl/plan.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_api_pl_plan_load_products_by_plan_id", "label": "_load_products_by_plan_id()", "file_type": "code", "source_file": "app/api/pl/plan.py", "source_locatiLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_py", "label": "plan.py", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductbase", "label": "PlanProductBase", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L16"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductcreate", "label": "PlanProductCreate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L55"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductupdate", "label": "PlanProductUpdate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L59"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planarchivebrandqtyitem", "label": "PlanArchiveBrandQtyItem", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L81"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductread", "label": "PlanProductRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L89"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planstopproductbody", "label": "PlanStopProductBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L106"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planstopproductread", "label": "PlanStopProductRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L124"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planloadbody", "label": "PlanLoadBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L143"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandeliverybody", "label": "PlanDeliveryBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L162"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planloadread", "label": "PlanLoadRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L182"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandeliveryread", "label": "PlanDeliveryRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L193"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planbase", "label": "PlanBase", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L209"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plancontractlink", "label": "PlanContractLink", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L260"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plancreate", "label": "PlanCreate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L267"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plantransportcreateblock", "label": "PlanTransportCreateBlock", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L284"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planupdate", "label": "PlanUpdate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L298"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_hazardcertreadybody", "label": "HazardCertReadyBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L350"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_erpenteredbody", "label": "ErpEnteredBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L355"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandocumentattachmentitem", "label": "PlanDocumentAttachmentItem", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L360"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planread", "label": "PlanRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L366"}, {"id": "planresourcesnapshotmixin", "label": "PlanResourceSnapshotMixin", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planwithproducts", "label": "PlanWithProducts", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L401"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_deliveryprintcustomeroption", "label": "DeliLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_py", "label": "pl_models.py", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plcarrier", "label": "PlCarrier", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L28"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plvehicle", "label": "PlVehicle", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L57"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plpersonnel", "label": "PlPersonnel", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L84"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportaddress", "label": "PlTransportAddress", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L119"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plcustomerstoppref", "label": "PlCustomerStopPref", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L143"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plcert", "label": "PlCert", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L168"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pldriver", "label": "PlDriver", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L194"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plvehiclepersonnel", "label": "PlVehiclePersonnel", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L220"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plvehicledriver", "label": "PlVehicleDriver", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L241"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplan", "label": "PlPlan", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L266"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplancontract", "label": "PlPlanContract", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L375"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L398"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanproductbrandqty", "label": "PlPlanProductBrandQty", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L457"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanchange", "label": "PlPlanChange", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L485"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanload", "label": "PlPlanLoad", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L507"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanloadproduct", "label": "PlPlanLoadProduct", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L535"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplandelivery", "label": "PlPlanDelivery", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L566"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplandeliveryproduct", "label": "PlPlanDeliveryProduct", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L595"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransport", "label": "PlTransport", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L631"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportproductfeedback", "label": "PlTransportProductFeedback", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L694"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportproductbrandqty", "label": "PlTransportProductBrandQty", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L715"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportdblink", "label": "PlTransportDbLink", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L738"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_appLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_py", "label": "pl_plan_service.py", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_determine_plan_type", "label": "determine_plan_type()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L48"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_is_ton_product_unit", "label": "is_ton_product_unit()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L56"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_product_quantity", "label": "_product_quantity()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L63"}, {"id": "planproductcreate", "label": "PlanProductCreate", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_product_create_actual_qty", "label": "_product_create_actual_qty()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L73"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_plan_product_actual_qty", "label": "_plan_product_actual_qty()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L79"}, {"id": "plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_sum_product_actual_quantities", "label": "sum_product_actual_quantities()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L85"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_sum_plan_product_actual_quantities", "label": "sum_plan_product_actual_quantities()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L89"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_estimate_qty_fields", "label": "_estimate_qty_fields()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L93"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_build_plan_product", "label": "build_plan_product()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L104"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_apply_vehicle_snapshot_to_plan_transport", "label": "_apply_vehicle_snapshot_to_plan_transport()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L154"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py"}, {"id": "pltransport", "label": "PlTransport", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py"}, {"id": "plvehicle", "label": "PlVehicle", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_resolve_driver", "label": "_resolve_driver()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L172"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_calc_freight_amount", "label": "_calc_freight_amount()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L218"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_fetch_hazard_flags_map", "label": "fetch_hazard_flags_map()", "file_type": "code", "source_file": "app/services/pl_plan_service.py", "source_location": "L239"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_service_resolve_hazard_flags_from_crm", "label": "resolve_hazard_flagLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "label": "test_pl_plan_archive_export.py", "file_type": "code", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_resolve_archive_list_io_column", "label": "test_resolve_archive_list_io_column()", "file_type": "code", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L12"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_append_stop_product_rows_io_and_amount", "label": "test_append_stop_product_rows_io_and_amount()", "file_type": "code", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L20"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_append_stop_product_rows_delivery_external_outbound", "label": "test_append_stop_product_rows_delivery_external_outbound()", "file_type": "code", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L68"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_fakeplan", "label": "_FakePlan", "file_type": "code", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L103"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_plan_common_export_fields", "label": "test_plan_common_export_fields()", "file_type": "code", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L116"}], "edges": [{"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "decimal", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "app_schemas_pl_plan", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "app_services_pl_internal_customer", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "app_services_pl_plan_archive_export", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_resolve_archive_list_io_column", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_append_stop_product_rows_io_and_amount", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L20", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_append_stop_product_rows_delivery_external_outbound", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L68", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_fakeplan", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L103", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_plan_common_export_fields", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_archive_export.py", "source_location": "L116", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_test_plan_common_export_fields", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_archive_export_fakeplan", "relation": "calls", "conteLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_py", "label": "pl_brand_archive.py", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_is_plan_level_row", "label": "_is_plan_level_row()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L12"}, {"id": "plplanproductbrandqty", "label": "PlPlanProductBrandQty", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_row_to_brand_dict", "label": "_row_to_brand_dict()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L16"}, {"id": "any", "label": "Any", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_parse_unit_price", "label": "_parse_unit_price()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L27"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_load_brand_qtys_by_plan_product_ids", "label": "load_brand_qtys_by_plan_product_ids()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L38"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_brand_archive.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_load_brand_qtys_by_stop_product_ids", "label": "load_brand_qtys_by_stop_product_ids()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L71"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_validate_brand_qty_rows", "label": "validate_brand_qty_rows()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L103"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_persist_plan_product_brand_qtys", "label": "persist_plan_product_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L121"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_persist_stop_product_brand_qtys", "label": "persist_stop_product_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_brand_archive.py", "source_location": "L177"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_44", "label": "\u6309\u8ba1\u5212\u4ea7\u54c1\u660e\u7ec6 ID \u6279\u91cf\u52a0\u8f7d\u8ba1\u5212\u7ea7\u5f52\u6863\u54c1\u724c\u6570\u91cf\uff08brandId/brandName/qty/unitPrice\uff09\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L44"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_77", "label": "\u6309\u88c5\u8d27/\u9001\u8d27\u6bb5\u4ea7\u54c1\u884c ID \u6279\u91cf\u52a0\u8f7d\u6bb5\u7ea7\u5f52\u6863\u54c1\u724c\u6570\u91cf\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L77"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_132", "label": "\u8f6f\u5220\u8ba1\u5212\u7ea7\u65e7\u54c1\u724c\u91cf\u884c\u540e\u6309\u5f52\u6863\u7ed3\u679c\u63d2\u5165\uff1b\u65e0\u54c1\u724c\u7ea6\u5b9a\u5219\u53ea\u8f6f\u5220\u4e0d\u63d2\u5165\u3002\u4e0d\u89e6\u78b0\u6bb5\u7ea7\u884c\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L132"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_rationale_190", "label": "\u8f6f\u5220\u8be5\u6bb5\u4ea7\u54c1\u884c\u65e7\u54c1\u724c\u91cf\u540e\u5199\u5165\uff1b\u65e0\u6709\u6548\u6570\u91cf\u5219\u53ea\u8f6f\u5220\u3002", "file_type": "rationale", "source_file": "app/services/pl_brand_archive.py", "source_location": "L190"}], "edges": [{"source": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_brand_archive_py", "target": "decimal", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "app/services/pl_brand_archive.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_app_servicesLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_py", "label": "pl_models.py", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plcarrier", "label": "PlCarrier", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L28"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plvehicle", "label": "PlVehicle", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L57"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plpersonnel", "label": "PlPersonnel", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L84"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportaddress", "label": "PlTransportAddress", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L119"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plcustomerstoppref", "label": "PlCustomerStopPref", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L143"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plcert", "label": "PlCert", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L168"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pldriver", "label": "PlDriver", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L194"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plvehiclepersonnel", "label": "PlVehiclePersonnel", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L220"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plvehicledriver", "label": "PlVehicleDriver", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L241"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplan", "label": "PlPlan", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L266"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplancontract", "label": "PlPlanContract", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L375"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L398"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanproductbrandqty", "label": "PlPlanProductBrandQty", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L457"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanchange", "label": "PlPlanChange", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L484"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanload", "label": "PlPlanLoad", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L506"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplanloadproduct", "label": "PlPlanLoadProduct", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L534"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplandelivery", "label": "PlPlanDelivery", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L565"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_plplandeliveryproduct", "label": "PlPlanDeliveryProduct", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L594"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransport", "label": "PlTransport", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L630"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportproductfeedback", "label": "PlTransportProductFeedback", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L693"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportproductbrandqty", "label": "PlTransportProductBrandQty", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L714"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_models_pl_models_pltransportdblink", "label": "PlTransportDbLink", "file_type": "code", "source_file": "app/models/pl_models.py", "source_location": "L737"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_appLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_py", "label": "pl_plan_contract_gap.py", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_stop_contract_gap", "label": "stop_contract_gap()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L46"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_side_is_exempt", "label": "side_is_exempt()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L51"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_compute_contract_gap_sides", "label": "compute_contract_gap_sides()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L55"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_compute_plan_gap_sides_from_stops", "label": "compute_plan_gap_sides_from_stops()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L70"}, {"id": "planloadread", "label": "PlanLoadRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "plandeliveryread", "label": "PlanDeliveryRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_stop_contract_exists", "label": "_stop_contract_exists()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L104"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_load_stop_gap_expr", "label": "_load_stop_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L121"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_delivery_stop_gap_expr", "label": "_delivery_stop_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L144"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_legacy_supplier_gap_expr", "label": "_legacy_supplier_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L167"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_legacy_demander_gap_expr", "label": "_legacy_demander_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L193"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_plan_has_contract_gap_exists", "label": "plan_has_contract_gap_exists()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L219"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_supplier_gap_expr", "label": "_supplier_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L229"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_demander_gap_expr", "label": "_demander_gap_expr()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L233"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_build_plan_contract_gap_read", "label": "build_plan_contract_gap_read()", "file_type": "code", "source_file": "app/services/pl_plan_contract_gap.py", "source_location": "L237"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "plancontractlink", "label": "PlanContractLink", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "planproductread", "label": "PlanProductRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "plancontractgapread", "label": "PlanContractGapRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_contract_gap.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_contract_gap_load_products_by_plan_ids", "label": "_load_productsLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_py", "label": "plan.py", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductbase", "label": "PlanProductBase", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L16"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductcreate", "label": "PlanProductCreate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L55"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductupdate", "label": "PlanProductUpdate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L59"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planarchivebrandqtyitem", "label": "PlanArchiveBrandQtyItem", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L81"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planproductread", "label": "PlanProductRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L88"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planstopproductbody", "label": "PlanStopProductBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L105"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planstopproductread", "label": "PlanStopProductRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L123"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planloadbody", "label": "PlanLoadBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L142"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandeliverybody", "label": "PlanDeliveryBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L161"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planloadread", "label": "PlanLoadRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L181"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandeliveryread", "label": "PlanDeliveryRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L192"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planbase", "label": "PlanBase", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L208"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plancontractlink", "label": "PlanContractLink", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L259"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plancreate", "label": "PlanCreate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L266"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plantransportcreateblock", "label": "PlanTransportCreateBlock", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L283"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planupdate", "label": "PlanUpdate", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L297"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_hazardcertreadybody", "label": "HazardCertReadyBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L349"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_erpenteredbody", "label": "ErpEnteredBody", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L354"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_plandocumentattachmentitem", "label": "PlanDocumentAttachmentItem", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L359"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planread", "label": "PlanRead", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L365"}, {"id": "planresourcesnapshotmixin", "label": "PlanResourceSnapshotMixin", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_planwithproducts", "label": "PlanWithProducts", "file_type": "code", "source_file": "app/schemas/pl/plan.py", "source_location": "L400"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_schemas_pl_plan_deliveryprintcustomeroption", "label": "DeliLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_py", "label": "pl_plan_stop_service.py", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_contract_link", "label": "_contract_link()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L46"}, {"id": "plplancontract", "label": "PlPlanContract", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "plancontractlink", "label": "PlanContractLink", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_attach_stop_brand_qtys", "label": "_attach_stop_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L54"}, {"id": "planstopproductread", "label": "PlanStopProductRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_resolve_stop_list_product_name", "label": "_resolve_stop_list_product_name()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L66"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_apply_stop_list_product_name", "label": "_apply_stop_list_product_name()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L83"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_load_stop_product", "label": "_load_stop_product()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L98"}, {"id": "plplanloadproduct", "label": "PlPlanLoadProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_delivery_stop_product", "label": "_delivery_stop_product()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L117"}, {"id": "plplandeliveryproduct", "label": "PlPlanDeliveryProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_load_stop_contract_rows_grouped", "label": "load_stop_contract_rows_grouped()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L136"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_contracts_for_stop", "label": "_contracts_for_stop()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L159"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_to_decimal", "label": "_to_decimal()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L174"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_resolve_stop_address_from_library", "label": "resolve_stop_address_from_library()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L183"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_enrich_stop_addresses_from_library", "label": "enrich_stop_addresses_from_library()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L224"}, {"id": "planloadbody", "label": "PlanLoadBody", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "plandeliverybody", "label": "PlanDeliveryBody", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/apLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_py", "label": "pl_plan_stop_service.py", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_contract_link", "label": "_contract_link()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L45"}, {"id": "plplancontract", "label": "PlPlanContract", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "plancontractlink", "label": "PlanContractLink", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_attach_stop_brand_qtys", "label": "_attach_stop_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L53"}, {"id": "planstopproductread", "label": "PlanStopProductRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_load_stop_product", "label": "_load_stop_product()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L65"}, {"id": "plplanloadproduct", "label": "PlPlanLoadProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_delivery_stop_product", "label": "_delivery_stop_product()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L75"}, {"id": "plplandeliveryproduct", "label": "PlPlanDeliveryProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_load_stop_contract_rows_grouped", "label": "load_stop_contract_rows_grouped()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L85"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_contracts_for_stop", "label": "_contracts_for_stop()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L108"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_to_decimal", "label": "_to_decimal()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L123"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_resolve_stop_address_from_library", "label": "resolve_stop_address_from_library()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L132"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_enrich_stop_addresses_from_library", "label": "enrich_stop_addresses_from_library()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L173"}, {"id": "planloadbody", "label": "PlanLoadBody", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "plandeliverybody", "label": "PlanDeliveryBody", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_stop_service_load_plan_stops_grouped", "label": "load_plan_stops_grouped()", "file_type": "code", "source_file": "app/services/pl_plan_stop_service.py", "source_location": "L192"}, {"id": "planloadread", "label": "PlanLoadRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_stop_service.py"}, {"id": "plandeliveryread", "laLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_py", "label": "pl_plan_archive_export.py", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_resolve_archive_list_io_column", "label": "resolve_archive_list_io_column()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L44"}, {"id": "stopkind", "label": "StopKind", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_format_date_value", "label": "_format_date_value()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L52"}, {"id": "date", "label": "date", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "datetime", "label": "datetime", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_format_plate_display", "label": "_format_plate_display()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L58"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_get_stop_archive_actual_qty", "label": "_get_stop_archive_actual_qty()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L68"}, {"id": "planstopproductread", "label": "PlanStopProductRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_get_stop_archive_unit_price", "label": "_get_stop_archive_unit_price()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L72"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_get_stop_archive_amount", "label": "_get_stop_archive_amount()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L76"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_stop_kind_label", "label": "_stop_kind_label()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L87"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_stop_time", "label": "_stop_time()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L93"}, {"id": "planloadread", "label": "PlanLoadRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "plandeliveryread", "label": "PlanDeliveryRead", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_load_legacy_products_by_plan", "label": "_load_legacy_products_by_plan()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L99"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "plplanproduct", "label": "PlPlanProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_export_plan_common_export_fields", "label": "_plan_common_export_fields()", "file_type": "code", "source_file": "app/services/pl_plan_archive_export.py", "source_location": "L120"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_export.py"}, {"id": "pltransport", "label": "PlTransport", "fiLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_py", "label": "pl_plan_archive_stop_service.py", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_require_qty", "label": "_require_qty()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L28"}, {"id": "decimal", "label": "Decimal", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_apply_stop_product_archive", "label": "_apply_stop_product_archive()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L37"}, {"id": "plplanloadproduct", "label": "PlPlanLoadProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "plplandeliveryproduct", "label": "PlPlanDeliveryProduct", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "planarchivestopproductitem", "label": "PlanArchiveStopProductItem", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_brand_rows_from_item", "label": "_brand_rows_from_item()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L64"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_persist_stop_brand_qtys", "label": "_persist_stop_brand_qtys()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L77"}, {"id": "session", "label": "Session", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "plplan", "label": "PlPlan", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_apply_archive_stop_quantities", "label": "apply_archive_stop_quantities()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L111"}, {"id": "planarchiveloadblock", "label": "PlanArchiveLoadBlock", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "planarchivedeliveryblock", "label": "PlanArchiveDeliveryBlock", "file_type": "code", "source_file": "", "source_location": "", "origin_file": "/Users/wuzilong/work/00code/shanxia2.0/backend/app/services/pl_plan_archive_stop_service.py"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_sync_plan_products_from_stop_archive", "label": "sync_plan_products_from_stop_archive()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L213"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_sum_delivery_outbound_qty", "label": "sum_delivery_outbound_qty()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L292"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_iter_stop_archive_inventory_lines", "label": "iter_stop_archive_inventory_lines()", "file_type": "code", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L308"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_rationale_220", "label": "\u5c06\u6bb5\u7ea7\u5f52\u6863\u6570\u91cf\u6c47\u603b\u56de\u5199 pl_plan_product\uff08_row_to_dict \u6743\u5a01\u6765\u6e90\uff09\u3002", "file_type": "rationale", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "L220"}], "edges": [{"source": "users_wuzilong_work_00code_shanxia2_0_backend_app_services_pl_plan_archive_stop_service_py", "target": "collections", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "app/services/pl_plan_archive_stop_service.py", "source_location": "Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_py", "label": "test_pl_plan_contract_gap.py", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L1"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_both_external_no_contracts_gaps_both", "label": "test_both_external_no_contracts_gaps_both()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L15"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_internal_supplier_external_demander_missing_only_demander", "label": "test_internal_supplier_external_demander_missing_only_demander()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L25"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_both_internal_no_gap", "label": "test_both_internal_no_gap()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L35"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_external_with_contracts_no_gap", "label": "test_external_with_contracts_no_gap()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L45"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_empty_names_treated_as_non_internal_gap_both", "label": "test_empty_names_treated_as_non_internal_gap_both()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L55"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_stop_contract_gap_internal_exempt", "label": "test_stop_contract_gap_internal_exempt()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L65"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_compute_plan_gap_sides_from_stops_multi_delivery", "label": "test_compute_plan_gap_sides_from_stops_multi_delivery()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L71"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_compute_plan_gap_sides_from_stops_internal_load_no_gap", "label": "test_compute_plan_gap_sides_from_stops_internal_load_no_gap()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L108"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_compile", "label": "_compile()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L133"}, {"id": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_plan_has_contract_gap_exists_mentions_contract_and_internal_names", "label": "test_plan_has_contract_gap_exists_mentions_contract_and_internal_names()", "file_type": "code", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L139"}], "edges": [{"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_py", "target": "sqlalchemy_dialects", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_py", "target": "app_schemas_pl_plan", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_py", "target": "app_services_pl_internal_customer", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_py", "target": "app_services_pl_plan_contract_gap", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_both_external_no_contracts_gaps_both", "relation": "contains", "confidence": "EXTRACTED", "source_file": "tests/test_pl_plan_contract_gap.py", "source_location": "L15", "weight": 1.0}, {"source": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_py", "target": "users_wuzilong_work_00code_shanxia2_0_backend_tests_test_pl_plan_contract_gap_test_internal_supplier_external_demLine truncated
|
||||
@@ -0,0 +1,123 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from app.schemas.pl.plan import PlanDeliveryRead, PlanLoadRead, PlanStopProductRead
|
||||
from app.services.pl_internal_customer import INTERNAL_CUSTOMER_NAMES
|
||||
from app.services.pl_plan_archive_export import (
|
||||
_append_stop_product_rows,
|
||||
_plan_common_export_fields,
|
||||
resolve_archive_list_io_column,
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_archive_list_io_column():
|
||||
internal = next(iter(INTERNAL_CUSTOMER_NAMES))
|
||||
assert resolve_archive_list_io_column("load", internal) == "outbound"
|
||||
assert resolve_archive_list_io_column("load", "外部客户") == "inbound"
|
||||
assert resolve_archive_list_io_column("delivery", internal) == "inbound"
|
||||
assert resolve_archive_list_io_column("delivery", "外部客户") == "outbound"
|
||||
|
||||
|
||||
def test_append_stop_product_rows_io_and_amount():
|
||||
internal = next(iter(INTERNAL_CUSTOMER_NAMES))
|
||||
product = PlanStopProductRead(
|
||||
id="sp1",
|
||||
planId="p1",
|
||||
tenantNo="t1",
|
||||
productId="prod1",
|
||||
productNo="P001",
|
||||
productName="甲醇",
|
||||
model="99%",
|
||||
unit="吨",
|
||||
packageSpec="散装",
|
||||
planQty=Decimal("10"),
|
||||
inboundQty=Decimal("9.5"),
|
||||
inboundPrice=Decimal("100"),
|
||||
inboundAmount=None,
|
||||
createTime="2026-08-27T00:00:00",
|
||||
updateTime="2026-08-27T00:00:00",
|
||||
)
|
||||
load = PlanLoadRead(
|
||||
id="load1",
|
||||
planId="p1",
|
||||
tenantNo="t1",
|
||||
customerName=internal,
|
||||
address="装货地址",
|
||||
loadTime="2026-08-27T08:00:00",
|
||||
products=[product],
|
||||
createTime="2026-08-27T00:00:00",
|
||||
updateTime="2026-08-27T00:00:00",
|
||||
)
|
||||
rows: list[dict] = []
|
||||
plan_common = {
|
||||
"planNo": "PL20260827001",
|
||||
"planDate": "2026-08-27",
|
||||
}
|
||||
row_no = _append_stop_product_rows(rows, 0, plan_common, "load", load, None, load.products)
|
||||
assert row_no == 1
|
||||
assert len(rows) == 1
|
||||
row = rows[0]
|
||||
assert row["stopKind"] == "装货"
|
||||
assert row["stopCustomer"] == internal
|
||||
assert row["actualQty"] == Decimal("9.5")
|
||||
assert row["outboundQty"] == Decimal("9.5")
|
||||
assert row["inboundQty"] is None
|
||||
assert row["unitPrice"] == Decimal("100")
|
||||
assert row["amount"] == Decimal("950")
|
||||
|
||||
|
||||
def test_append_stop_product_rows_delivery_external_outbound():
|
||||
product = PlanStopProductRead(
|
||||
id="sp2",
|
||||
planId="p1",
|
||||
tenantNo="t1",
|
||||
productId="prod1",
|
||||
productNo="P001",
|
||||
productName="甲醇",
|
||||
outboundQty=Decimal("8"),
|
||||
outboundPrice=Decimal("120"),
|
||||
outboundAmount=Decimal("960"),
|
||||
createTime="2026-08-27T00:00:00",
|
||||
updateTime="2026-08-27T00:00:00",
|
||||
)
|
||||
delivery = PlanDeliveryRead(
|
||||
id="del1",
|
||||
planId="p1",
|
||||
tenantNo="t1",
|
||||
seq=1,
|
||||
customerName="外部客户",
|
||||
address="送货地址",
|
||||
deliveryTime="2026-08-28T10:00:00",
|
||||
products=[product],
|
||||
createTime="2026-08-27T00:00:00",
|
||||
updateTime="2026-08-27T00:00:00",
|
||||
)
|
||||
rows: list[dict] = []
|
||||
row_no = _append_stop_product_rows(rows, 0, {"planNo": "P1"}, "delivery", delivery, 1, delivery.products)
|
||||
row = rows[0]
|
||||
assert row["stopKind"] == "送货 #1"
|
||||
assert row["outboundQty"] == Decimal("8")
|
||||
assert row["inboundQty"] is None
|
||||
assert row["amount"] == Decimal("960")
|
||||
|
||||
|
||||
class _FakePlan:
|
||||
plan_no = "PL001"
|
||||
plan_date = "2026-08-27"
|
||||
archive_status = "archived"
|
||||
follow_status = "arranged"
|
||||
plan_type = "direct"
|
||||
license_plate = "皖A12345"
|
||||
trailer_no = "挂001"
|
||||
erp_entered = 1
|
||||
freight_amount = Decimal("500")
|
||||
remark = "测试"
|
||||
|
||||
|
||||
def test_plan_common_export_fields():
|
||||
fields = _plan_common_export_fields(_FakePlan(), None)
|
||||
assert fields["planNo"] == "PL001"
|
||||
assert fields["archiveStatus"] == "已归档"
|
||||
assert fields["followStatus"] == "已安排"
|
||||
assert fields["planType"] == "直销单"
|
||||
assert fields["licensePlate"] == "皖A12345挂001"
|
||||
assert fields["erpEntered"] == "是"
|
||||
@@ -0,0 +1,858 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Fresh Mint · 运输计划合同完善 · 补充弹窗 Demo</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<style>
|
||||
:root {
|
||||
--primary: #10b981;
|
||||
--primary-dark: #059669;
|
||||
--primary-bg: #ecfdf5;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #d9e8e2;
|
||||
--surface: #ffffff;
|
||||
--table-head: #f8fafc;
|
||||
--bg-page: #f8fafc;
|
||||
--row-alt: #fafafa;
|
||||
--row-hover: #f1f5f9;
|
||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
--shadow-lg: 0 12px 40px rgba(15, 23, 42, 0.12);
|
||||
--radius: 10px;
|
||||
--warn: #d97706;
|
||||
--warn-bg: #fffbeb;
|
||||
--danger: #dc2626;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: var(--text);
|
||||
background: var(--bg-page);
|
||||
}
|
||||
|
||||
.demo-page { max-width: 1360px; margin: 0 auto; padding: 28px 24px 80px; }
|
||||
.demo-head { margin-bottom: 20px; }
|
||||
.demo-head h1 { margin: 0 0 8px; font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
|
||||
.demo-head h1 span { color: var(--primary); }
|
||||
.demo-head p { margin: 0; color: var(--text-muted); max-width: 920px; }
|
||||
.meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
||||
.pill {
|
||||
display: inline-flex; align-items: center;
|
||||
padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 500;
|
||||
background: var(--primary-bg); color: var(--primary-dark); border: 1px solid #a7f3d0;
|
||||
}
|
||||
.pill.muted { background: #f1f5f9; color: var(--text-muted); border-color: #e2e8f0; }
|
||||
.pill.warn { background: var(--warn-bg); color: var(--warn); border-color: rgba(217,119,6,.25); }
|
||||
|
||||
.note {
|
||||
margin: 0 0 16px; padding: 12px 14px; border-radius: 8px;
|
||||
background: var(--primary-bg); border: 1px solid #6ee7b7; color: var(--primary-dark);
|
||||
font-size: 13px;
|
||||
}
|
||||
.note strong { color: var(--text); }
|
||||
|
||||
.panel {
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
|
||||
}
|
||||
.panel-top {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 12px; padding: 16px 20px 12px; border-bottom: 1px solid #eef2f6;
|
||||
}
|
||||
.panel-top h2 {
|
||||
margin: 0; font-size: 16px; font-weight: 600;
|
||||
display: flex; align-items: center;
|
||||
}
|
||||
.panel-top h2::before {
|
||||
content: ""; display: inline-block; width: 3px; height: 16px;
|
||||
background: var(--primary); border-radius: 2px; margin-right: 10px;
|
||||
}
|
||||
.stat-chip {
|
||||
display: inline-flex; align-items: center;
|
||||
padding: 4px 10px; border-radius: 8px; font-size: 12px; font-weight: 600;
|
||||
background: var(--warn-bg); color: var(--warn); border: 1px solid rgba(217,119,6,.2);
|
||||
}
|
||||
|
||||
.filter-block { padding: 14px 20px 6px; }
|
||||
.filter-grid {
|
||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px 16px;
|
||||
}
|
||||
@media (max-width: 1100px) { .filter-grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
.field label {
|
||||
display: block; font-size: 11px; color: var(--text-muted); margin-bottom: 4px;
|
||||
}
|
||||
.field input, .field select {
|
||||
width: 100%; height: 36px; border: 1px solid var(--border); border-radius: 8px;
|
||||
padding: 0 10px; font-size: 14px; color: var(--text); background: #fff;
|
||||
}
|
||||
.filter-actions {
|
||||
display: flex; justify-content: flex-end; gap: 8px; padding: 10px 0 4px;
|
||||
}
|
||||
|
||||
.list-toolbar {
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
padding: 10px 20px; border-top: 1px solid #eef2f6;
|
||||
}
|
||||
.date-pills { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.date-pill {
|
||||
height: 30px; padding: 0 10px; border-radius: 999px; border: 1px solid var(--border);
|
||||
background: #fff; font-size: 12px; color: var(--text-muted); cursor: pointer;
|
||||
}
|
||||
.date-pill.active {
|
||||
background: var(--primary-bg); border-color: #6ee7b7; color: var(--primary-dark); font-weight: 600;
|
||||
}
|
||||
.toolbar-spacer { flex: 1; }
|
||||
.quick-search {
|
||||
width: 220px; height: 36px; border: 1px solid var(--border); border-radius: 8px;
|
||||
padding: 0 12px; font-size: 13px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
appearance: none; display: inline-flex; align-items: center; gap: 6px;
|
||||
height: 36px; padding: 0 14px; border-radius: 8px; font-size: 13px; font-weight: 500;
|
||||
cursor: pointer; border: 1px solid var(--border); background: #fff; color: var(--text);
|
||||
}
|
||||
.btn:hover { background: #f8fafc; }
|
||||
.btn-main { background: var(--primary); border-color: var(--primary); color: #fff; }
|
||||
.btn-main:hover { background: var(--primary-dark); }
|
||||
.btn-ghost { background: #fff; }
|
||||
.btn-link {
|
||||
height: auto; padding: 0; border: none; background: transparent;
|
||||
color: var(--primary-dark); font-weight: 500; cursor: pointer; font-size: 13px;
|
||||
}
|
||||
.btn-link:hover { text-decoration: underline; }
|
||||
.btn-link.warn { color: var(--warn); }
|
||||
.btn-sm { height: 30px; padding: 0 10px; font-size: 12px; }
|
||||
.btn-icon {
|
||||
width: 28px; height: 28px; padding: 0; justify-content: center;
|
||||
border: none; background: transparent; color: var(--text-muted); cursor: pointer;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.btn-icon:hover { background: #f1f5f9; color: var(--primary-dark); }
|
||||
.btn-icon.danger:hover { color: var(--danger); background: #fef2f2; }
|
||||
|
||||
.list-body { border-top: 1px solid #eef2f6; overflow-x: auto; }
|
||||
table.list-table {
|
||||
width: 100%; border-collapse: collapse; min-width: 1100px;
|
||||
}
|
||||
.list-table th {
|
||||
background: var(--table-head); text-align: left; font-weight: 600; font-size: 12px;
|
||||
color: var(--text-muted); padding: 10px 12px; border-bottom: 1px solid #eef2f6;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.list-table td {
|
||||
padding: 12px; border-bottom: 1px solid #f1f5f9; vertical-align: middle;
|
||||
font-size: 13px;
|
||||
}
|
||||
.list-table tbody tr:hover { background: var(--row-hover); }
|
||||
.list-table .mono { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--primary-dark); }
|
||||
.list-table .muted { color: var(--text-muted); }
|
||||
.list-table .merge-cell { background: #fff; }
|
||||
.addr-time { display: flex; flex-direction: column; gap: 2px; }
|
||||
.addr-time .addr { color: var(--text); }
|
||||
.addr-time .time { font-size: 12px; color: var(--text-muted); }
|
||||
|
||||
.tag {
|
||||
display: inline-flex; align-items: center; padding: 2px 8px;
|
||||
border-radius: 6px; font-size: 12px; font-weight: 500; white-space: nowrap;
|
||||
}
|
||||
.tag.success { background: #059669; color: #fff; }
|
||||
.tag.warn { background: #d97706; color: #fff; }
|
||||
.tag.danger { background: #dc2626; color: #fff; }
|
||||
.tag.info { background: transparent; color: #1e293b; border: 1px solid #e2e8f0; }
|
||||
.tag.muted { background: #f1f5f9; color: var(--text-muted); }
|
||||
.tag.internal { background: #059669; color: #fff; margin-left: 6px; }
|
||||
|
||||
.gap-cell {
|
||||
display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
|
||||
}
|
||||
.gap-line {
|
||||
display: flex; align-items: center; gap: 6px; font-size: 12px;
|
||||
}
|
||||
.gap-line .side { color: var(--text-muted); min-width: 2.5em; }
|
||||
.gap-line .pending { color: var(--warn); font-weight: 500; }
|
||||
.gap-line .ok { color: var(--primary-dark); font-weight: 500; }
|
||||
|
||||
.pager {
|
||||
display: flex; align-items: center; justify-content: flex-end; gap: 12px;
|
||||
padding: 12px 20px; border-top: 1px solid #eef2f6; color: var(--text-muted); font-size: 13px;
|
||||
}
|
||||
|
||||
/* 弹窗 */
|
||||
.modal-mask {
|
||||
display: none; position: fixed; inset: 0; background: rgba(15, 23, 42, 0.45);
|
||||
z-index: 1000; align-items: center; justify-content: center; padding: 24px;
|
||||
backdrop-filter: none;
|
||||
}
|
||||
.modal-mask.open { display: flex; }
|
||||
.dialog {
|
||||
width: min(760px, 100%); max-height: calc(100vh - 100px);
|
||||
background: #fff; border-radius: 12px; box-shadow: var(--shadow-lg);
|
||||
display: flex; flex-direction: column; overflow: hidden;
|
||||
}
|
||||
.dialog-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 14px 20px; border-bottom: 1px solid #eef2f6; flex-shrink: 0;
|
||||
}
|
||||
.dialog-head h3 { margin: 0; font-size: 16px; font-weight: 600; }
|
||||
.dialog-body { padding: 18px 20px; overflow: auto; flex: 1; }
|
||||
.dialog-foot {
|
||||
display: flex; justify-content: flex-end; gap: 8px;
|
||||
padding: 12px 20px; border-top: 1px solid #eef2f6; flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stop-summary {
|
||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
|
||||
margin-bottom: 16px; padding: 12px 14px; background: #f8fafc; border-radius: 8px;
|
||||
border: 1px solid #eef2f6;
|
||||
}
|
||||
@media (max-width: 700px) { .stop-summary { grid-template-columns: 1fr 1fr; } }
|
||||
.kv .k { font-size: 11px; color: var(--text-muted); }
|
||||
.kv .v { font-size: 13px; font-weight: 500; margin-top: 2px; }
|
||||
|
||||
.side-box {
|
||||
background: #fff; border: 1px solid var(--border); border-radius: 8px; padding: 14px;
|
||||
}
|
||||
.side-box.need { border-color: rgba(217,119,6,.35); background: #fffdf8; }
|
||||
.side-box.ok { border-color: #a7f3d0; background: #f6fffb; }
|
||||
.side-box.exempt { border-color: #e2e8f0; background: #f8fafc; }
|
||||
.side-box__head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 8px; margin-bottom: 6px;
|
||||
}
|
||||
.side-box__head h4 { margin: 0; font-size: 14px; font-weight: 600; }
|
||||
.side-box__customer { font-size: 13px; margin-bottom: 12px; color: var(--text); }
|
||||
.side-box__exempt {
|
||||
margin: 0; padding: 14px; border: 1px solid #e2e8f0; border-radius: 8px;
|
||||
background: #fff; color: var(--text-muted); font-size: 13px; text-align: center;
|
||||
}
|
||||
|
||||
.contract-toolbar {
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px;
|
||||
}
|
||||
.contract-hint { font-size: 12px; color: var(--text-muted); }
|
||||
.contract-hint.is-warn { color: var(--warn); }
|
||||
|
||||
.contract-cards { display: flex; flex-direction: column; gap: 10px; }
|
||||
.contract-card {
|
||||
border: 1px solid var(--border); border-radius: 8px; background: #fff; overflow: hidden;
|
||||
}
|
||||
.contract-card__head {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
||||
padding: 10px 12px; background: #f8fafc; border-bottom: 1px solid #eef2f6;
|
||||
}
|
||||
.contract-card__meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.contract-card__no { font-weight: 600; color: var(--primary-dark); font-size: 13px; }
|
||||
.contract-card__count { font-size: 12px; color: var(--text-muted); }
|
||||
.contract-card__actions { display: flex; align-items: center; gap: 2px; }
|
||||
.contract-card__body { padding: 0; }
|
||||
.mini-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||||
.mini-table th {
|
||||
background: #fafafa; color: var(--text-muted); font-weight: 500;
|
||||
padding: 6px 10px; text-align: left; border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
.mini-table td {
|
||||
padding: 6px 10px; border-bottom: 1px solid #f8fafc; color: var(--text);
|
||||
}
|
||||
.empty-hint {
|
||||
margin: 0; padding: 18px; text-align: center; color: var(--text-muted);
|
||||
font-size: 13px; border: 1px dashed #cbd5e1; border-radius: 8px; background: #fff;
|
||||
}
|
||||
|
||||
/* 合同选择器(二级弹层) */
|
||||
.selector-mask {
|
||||
display: none; position: fixed; inset: 0; background: rgba(15, 23, 42, 0.4);
|
||||
z-index: 1100; align-items: center; justify-content: center; padding: 24px;
|
||||
}
|
||||
.selector-mask.open { display: flex; }
|
||||
.selector-dialog {
|
||||
width: min(640px, 100%); max-height: calc(100vh - 120px);
|
||||
background: #fff; border-radius: 12px; box-shadow: var(--shadow-lg);
|
||||
display: flex; flex-direction: column; overflow: hidden;
|
||||
}
|
||||
.selector-list { overflow: auto; flex: 1; border-top: 1px solid #eef2f6; }
|
||||
.selector-item {
|
||||
display: flex; align-items: flex-start; gap: 10px;
|
||||
padding: 12px 16px; border-bottom: 1px solid #f1f5f9; cursor: pointer;
|
||||
}
|
||||
.selector-item:hover, .selector-item.selected { background: var(--primary-bg); }
|
||||
.selector-item .meta-line { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
|
||||
.selector-item input { margin-top: 3px; }
|
||||
|
||||
.toast {
|
||||
position: fixed; top: 20px; left: 50%; transform: translateX(-50%) translateY(-120%);
|
||||
background: #059669; color: #fff; padding: 10px 18px; border-radius: 8px;
|
||||
font-size: 13px; font-weight: 500; box-shadow: var(--shadow-lg);
|
||||
transition: transform .25s ease; z-index: 2000;
|
||||
}
|
||||
.toast.show { transform: translateX(-50%) translateY(0); }
|
||||
|
||||
.legend {
|
||||
margin-top: 20px; display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
|
||||
}
|
||||
@media (max-width: 800px) { .legend { grid-template-columns: 1fr; } }
|
||||
.card {
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow);
|
||||
}
|
||||
.card h3 {
|
||||
margin: 0 0 8px; font-size: 14px; font-weight: 600;
|
||||
padding-left: 10px; border-left: 3px solid var(--primary); color: var(--primary-dark);
|
||||
}
|
||||
.card ul { margin: 0; padding-left: 18px; color: var(--text-muted); font-size: 13px; }
|
||||
.card li { margin: 4px 0; }
|
||||
.card li strong { color: var(--text); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="demo-page">
|
||||
<header class="demo-head">
|
||||
<h1>运输计划合同完善 · <span>补充弹窗</span></h1>
|
||||
<p>
|
||||
对齐当前线上列表(段次行合并):点击合同缺口旁的「补充」打开弹窗,
|
||||
仅针对<strong>本段客户</strong>关联已有 CRM 合同并保存。不再使用行内展开。
|
||||
</p>
|
||||
<div class="meta">
|
||||
<span class="pill">Fresh Mint · web-admin</span>
|
||||
<span class="pill warn">点击「补充」体验弹窗</span>
|
||||
<span class="pill muted">路径 /#/pl/plan-module/plan-contract</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<p class="note">
|
||||
<strong>交互变更:</strong>原「行内展开 + 保存本段合同」→ 改为「弹窗补充」。
|
||||
弹窗内复用合同多选卡片(选择合同 / 查看 / 移除),底栏「取消 / 保存本段合同」。
|
||||
未保存关闭时二次确认(Demo 用 confirm)。
|
||||
</p>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-top">
|
||||
<h2>运输计划合同完善</h2>
|
||||
<span class="stat-chip">待完善 3 条</span>
|
||||
</div>
|
||||
|
||||
<div class="filter-block">
|
||||
<div class="filter-grid">
|
||||
<div class="field">
|
||||
<label>计划编号</label>
|
||||
<input placeholder="搜索计划编号" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>缺口类型</label>
|
||||
<select>
|
||||
<option>全部缺口</option>
|
||||
<option>装货段缺合同</option>
|
||||
<option>送货段缺合同</option>
|
||||
<option>装货与送货均缺</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>客户名称</label>
|
||||
<input placeholder="段次客户名称" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>计划日期</label>
|
||||
<input type="text" placeholder="开始日期 - 结束日期" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<button type="button" class="btn btn-ghost">重置</button>
|
||||
<button type="button" class="btn btn-main">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-toolbar">
|
||||
<div class="date-pills">
|
||||
<button type="button" class="date-pill active">全部</button>
|
||||
<button type="button" class="date-pill">今天</button>
|
||||
<button type="button" class="date-pill">近 7 天</button>
|
||||
<button type="button" class="date-pill">本月</button>
|
||||
</div>
|
||||
<div class="toolbar-spacer"></div>
|
||||
<input class="quick-search" placeholder="计划编号/客户名称" />
|
||||
<button type="button" class="btn btn-ghost">导出</button>
|
||||
<button type="button" class="btn btn-ghost">刷新</button>
|
||||
<span class="muted" style="font-size:13px">待完善 3 条</span>
|
||||
</div>
|
||||
|
||||
<div class="list-body">
|
||||
<table class="list-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:52px">序号</th>
|
||||
<th style="width:130px">计划编号</th>
|
||||
<th style="width:100px">计划日期</th>
|
||||
<th style="width:80px">单据类型</th>
|
||||
<th style="width:88px">跟进状态</th>
|
||||
<th style="width:72px">段次</th>
|
||||
<th style="width:160px">客户</th>
|
||||
<th style="width:160px">地址 / 时间</th>
|
||||
<th style="width:200px">合同缺口</th>
|
||||
<th>产品名称</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 计划 1:装货缺合同,送货内置可免 -->
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td rowspan="2" class="merge-cell">
|
||||
<button type="button" class="btn-link mono">YP20260820001</button>
|
||||
</td>
|
||||
<td rowspan="2" class="merge-cell">2026-08-20</td>
|
||||
<td rowspan="2" class="merge-cell">汽运</td>
|
||||
<td rowspan="2" class="merge-cell"><span class="tag success">已安排</span></td>
|
||||
<td>装货</td>
|
||||
<td>合肥某某商贸有限公司</td>
|
||||
<td>
|
||||
<div class="addr-time">
|
||||
<span class="addr">合肥市经开区某某路 88 号</span>
|
||||
<span class="time">08:30</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="gap-cell">
|
||||
<div class="gap-line">
|
||||
<span class="side">装货</span>
|
||||
<span class="pending">待补充</span>
|
||||
</div>
|
||||
<button type="button" class="btn-link warn" data-open="load1">✎ 补充</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>液碱 32%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>送货</td>
|
||||
<td>
|
||||
安庆市山峡化工有限公司
|
||||
<span class="tag internal">内部</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="addr-time">
|
||||
<span class="addr">安庆市迎江区某某大道</span>
|
||||
<span class="time">14:00</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="gap-cell">
|
||||
<div class="gap-line">
|
||||
<span class="side">送货</span>
|
||||
<span class="ok">内置可免</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>液碱 32%</td>
|
||||
</tr>
|
||||
|
||||
<!-- 计划 2:送货缺合同 -->
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td rowspan="2" class="merge-cell">
|
||||
<button type="button" class="btn-link mono">YP20260819088</button>
|
||||
</td>
|
||||
<td rowspan="2" class="merge-cell">2026-08-19</td>
|
||||
<td rowspan="2" class="merge-cell">汽运</td>
|
||||
<td rowspan="2" class="merge-cell"><span class="tag info">待安排</span></td>
|
||||
<td>装货</td>
|
||||
<td>
|
||||
安徽云栋化工有限公司
|
||||
<span class="tag internal">内部</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="addr-time">
|
||||
<span class="addr">安庆市大观区某某工业园</span>
|
||||
<span class="time">09:00</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="gap-cell">
|
||||
<div class="gap-line">
|
||||
<span class="side">装货</span>
|
||||
<span class="ok">内置可免</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>盐酸</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>送货</td>
|
||||
<td>芜湖某某材料有限公司</td>
|
||||
<td>
|
||||
<div class="addr-time">
|
||||
<span class="addr">芜湖市弋江区某某路</span>
|
||||
<span class="time">16:00</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="gap-cell">
|
||||
<div class="gap-line">
|
||||
<span class="side">送货</span>
|
||||
<span class="pending">待补充</span>
|
||||
</div>
|
||||
<button type="button" class="btn-link warn" data-open="delivery2">✎ 补充</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>盐酸</td>
|
||||
</tr>
|
||||
|
||||
<!-- 计划 3:两侧均缺,仅示装货行补充入口 -->
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td class="merge-cell">
|
||||
<button type="button" class="btn-link mono">YP20260818042</button>
|
||||
</td>
|
||||
<td class="merge-cell">2026-08-18</td>
|
||||
<td class="merge-cell">汽运</td>
|
||||
<td class="merge-cell"><span class="tag success">运输中</span></td>
|
||||
<td>装货</td>
|
||||
<td>马鞍山某某化工有限公司</td>
|
||||
<td>
|
||||
<div class="addr-time">
|
||||
<span class="addr">马鞍山市雨山区某某路</span>
|
||||
<span class="time">07:30</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="gap-cell">
|
||||
<div class="gap-line">
|
||||
<span class="side">装货</span>
|
||||
<span class="pending">待补充</span>
|
||||
</div>
|
||||
<button type="button" class="btn-link warn" data-open="load3">✎ 补充</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>片碱</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pager">共 3 条 · 每页 20 条 · 1 / 1</div>
|
||||
</section>
|
||||
|
||||
<section class="legend">
|
||||
<div class="card">
|
||||
<h3>弹窗行为</h3>
|
||||
<ul>
|
||||
<li>标题:<strong>补充合同 · 装货/送货</strong> + 计划编号</li>
|
||||
<li>摘要:计划日期、跟进状态、本段客户、地址时间</li>
|
||||
<li>主体:复用合同多选卡片(选择 / 查看 / 移除)</li>
|
||||
<li>保存:仅提交本段合同;成功后刷新列表与角标</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>与现网差异</h3>
|
||||
<ul>
|
||||
<li>❌ 去掉表格 <code>type="expand"</code> 行内展开区</li>
|
||||
<li>✅ 「补充」改为打开弹窗(本 Demo)</li>
|
||||
<li>✅ 内置客户行仍不展示「补充」入口</li>
|
||||
<li>✅ 不在此页新建 / 编辑合同</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 补充合同弹窗 -->
|
||||
<div class="modal-mask" id="dialog-mask" role="dialog" aria-modal="true" aria-labelledby="dlg-title">
|
||||
<div class="dialog">
|
||||
<div class="dialog-head">
|
||||
<h3 id="dlg-title">补充合同 · <span id="dlg-stop-kind">装货</span> · <span id="dlg-plan-no">YP20260820001</span></h3>
|
||||
<button type="button" class="btn-link" id="dlg-close" aria-label="关闭">✕</button>
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="stop-summary">
|
||||
<div class="kv"><div class="k">计划日期</div><div class="v" id="dlg-date">2026-08-20</div></div>
|
||||
<div class="kv"><div class="k">跟进状态</div><div class="v" id="dlg-status"><span class="tag success">已安排</span></div></div>
|
||||
<div class="kv"><div class="k">本段客户</div><div class="v" id="dlg-customer">合肥某某商贸有限公司</div></div>
|
||||
<div class="kv"><div class="k">地址 / 时间</div><div class="v" id="dlg-addr">合肥市经开区某某路 88 号 · 08:30</div></div>
|
||||
</div>
|
||||
|
||||
<div class="side-box need" id="dlg-side-box">
|
||||
<div class="side-box__head">
|
||||
<h4 id="dlg-side-title">装货合同</h4>
|
||||
<span class="tag warn" id="dlg-side-tag">待补充</span>
|
||||
</div>
|
||||
<p class="side-box__customer" id="dlg-side-customer">合肥某某商贸有限公司</p>
|
||||
|
||||
<div id="dlg-exempt" class="side-box__exempt" hidden>
|
||||
内置公司(山峡/云栋),无需补充合同
|
||||
</div>
|
||||
|
||||
<div id="dlg-editor">
|
||||
<div class="contract-toolbar">
|
||||
<button type="button" class="btn btn-ghost btn-sm" id="btn-pick">🔍 选择合同</button>
|
||||
<span class="contract-hint is-warn" id="dlg-hint">请至少选择 1 份合同后保存</span>
|
||||
</div>
|
||||
<div class="contract-cards" id="dlg-cards">
|
||||
<p class="empty-hint">暂未选择合同</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-foot">
|
||||
<button type="button" class="btn btn-ghost" id="dlg-cancel">✕ 取消</button>
|
||||
<button type="button" class="btn btn-main" id="dlg-save">✓ 保存本段合同</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 选择合同(二级) -->
|
||||
<div class="selector-mask" id="selector-mask" role="dialog" aria-modal="true">
|
||||
<div class="selector-dialog">
|
||||
<div class="dialog-head">
|
||||
<h3>选择合同 · <span id="sel-customer">客户</span></h3>
|
||||
<button type="button" class="btn-link" id="sel-close">✕</button>
|
||||
</div>
|
||||
<div style="padding:12px 16px;border-bottom:1px solid #eef2f6">
|
||||
<input style="width:100%;height:36px;border:1px solid var(--border);border-radius:8px;padding:0 12px"
|
||||
placeholder="搜索合同编号 / 产品" id="sel-search" />
|
||||
</div>
|
||||
<div class="selector-list" id="sel-list"></div>
|
||||
<div class="dialog-foot">
|
||||
<button type="button" class="btn btn-ghost" id="sel-cancel">取消</button>
|
||||
<button type="button" class="btn btn-main" id="sel-confirm">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast">本段合同已保存(Demo)</div>
|
||||
|
||||
<script>
|
||||
const catalog = [
|
||||
{ id: 'c1', no: 'HT-2026-0312', type: '销售合同', status: '执行中', product: '液碱 32%', qty: '30 吨' },
|
||||
{ id: 'c2', no: 'HT-2026-0288', type: '框架合同', status: '已审批', product: '盐酸', qty: '20 吨' },
|
||||
{ id: 'c3', no: 'HT-2026-0199', type: '销售合同', status: '执行中', product: '片碱', qty: '15 吨' }
|
||||
]
|
||||
|
||||
const scenarios = {
|
||||
load1: {
|
||||
planNo: 'YP20260820001',
|
||||
stopKind: '装货',
|
||||
date: '2026-08-20',
|
||||
statusHtml: '<span class="tag success">已安排</span>',
|
||||
customer: '合肥某某商贸有限公司',
|
||||
addr: '合肥市经开区某某路 88 号 · 08:30',
|
||||
exempt: false,
|
||||
selected: []
|
||||
},
|
||||
delivery2: {
|
||||
planNo: 'YP20260819088',
|
||||
stopKind: '送货',
|
||||
date: '2026-08-19',
|
||||
statusHtml: '<span class="tag info">待安排</span>',
|
||||
customer: '芜湖某某材料有限公司',
|
||||
addr: '芜湖市弋江区某某路 · 16:00',
|
||||
exempt: false,
|
||||
selected: []
|
||||
},
|
||||
load3: {
|
||||
planNo: 'YP20260818042',
|
||||
stopKind: '装货',
|
||||
date: '2026-08-18',
|
||||
statusHtml: '<span class="tag success">运输中</span>',
|
||||
customer: '马鞍山某某化工有限公司',
|
||||
addr: '马鞍山市雨山区某某路 · 07:30',
|
||||
exempt: false,
|
||||
selected: []
|
||||
}
|
||||
}
|
||||
|
||||
let currentKey = 'load1'
|
||||
let draftIds = []
|
||||
let baselineIds = []
|
||||
let pendingPick = []
|
||||
|
||||
const mask = document.getElementById('dialog-mask')
|
||||
const selMask = document.getElementById('selector-mask')
|
||||
|
||||
function isDirty() {
|
||||
const a = [...draftIds].sort().join(',')
|
||||
const b = [...baselineIds].sort().join(',')
|
||||
return a !== b
|
||||
}
|
||||
|
||||
function renderCards() {
|
||||
const el = document.getElementById('dlg-cards')
|
||||
const hint = document.getElementById('dlg-hint')
|
||||
const box = document.getElementById('dlg-side-box')
|
||||
const tag = document.getElementById('dlg-side-tag')
|
||||
|
||||
if (!draftIds.length) {
|
||||
el.innerHTML = '<p class="empty-hint">暂未选择合同</p>'
|
||||
hint.textContent = '请至少选择 1 份合同后保存'
|
||||
hint.classList.add('is-warn')
|
||||
box.className = 'side-box need'
|
||||
tag.className = 'tag warn'
|
||||
tag.textContent = '待补充'
|
||||
return
|
||||
}
|
||||
|
||||
hint.textContent = '已选 ' + draftIds.length + ' 份合同'
|
||||
hint.classList.remove('is-warn')
|
||||
box.className = 'side-box ok'
|
||||
tag.className = 'tag success'
|
||||
tag.textContent = '已选'
|
||||
|
||||
el.innerHTML = draftIds.map(id => {
|
||||
const c = catalog.find(x => x.id === id)
|
||||
if (!c) return ''
|
||||
return `
|
||||
<article class="contract-card" data-id="${c.id}">
|
||||
<div class="contract-card__head">
|
||||
<div class="contract-card__meta">
|
||||
<span class="contract-card__no">${c.no}</span>
|
||||
<span class="tag info">${c.type}</span>
|
||||
<span class="contract-card__count">1 个产品</span>
|
||||
</div>
|
||||
<div class="contract-card__actions">
|
||||
<button type="button" class="btn-icon" title="查看" data-view="${c.id}">👁</button>
|
||||
<button type="button" class="btn-icon danger" title="移除" data-remove="${c.id}">🗑</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contract-card__body">
|
||||
<table class="mini-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>产品名称</th>
|
||||
<th>数量</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>${c.product}</td>
|
||||
<td>${c.qty}</td>
|
||||
<td>${c.status}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>`
|
||||
}).join('')
|
||||
|
||||
el.querySelectorAll('[data-remove]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
draftIds = draftIds.filter(id => id !== btn.dataset.remove)
|
||||
renderCards()
|
||||
})
|
||||
})
|
||||
el.querySelectorAll('[data-view]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const c = catalog.find(x => x.id === btn.dataset.view)
|
||||
alert('Demo:查看合同详情\\n' + (c ? c.no : ''))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function openDialog(key) {
|
||||
currentKey = key
|
||||
const s = scenarios[key] || scenarios.load1
|
||||
draftIds = [...(s.selected || [])]
|
||||
baselineIds = [...draftIds]
|
||||
|
||||
document.getElementById('dlg-stop-kind').textContent = s.stopKind
|
||||
document.getElementById('dlg-plan-no').textContent = s.planNo
|
||||
document.getElementById('dlg-date').textContent = s.date
|
||||
document.getElementById('dlg-status').innerHTML = s.statusHtml
|
||||
document.getElementById('dlg-customer').textContent = s.customer
|
||||
document.getElementById('dlg-addr').textContent = s.addr
|
||||
document.getElementById('dlg-side-title').textContent = s.stopKind + '合同'
|
||||
document.getElementById('dlg-side-customer').textContent = s.customer
|
||||
document.getElementById('sel-customer').textContent = s.customer
|
||||
|
||||
const exempt = !!s.exempt
|
||||
document.getElementById('dlg-exempt').hidden = !exempt
|
||||
document.getElementById('dlg-editor').hidden = exempt
|
||||
document.getElementById('dlg-save').style.display = exempt ? 'none' : ''
|
||||
|
||||
if (exempt) {
|
||||
document.getElementById('dlg-side-box').className = 'side-box exempt'
|
||||
document.getElementById('dlg-side-tag').className = 'tag muted'
|
||||
document.getElementById('dlg-side-tag').textContent = '可免'
|
||||
} else {
|
||||
renderCards()
|
||||
}
|
||||
|
||||
mask.classList.add('open')
|
||||
}
|
||||
|
||||
function tryCloseDialog() {
|
||||
if (isDirty() && !confirm('当前有未保存的合同变更,确认关闭吗?')) return
|
||||
mask.classList.remove('open')
|
||||
}
|
||||
|
||||
function openSelector() {
|
||||
pendingPick = [...draftIds]
|
||||
const list = document.getElementById('sel-list')
|
||||
list.innerHTML = catalog.map(c => {
|
||||
const checked = pendingPick.includes(c.id) ? 'checked' : ''
|
||||
const selected = pendingPick.includes(c.id) ? 'selected' : ''
|
||||
return `
|
||||
<label class="selector-item ${selected}">
|
||||
<input type="checkbox" value="${c.id}" ${checked} />
|
||||
<div>
|
||||
<div style="font-weight:600;color:var(--primary-dark)">${c.no}</div>
|
||||
<div class="meta-line">${c.type} · ${c.status} · ${c.product}</div>
|
||||
</div>
|
||||
</label>`
|
||||
}).join('')
|
||||
|
||||
list.querySelectorAll('input').forEach(input => {
|
||||
input.addEventListener('change', () => {
|
||||
pendingPick = [...list.querySelectorAll('input:checked')].map(i => i.value)
|
||||
list.querySelectorAll('.selector-item').forEach(item => {
|
||||
const cb = item.querySelector('input')
|
||||
item.classList.toggle('selected', cb.checked)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
selMask.classList.add('open')
|
||||
}
|
||||
|
||||
function closeSelector() {
|
||||
selMask.classList.remove('open')
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-open]').forEach(btn => {
|
||||
btn.addEventListener('click', () => openDialog(btn.dataset.open))
|
||||
})
|
||||
|
||||
document.getElementById('dlg-close').addEventListener('click', tryCloseDialog)
|
||||
document.getElementById('dlg-cancel').addEventListener('click', tryCloseDialog)
|
||||
mask.addEventListener('click', e => { if (e.target === mask) tryCloseDialog() })
|
||||
|
||||
document.getElementById('btn-pick').addEventListener('click', openSelector)
|
||||
document.getElementById('sel-close').addEventListener('click', closeSelector)
|
||||
document.getElementById('sel-cancel').addEventListener('click', closeSelector)
|
||||
selMask.addEventListener('click', e => { if (e.target === selMask) closeSelector() })
|
||||
document.getElementById('sel-confirm').addEventListener('click', () => {
|
||||
draftIds = [...pendingPick]
|
||||
closeSelector()
|
||||
renderCards()
|
||||
})
|
||||
|
||||
document.getElementById('dlg-save').addEventListener('click', () => {
|
||||
if (!draftIds.length) {
|
||||
alert('请先选择至少 1 份合同')
|
||||
return
|
||||
}
|
||||
scenarios[currentKey].selected = [...draftIds]
|
||||
baselineIds = [...draftIds]
|
||||
mask.classList.remove('open')
|
||||
const toast = document.getElementById('toast')
|
||||
toast.classList.add('show')
|
||||
setTimeout(() => toast.classList.remove('show'), 1800)
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
-- 段级归档品牌数量:扩展 pl_plan_product_brand_qty
|
||||
-- stop_product_id 为空 = 计划产品级汇总;非空 = 装货/送货段产品行上的填写值
|
||||
-- unit_price = 该品牌单价(金额 = qty * unit_price,展示时计算)
|
||||
|
||||
ALTER TABLE `pl_plan_product_brand_qty`
|
||||
ADD COLUMN `stop_type` varchar(20) DEFAULT NULL COMMENT 'load/delivery,空=计划级汇总' AFTER `plan_product_id`,
|
||||
ADD COLUMN `stop_product_id` varchar(64) DEFAULT NULL COMMENT '段产品行ID,空=计划级汇总' AFTER `stop_type`,
|
||||
ADD COLUMN `unit_price` decimal(15,4) DEFAULT NULL COMMENT '本品牌归档单价' AFTER `qty`,
|
||||
ADD KEY `idx_stop_product` (`stop_product_id`) USING BTREE;
|
||||
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<section v-if="items.length" class="pl-archive-brand-view">
|
||||
<h4 class="pl-archive-brand-view__title">品牌归档明细</h4>
|
||||
<div v-for="item in items" :key="item.key" class="pl-archive-brand-product">
|
||||
<div class="pl-archive-brand-product__head">
|
||||
<span class="pl-archive-brand-product__name">{{ item.productName }}</span>
|
||||
<span class="pl-archive-brand-product__plan">
|
||||
计划 {{ item.planQty ?? '—' }} {{ unitLabel(item.unit) }}
|
||||
</span>
|
||||
<span class="pl-archive-brand-product__segment-total">
|
||||
本段合计 {{ formatQty(item.segmentQty) }} {{ unitLabel(item.unit) }}
|
||||
</span>
|
||||
<el-tag v-if="item.brandMode === 'required'" size="small" type="warning">指定品牌</el-tag>
|
||||
<el-tag v-else-if="item.brandMode === 'any'" size="small" type="info">不限品牌</el-tag>
|
||||
</div>
|
||||
<div class="pl-archive-brand-table-wrap">
|
||||
<table class="pl-archive-brand-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>品牌</th>
|
||||
<th class="is-num">实际数量</th>
|
||||
<th class="is-num">单价(元)</th>
|
||||
<th class="is-num">金额(元)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="brandRow in item.rows" :key="brandRow.brandId">
|
||||
<td>{{ brandRow.brandName || '—' }}</td>
|
||||
<td class="is-num">{{ formatQty(brandRow.qty) }}</td>
|
||||
<td class="is-num">{{ formatPrice(brandRow.price) }}</td>
|
||||
<td class="is-num pl-archive-brand-table__amount">
|
||||
{{ formatArchiveBrandAmount(brandRow.qty, brandRow.price) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot v-if="item.rows.length > 1">
|
||||
<tr>
|
||||
<td>合计</td>
|
||||
<td class="is-num">{{ formatQty(sumQty(item.rows)) }}</td>
|
||||
<td class="is-num">—</td>
|
||||
<td class="is-num">{{ formatAmountTotal(item.rows) }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
type ArchiveBrandViewItem,
|
||||
type ArchiveBrandViewRow,
|
||||
formatArchiveBrandAmount
|
||||
} from '@/utils/pl-plan-archive-brand-view'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
|
||||
defineProps<{
|
||||
items: ArchiveBrandViewItem[]
|
||||
}>()
|
||||
|
||||
const dictStore = useDictStore()
|
||||
|
||||
function unitLabel(unit?: string) {
|
||||
const code = (unit || '').trim()
|
||||
return dictStore.getLabel('crm_product_unit', code) || code || '吨'
|
||||
}
|
||||
|
||||
function formatQty(value?: number | null) {
|
||||
if (value == null || !Number.isFinite(Number(value))) return '—'
|
||||
return Number(value).toFixed(3)
|
||||
}
|
||||
|
||||
function formatPrice(value?: number | null) {
|
||||
if (value == null || !Number.isFinite(Number(value))) return '—'
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
function sumQty(rows: ArchiveBrandViewRow[]) {
|
||||
return rows.reduce((sum, row) => sum + Number(row.qty || 0), 0)
|
||||
}
|
||||
|
||||
function formatAmountTotal(rows: ArchiveBrandViewRow[]) {
|
||||
const total = rows.reduce((sum, row) => sum + Number(row.qty || 0) * Number(row.price || 0), 0)
|
||||
return Number.isFinite(total) ? total.toFixed(2) : '—'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pl-archive-brand-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 4px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed var(--el-border-color-lighter, #e2e8f0);
|
||||
}
|
||||
|
||||
.pl-archive-brand-view__title {
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--section-bar-title-color, var(--primary-dark, #059669));
|
||||
border-left: 3px solid var(--section-bar-border-color, var(--primary, #10b981));
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.pl-archive-brand-product {
|
||||
padding: 12px;
|
||||
border: 1px solid var(--el-border-color-lighter, #e2e8f0);
|
||||
border-radius: 8px;
|
||||
background: var(--mint-bg-soft, #f8fafc);
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__head {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__plan {
|
||||
font-size: 12px;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__segment-total {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.pl-archive-brand-table-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.pl-archive-brand-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
tfoot td {
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.is-num {
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-archive-brand-table__amount {
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: #334155;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,392 @@
|
||||
<template>
|
||||
<div class="pl-plan-view-stop-block pl-plan-archive-segment-block">
|
||||
<div class="pl-plan-view-stop-meta">
|
||||
<div class="pl-plan-view-party-field">
|
||||
<span class="pl-plan-view-party-field__label">{{ customerLabel }}</span>
|
||||
<span class="pl-plan-view-party-field__value">{{ meta.customerName || '—' }}</span>
|
||||
</div>
|
||||
<div class="pl-plan-view-party-field">
|
||||
<span class="pl-plan-view-party-field__label">{{ timeLabel }}</span>
|
||||
<span class="pl-plan-view-party-field__value">{{ formatViewDate(meta.time) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PlPlanContractMultiSelect
|
||||
:model-value="meta.contracts || []"
|
||||
:customer-id="meta.customerId"
|
||||
readonly
|
||||
compact-summary
|
||||
/>
|
||||
|
||||
<PlPlanStopProductTable
|
||||
v-if="viewProducts.length"
|
||||
:products="viewProducts"
|
||||
compact
|
||||
show-stop-actual-qty
|
||||
/>
|
||||
<p v-else class="pl-plan-form-empty-hint">暂无产品</p>
|
||||
|
||||
<PlPlanStopFeedbackPanel
|
||||
v-if="showStopFeedback"
|
||||
:stop-label="feedbackStopLabel"
|
||||
:customer-name="meta.customerName"
|
||||
:generate-dibang="generateDibang"
|
||||
:dibang-rows="dibangRows"
|
||||
:driver-feedback="driverFeedback"
|
||||
:site-feedback="siteFeedback"
|
||||
:fallback-remark="fallbackRemark"
|
||||
/>
|
||||
|
||||
<section class="pl-archive-segment-form">
|
||||
<h4 class="pl-archive-segment-form__title">归档填写</h4>
|
||||
|
||||
<PlPlanFormProductTable
|
||||
v-if="products.length"
|
||||
:products="products"
|
||||
archive-compact
|
||||
show-archive-io-fields
|
||||
:archive-io-inbound-only="archiveMode === 'inbound'"
|
||||
:archive-io-outbound-only="archiveMode === 'outbound'"
|
||||
archive-segment-labels
|
||||
editable-archive-io-fields
|
||||
/>
|
||||
<p v-else class="pl-plan-form-empty-hint">暂无可归档产品</p>
|
||||
|
||||
<div
|
||||
v-for="row in brandProducts"
|
||||
:key="`brand-${row.id || row.productId}`"
|
||||
class="pl-archive-brand-product"
|
||||
>
|
||||
<div class="pl-archive-brand-product__head">
|
||||
<span class="pl-archive-brand-product__name">{{
|
||||
row.standardProductName || row.productName
|
||||
}}</span>
|
||||
<span class="pl-archive-brand-product__plan"
|
||||
>计划 {{ row.quantity ?? '—' }} {{ unitLabel(row.unit) }}</span
|
||||
>
|
||||
<span class="pl-archive-brand-product__segment-total">
|
||||
本段合计 {{ formatSegmentActualQty(row) }} {{ unitLabel(row.unit) }}
|
||||
</span>
|
||||
<el-tag v-if="row.brandMode === 'required'" size="small" type="warning">指定品牌</el-tag>
|
||||
<el-tag v-else size="small" type="info">不限品牌</el-tag>
|
||||
</div>
|
||||
<div class="pl-archive-brand-table-wrap">
|
||||
<table class="pl-archive-brand-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>品牌</th>
|
||||
<th class="is-num">实际数量</th>
|
||||
<th class="is-num">单价(元)</th>
|
||||
<th class="is-num">金额(元)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="brandRow in activeBrandRows(row)" :key="brandRow.brandId">
|
||||
<td>{{ brandRow.brandName }}</td>
|
||||
<td class="is-num">
|
||||
<el-input-number
|
||||
v-model="brandRow.qty"
|
||||
:min="0"
|
||||
:precision="3"
|
||||
:controls="false"
|
||||
@change="() => syncProductFromBrandRows(row)"
|
||||
/>
|
||||
</td>
|
||||
<td class="is-num">
|
||||
<el-input-number
|
||||
v-model="brandRow.price"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
@change="() => syncProductFromBrandRows(row)"
|
||||
/>
|
||||
</td>
|
||||
<td class="is-num pl-archive-brand-table__amount">
|
||||
{{ formatBrandRowAmount(brandRow) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot v-if="activeBrandRows(row).length > 1">
|
||||
<tr>
|
||||
<td>合计</td>
|
||||
<td class="is-num">{{ formatBrandQtyTotal(row) }}</td>
|
||||
<td class="is-num">—</td>
|
||||
<td class="is-num">{{ formatBrandAmountTotal(row) }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<BrandQtyChipsField
|
||||
v-if="row.brandMode === 'any' && (row.brandOptions || []).length > 1"
|
||||
v-model="row.brandQtyRows"
|
||||
brand-mode="any"
|
||||
:brand-options="row.brandOptions || []"
|
||||
:unit="unitLabel(row.unit)"
|
||||
variant="admin"
|
||||
@update:model-value="() => syncProductFromBrandRows(row)"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { TransportStopFeedback } from '@/api/pl'
|
||||
import BrandQtyChipsField from '@/components/business/brand-qty-chips-field/index.vue'
|
||||
import PlPlanContractMultiSelect from '@/components/business/pl-plan-contract-multi-select/index.vue'
|
||||
import PlPlanFormProductTable from '@/components/business/pl-plan-form-dialog/PlPlanFormProductTable.vue'
|
||||
import PlPlanStopFeedbackPanel from '@/components/business/pl-plan-stop-feedback-panel/index.vue'
|
||||
import PlPlanStopProductTable from '@/components/business/pl-plan-stop-product-table/index.vue'
|
||||
import type { PlPlanTransportDibangRow } from '@/composables/usePlPlanViewDetail'
|
||||
import { type PlanFormProductRow } from '@/composables/usePlContractToPlan'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import type { PlanStopProductFormRow } from '@/utils/pl-plan-stop-form'
|
||||
|
||||
export interface ArchiveSegmentMeta {
|
||||
customerId?: string
|
||||
customerName?: string
|
||||
time?: string
|
||||
contracts?: Array<{ contractId: string; contractNo?: string }>
|
||||
}
|
||||
|
||||
export interface ArchiveBrandQtyRow {
|
||||
brandId: string
|
||||
brandName: string
|
||||
qty: number
|
||||
price?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
stopType: 'load' | 'delivery'
|
||||
meta: ArchiveSegmentMeta
|
||||
viewProducts: PlanStopProductFormRow[]
|
||||
products: PlanFormProductRow[]
|
||||
archiveMode: 'inbound' | 'outbound'
|
||||
showStopFeedback?: boolean
|
||||
feedbackStopLabel?: string
|
||||
generateDibang?: number
|
||||
dibangRows?: PlPlanTransportDibangRow[]
|
||||
driverFeedback?: TransportStopFeedback | null
|
||||
siteFeedback?: TransportStopFeedback | null
|
||||
fallbackRemark?: string | null
|
||||
}>()
|
||||
|
||||
const dictStore = useDictStore()
|
||||
|
||||
const customerLabel = computed(() => (props.stopType === 'load' ? '装货客户' : '送货客户'))
|
||||
const timeLabel = computed(() => (props.stopType === 'load' ? '装货时间' : '送货时间'))
|
||||
const feedbackStopLabel = computed(
|
||||
() => props.feedbackStopLabel || (props.stopType === 'load' ? '装货' : '送货')
|
||||
)
|
||||
|
||||
const brandProducts = computed(() =>
|
||||
(props.products || []).filter((row) => row.brandMode === 'any' || row.brandMode === 'required')
|
||||
)
|
||||
|
||||
function formatSegmentActualQty(row: PlanFormProductRow) {
|
||||
const qty =
|
||||
props.archiveMode === 'inbound'
|
||||
? row.inboundQty != null
|
||||
? Number(row.inboundQty)
|
||||
: null
|
||||
: row.outboundQty != null
|
||||
? Number(row.outboundQty)
|
||||
: null
|
||||
return qty != null && Number.isFinite(qty) ? qty.toFixed(3) : '—'
|
||||
}
|
||||
|
||||
function formatViewDate(value?: string | null) {
|
||||
const text = (value || '').trim()
|
||||
if (!text) return '—'
|
||||
return text.length >= 10 ? text.slice(0, 10) : text
|
||||
}
|
||||
|
||||
function unitLabel(unit?: string) {
|
||||
const code = (unit || '').trim()
|
||||
return dictStore.getLabel('crm_product_unit', code) || code || '吨'
|
||||
}
|
||||
|
||||
function activeBrandRows(row: PlanFormProductRow): ArchiveBrandQtyRow[] {
|
||||
return (row.brandQtyRows || []) as ArchiveBrandQtyRow[]
|
||||
}
|
||||
|
||||
function formatBrandRowAmount(brandRow: ArchiveBrandQtyRow) {
|
||||
if (brandRow.qty == null || brandRow.price == null) return '—'
|
||||
const amount = Number(brandRow.qty) * Number(brandRow.price)
|
||||
return Number.isFinite(amount) ? amount.toFixed(2) : '—'
|
||||
}
|
||||
|
||||
function formatBrandQtyTotal(row: PlanFormProductRow) {
|
||||
const total = activeBrandRows(row).reduce((sum, item) => sum + Number(item.qty || 0), 0)
|
||||
return total.toFixed(3)
|
||||
}
|
||||
|
||||
function formatBrandAmountTotal(row: PlanFormProductRow) {
|
||||
const total = activeBrandRows(row).reduce(
|
||||
(sum, item) => sum + Number(item.qty || 0) * Number(item.price || 0),
|
||||
0
|
||||
)
|
||||
return total.toFixed(2)
|
||||
}
|
||||
|
||||
function syncProductFromBrandRows(row: PlanFormProductRow) {
|
||||
const rows = activeBrandRows(row)
|
||||
if (!rows.length) return
|
||||
const totalQty = rows.reduce((sum, item) => sum + Number(item.qty || 0), 0)
|
||||
const totalAmount = rows.reduce(
|
||||
(sum, item) => sum + Number(item.qty || 0) * Number(item.price || 0),
|
||||
0
|
||||
)
|
||||
const avgPrice = totalQty > 0 ? totalAmount / totalQty : undefined
|
||||
if (props.archiveMode === 'inbound') {
|
||||
row.inboundQty = totalQty
|
||||
row.inboundPrice = avgPrice
|
||||
} else {
|
||||
row.outboundQty = totalQty
|
||||
row.outboundPrice = avgPrice
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pl-plan-archive-segment-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pl-plan-view-stop-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px 16px;
|
||||
}
|
||||
|
||||
.pl-plan-view-party-field {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 0 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pl-plan-view-party-field__label {
|
||||
font-size: 13px;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pl-plan-view-party-field__value {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
word-break: break-word;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.pl-plan-form-empty-hint {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
}
|
||||
|
||||
.pl-archive-segment-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding-top: 4px;
|
||||
border-top: 1px solid var(--el-border-color-lighter, #e2e8f0);
|
||||
}
|
||||
|
||||
.pl-archive-segment-form__title {
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--section-bar-title-color, var(--primary-dark, #059669));
|
||||
border-left: 3px solid var(--section-bar-border-color, var(--primary, #10b981));
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.pl-archive-brand-product {
|
||||
padding: 12px;
|
||||
border: 1px solid var(--el-border-color-lighter, #e2e8f0);
|
||||
border-radius: 8px;
|
||||
background: var(--mint-bg-soft, #f8fafc);
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__head {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__plan {
|
||||
font-size: 12px;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
}
|
||||
|
||||
.pl-archive-brand-product__segment-total {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.pl-archive-brand-table-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.pl-archive-brand-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
tfoot td {
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.is-num {
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.el-input-number) {
|
||||
width: 120px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
:deep(.el-input-number .el-input__inner) {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-archive-brand-table__amount {
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: #334155;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="pl-plan-contract-cell">
|
||||
<div
|
||||
v-for="(line, index) in lines"
|
||||
:key="`${line.side}-${line.contractId || line.no}-${index}`"
|
||||
class="pl-plan-contract-cell__row"
|
||||
>
|
||||
<span class="pl-plan-contract-cell__side">{{ line.side }}</span>
|
||||
<el-link
|
||||
v-if="isClickable(line)"
|
||||
type="primary"
|
||||
class="pl-plan-contract-cell__no pl-plan-contract-cell__link"
|
||||
@click.stop="emit('open', line.contractId!)"
|
||||
>
|
||||
{{ line.no }}
|
||||
</el-link>
|
||||
<span v-else class="pl-plan-contract-cell__no" :class="{ 'is-pending': line.pending }">
|
||||
{{ line.no }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { PlanContractDisplayLine } from '@/utils/pl-plan-contract-display'
|
||||
|
||||
defineProps<{
|
||||
lines: PlanContractDisplayLine[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
open: [contractId: string]
|
||||
}>()
|
||||
|
||||
function isClickable(line: PlanContractDisplayLine) {
|
||||
return !!line.contractId && !line.pending && line.no !== '—'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pl-plan-contract-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pl-plan-contract-cell__row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: var(--text, #1e293b);
|
||||
}
|
||||
|
||||
.pl-plan-contract-cell__side {
|
||||
flex-shrink: 0;
|
||||
font-weight: 600;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
}
|
||||
|
||||
.pl-plan-contract-cell__no {
|
||||
min-width: 0;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
&.is-pending {
|
||||
font-weight: 600;
|
||||
color: #d97706;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-plan-contract-cell__link {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
import { defineAsyncComponent, nextTick, ref, watch } from 'vue'
|
||||
|
||||
export const ContractDetailDialogAsync = defineAsyncComponent(
|
||||
() => import('@/views/crm/contract/components/ContractDetailDialog.vue')
|
||||
)
|
||||
|
||||
type ContractDetailDialogExpose = {
|
||||
open: (id: string | number) => Promise<void>
|
||||
}
|
||||
|
||||
export function useContractDetailDialog() {
|
||||
const contractDetailRef = ref<ContractDetailDialogExpose>()
|
||||
|
||||
async function waitForContractDetailDialog() {
|
||||
if (contractDetailRef.value) return contractDetailRef.value
|
||||
await nextTick()
|
||||
if (contractDetailRef.value) return contractDetailRef.value
|
||||
return new Promise<ContractDetailDialogExpose>((resolve) => {
|
||||
const stop = watch(
|
||||
contractDetailRef,
|
||||
(val) => {
|
||||
if (val) {
|
||||
stop()
|
||||
resolve(val)
|
||||
}
|
||||
},
|
||||
{ flush: 'post' }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
async function openContractDetail(contractId: string | number | undefined | null) {
|
||||
const id = String(contractId ?? '').trim()
|
||||
if (!id) {
|
||||
ElMessage.warning('暂无关联合同')
|
||||
return
|
||||
}
|
||||
const dialog = await waitForContractDetailDialog()
|
||||
await dialog.open(id)
|
||||
}
|
||||
|
||||
return {
|
||||
ContractDetailDialog: ContractDetailDialogAsync,
|
||||
contractDetailRef,
|
||||
openContractDetail
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { ref, watch, type Ref } from 'vue'
|
||||
|
||||
/** 是否为可提交的完整非负小数(空字符串视为清空) */
|
||||
export function isCompleteDecimalInput(raw: string): boolean {
|
||||
const trimmed = raw.trim()
|
||||
return trimmed === '' || /^\d+(\.\d+)?$/.test(trimmed)
|
||||
}
|
||||
|
||||
export function parseDecimalInput(raw: string, precision?: number): number | null {
|
||||
const trimmed = raw.trim()
|
||||
if (!trimmed) return null
|
||||
if (!/^\d+(\.\d+)?$/.test(trimmed)) return null
|
||||
const num = Number(trimmed)
|
||||
if (!Number.isFinite(num) || num < 0) return null
|
||||
if (precision == null) return num
|
||||
return Number(num.toFixed(precision))
|
||||
}
|
||||
|
||||
function formatDecimalDisplay(val: number, precision?: number): string {
|
||||
if (precision == null) return String(val)
|
||||
return Number(val.toFixed(precision)).toString()
|
||||
}
|
||||
|
||||
function sanitizeDecimalText(raw: string): string {
|
||||
let text = raw.replace(/[^\d.]/g, '')
|
||||
const dotIndex = text.indexOf('.')
|
||||
if (dotIndex >= 0) {
|
||||
text = text.slice(0, dotIndex + 1) + text.slice(dotIndex + 1).replace(/\./g, '')
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 number | null 与 el-input 文本双向绑定;
|
||||
* 仅在完整小数或清空时写入 source,避免打断输入,同时保证运费计算 watch 能触发。
|
||||
*/
|
||||
export function useDecimalInputBinding(
|
||||
source: Ref<number | null>,
|
||||
options?: { precision?: number; onCommit?: () => void }
|
||||
) {
|
||||
const text = ref('')
|
||||
|
||||
watch(
|
||||
source,
|
||||
(val) => {
|
||||
const next =
|
||||
val == null || Number.isNaN(Number(val))
|
||||
? ''
|
||||
: formatDecimalDisplay(Number(val), options?.precision)
|
||||
if (text.value !== next) text.value = next
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function commitFromText(raw: string, force = false) {
|
||||
const sanitized = sanitizeDecimalText(raw)
|
||||
text.value = sanitized
|
||||
if (!force && !isCompleteDecimalInput(sanitized)) return
|
||||
const parsed = parseDecimalInput(sanitized, options?.precision)
|
||||
const prev = source.value
|
||||
const changed =
|
||||
parsed == null ? prev != null : prev == null || Math.abs(Number(prev) - parsed) > 1e-9
|
||||
source.value = parsed
|
||||
if (parsed != null) {
|
||||
text.value = formatDecimalDisplay(parsed, options?.precision)
|
||||
} else if (force) {
|
||||
text.value = ''
|
||||
}
|
||||
if (changed) options?.onCommit?.()
|
||||
}
|
||||
|
||||
function handleInput(val: string) {
|
||||
commitFromText(val, false)
|
||||
}
|
||||
|
||||
function handleBlur() {
|
||||
commitFromText(text.value, true)
|
||||
}
|
||||
|
||||
return { text, handleInput, handleBlur }
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import type { PlanFormProductRow } from '@/composables/usePlContractToPlan'
|
||||
import type { PlanStopProductFormRow } from '@/utils/pl-plan-stop-form'
|
||||
|
||||
export interface ArchiveBrandViewRow {
|
||||
brandId: string
|
||||
brandName: string
|
||||
qty: number
|
||||
price?: number
|
||||
amount?: number
|
||||
}
|
||||
|
||||
export interface ArchiveBrandViewItem {
|
||||
key: string
|
||||
productName: string
|
||||
unit?: string
|
||||
planQty?: number
|
||||
segmentQty: number
|
||||
brandMode?: string
|
||||
rows: ArchiveBrandViewRow[]
|
||||
}
|
||||
|
||||
function getSegmentQty(
|
||||
row: PlanStopProductFormRow,
|
||||
mode: 'inbound' | 'outbound',
|
||||
planRow?: PlanFormProductRow
|
||||
): number {
|
||||
const primary = mode === 'inbound' ? row.inboundQty : row.outboundQty
|
||||
if (primary != null && Number.isFinite(Number(primary))) return Number(primary)
|
||||
const fallback = mode === 'inbound' ? planRow?.inboundQty : planRow?.outboundQty
|
||||
if (fallback != null && Number.isFinite(Number(fallback))) return Number(fallback)
|
||||
return 0
|
||||
}
|
||||
|
||||
function getSegmentPrice(
|
||||
row: PlanStopProductFormRow,
|
||||
mode: 'inbound' | 'outbound',
|
||||
planRow?: PlanFormProductRow
|
||||
): number | undefined {
|
||||
const primary = mode === 'inbound' ? row.inboundPrice : row.outboundPrice
|
||||
if (primary != null && Number.isFinite(Number(primary))) return Number(primary)
|
||||
const fallback = mode === 'inbound' ? planRow?.inboundPrice : planRow?.outboundPrice
|
||||
if (fallback != null && Number.isFinite(Number(fallback))) return Number(fallback)
|
||||
if (planRow?.taxPrice != null && Number.isFinite(Number(planRow.taxPrice))) {
|
||||
return Number(planRow.taxPrice)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function getPlanBrandRows(planRow?: PlanFormProductRow) {
|
||||
return (planRow?.brandQtyRows || []).filter((row) => Number(row.qty) > 0)
|
||||
}
|
||||
|
||||
function getStopBrandRows(stopRow: PlanStopProductFormRow) {
|
||||
return (stopRow.brandQtys || []).filter((row) => Number(row.qty) > 0)
|
||||
}
|
||||
|
||||
function getAllocationMap(
|
||||
allocations: Map<string, Map<string, number>>,
|
||||
planProductId: string
|
||||
): Map<string, number> {
|
||||
let map = allocations.get(planProductId)
|
||||
if (!map) {
|
||||
map = new Map()
|
||||
allocations.set(planProductId, map)
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
/** 按段顺序贪心分配计划级品牌量(旧数据无段级品牌明细时回退) */
|
||||
function allocateBrandRows(
|
||||
brandRows: Array<{ brandId: string; brandName: string; qty: number }>,
|
||||
segmentQty: number,
|
||||
price: number | undefined,
|
||||
allocated: Map<string, number>
|
||||
): ArchiveBrandViewRow[] {
|
||||
if (segmentQty <= 0 || !brandRows.length) return []
|
||||
let remaining = segmentQty
|
||||
const result: ArchiveBrandViewRow[] = []
|
||||
for (const brand of brandRows) {
|
||||
const total = Number(brand.qty || 0)
|
||||
if (total <= 0) continue
|
||||
const used = allocated.get(brand.brandId) || 0
|
||||
const available = total - used
|
||||
if (available <= 0.0005) continue
|
||||
const take = Math.min(available, remaining)
|
||||
if (take <= 0.0005) continue
|
||||
allocated.set(brand.brandId, used + take)
|
||||
remaining -= take
|
||||
result.push({
|
||||
brandId: brand.brandId,
|
||||
brandName: brand.brandName,
|
||||
qty: take,
|
||||
price,
|
||||
amount: price != null ? take * price : undefined
|
||||
})
|
||||
if (remaining <= 0.0005) break
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function buildRowsFromStopBrandQtys(
|
||||
stopRow: PlanStopProductFormRow,
|
||||
fallbackPrice: number | undefined
|
||||
): ArchiveBrandViewRow[] {
|
||||
return getStopBrandRows(stopRow).map((brand) => {
|
||||
const qty = Number(brand.qty || 0)
|
||||
const price =
|
||||
brand.unitPrice != null && Number.isFinite(Number(brand.unitPrice))
|
||||
? Number(brand.unitPrice)
|
||||
: fallbackPrice
|
||||
return {
|
||||
brandId: String(brand.brandId || ''),
|
||||
brandName: String(brand.brandName || ''),
|
||||
qty,
|
||||
price,
|
||||
amount: price != null ? qty * price : undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function buildArchiveBrandViewItems(
|
||||
stopProducts: PlanStopProductFormRow[],
|
||||
planProducts: PlanFormProductRow[],
|
||||
mode: 'inbound' | 'outbound',
|
||||
allocations: Map<string, Map<string, number>>
|
||||
): ArchiveBrandViewItem[] {
|
||||
const planById = new Map(planProducts.filter((row) => row.id).map((row) => [String(row.id), row]))
|
||||
const planByProductId = new Map(planProducts.map((row) => [String(row.productId), row]))
|
||||
const items: ArchiveBrandViewItem[] = []
|
||||
|
||||
for (const stopRow of stopProducts) {
|
||||
const planRow =
|
||||
(stopRow.planProductId ? planById.get(String(stopRow.planProductId)) : undefined) ||
|
||||
planByProductId.get(String(stopRow.productId))
|
||||
const brandMode = stopRow.brandMode || planRow?.brandMode
|
||||
if (brandMode !== 'any' && brandMode !== 'required') continue
|
||||
|
||||
const planProductId = String(stopRow.planProductId || planRow?.id || '').trim()
|
||||
if (!planProductId) continue
|
||||
|
||||
const segmentQty = getSegmentQty(stopRow, mode, planRow)
|
||||
if (segmentQty <= 0) continue
|
||||
const price = getSegmentPrice(stopRow, mode, planRow)
|
||||
|
||||
// 优先使用段级归档时填写的品牌数量
|
||||
let rows = buildRowsFromStopBrandQtys(stopRow, price)
|
||||
if (!rows.length) {
|
||||
const brandRows = getPlanBrandRows(planRow)
|
||||
if (!brandRows.length) continue
|
||||
const allocated = getAllocationMap(allocations, planProductId)
|
||||
rows = allocateBrandRows(brandRows, segmentQty, price, allocated)
|
||||
}
|
||||
if (!rows.length) continue
|
||||
|
||||
items.push({
|
||||
key: `${planProductId}-${stopRow.id || stopRow.productId}`,
|
||||
productName:
|
||||
stopRow.standardProductName ||
|
||||
stopRow.productName ||
|
||||
planRow?.standardProductName ||
|
||||
planRow?.productName ||
|
||||
'—',
|
||||
unit: stopRow.unit || planRow?.unit,
|
||||
planQty: stopRow.planQty ?? planRow?.quantity,
|
||||
segmentQty,
|
||||
brandMode,
|
||||
rows
|
||||
})
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
export function buildArchiveBrandViewsBySegment(input: {
|
||||
loadProducts: PlanStopProductFormRow[]
|
||||
deliveries: Array<{ key: string; products: PlanStopProductFormRow[] }>
|
||||
planProducts: PlanFormProductRow[]
|
||||
}): Map<string, ArchiveBrandViewItem[]> {
|
||||
const result = new Map<string, ArchiveBrandViewItem[]>()
|
||||
// 装货(入库)与送货(出库)各自独立分配池,互不占位(仅旧数据回退贪心时使用)
|
||||
const inboundAllocations = new Map<string, Map<string, number>>()
|
||||
const outboundAllocations = new Map<string, Map<string, number>>()
|
||||
|
||||
result.set(
|
||||
'load',
|
||||
buildArchiveBrandViewItems(
|
||||
input.loadProducts,
|
||||
input.planProducts,
|
||||
'inbound',
|
||||
inboundAllocations
|
||||
)
|
||||
)
|
||||
|
||||
for (const delivery of input.deliveries) {
|
||||
result.set(
|
||||
delivery.key,
|
||||
buildArchiveBrandViewItems(
|
||||
delivery.products,
|
||||
input.planProducts,
|
||||
'outbound',
|
||||
outboundAllocations
|
||||
)
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function formatArchiveBrandAmount(qty?: number, price?: number): string {
|
||||
if (qty == null || price == null) return '—'
|
||||
const amount = Number(qty) * Number(price)
|
||||
return Number.isFinite(amount) ? amount.toFixed(2) : '—'
|
||||
}
|
||||
Reference in new issue
Block a user