diff --git a/backend/app/services/pl_quota_planning.py b/backend/app/services/pl_quota_planning.py index 11facf37e..de9ea2749 100644 --- a/backend/app/services/pl_quota_planning.py +++ b/backend/app/services/pl_quota_planning.py @@ -351,7 +351,11 @@ def get_plan_quota(db, user, plan_id): ), 'usages': []} for usage in usages: - _get_row(db, user, usage.cert_type, usage.cert_id) + cert = _get_row(db, user, usage.cert_type, usage.cert_id) items = db.query(PlPlanCertUsageItem).filter_by(tenant_no=user.tenant_no, usage_id=usage.id, del_flag=0).order_by(PlPlanCertUsageItem.allocation_seq).all() - result['usages'].append({**_row_to_dict(usage), 'items': [_row_to_dict(row) for row in items]}) + payload = {**_row_to_dict(usage), 'items': [_row_to_dict(row) for row in items]} + # 详情展示证面附件:用量行本身无附件字段,从证件档案回填 + payload['attachmentUrls'] = getattr(cert, 'attachment_urls', None) or '' + payload['certNo'] = payload.get('certNo') or getattr(cert, 'cert_no', None) or usage.cert_no_snapshot + result['usages'].append(payload) return result diff --git a/backend/tests/test_pl_quota_sources.py b/backend/tests/test_pl_quota_sources.py index 8156964c3..b45d3f484 100644 --- a/backend/tests/test_pl_quota_sources.py +++ b/backend/tests/test_pl_quota_sources.py @@ -317,3 +317,39 @@ def test_missing_contract_is_empty_binding_not_unreadable(case, contract_id): assert rows[0]['bindingsReadable'] is True assert rows[0]['certificates'] == [] assert rows[1]['bindingsReadable'] is False + + +def test_plan_quota_usages_include_cert_attachment_urls(case): + from app.models.crm_models import CrmContract + from app.models.pl_quota_models import ( + PlPlanCertUsage, PlPlanCertUsageItem, PlPurchaseCert, PlTransportCert, + ) + from app.services.pl_quota_planning import get_plan_quota + c = case + for model in (CrmContract, PlPlanCertUsage, PlPlanCertUsageItem, PlPurchaseCert, PlTransportCert): + model.__table__.create(c.db.bind) + c.db.add(PlPurchaseCert( + id='p1', cert_no='G-BUY', product_name_on_cert='硫酸', valid_use_text='一次有效', + buyer_name='买方', seller_name='卖方', total_qty=33, quantity_unit='吨', + attachment_urls='https://example.com/buy.jpg', tenant_no='t', status='active', + )) + c.db.add(PlTransportCert( + id='t1', cert_no='Y-TR', product_name_on_cert='硫酸', valid_use_text='多次有效', + shipper_name='货主', total_qty=70, quantity_unit='吨', vehicle_numbers_json=['皖A1'], + attachment_urls='', tenant_no='t', status='active', + )) + c.db.add(PlPlanCertUsage( + id='u1', tenant_no='t', plan_id='plan', plan_no_snapshot='PLAN', + cert_type='purchase', cert_id='p1', cert_no_snapshot='G-BUY', cert_revision=1, + quantity_unit='吨', reserved_qty=10, used_qty=0, usage_status='reserved', + )) + c.db.add(PlPlanCertUsage( + id='u2', tenant_no='t', plan_id='plan', plan_no_snapshot='PLAN', + cert_type='transport', cert_id='t1', cert_no_snapshot='Y-TR', cert_revision=1, + quantity_unit='吨', reserved_qty=10, used_qty=0, usage_status='reserved', + )) + c.db.flush() + usages = {u['certId']: u for u in get_plan_quota(c.db, c.user, 'plan')['usages']} + assert usages['p1']['attachmentUrls'] == 'https://example.com/buy.jpg' + assert usages['p1']['certNo'] == 'G-BUY' + assert usages['t1']['attachmentUrls'] in ('', None) diff --git a/web-admin/scripts/plan-detail-linked-certs.test.ts b/web-admin/scripts/plan-detail-linked-certs.test.ts new file mode 100644 index 000000000..c05958648 --- /dev/null +++ b/web-admin/scripts/plan-detail-linked-certs.test.ts @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const viewTabs = readFileSync( + new URL('../src/components/business/pl-plan-view-tabs/index.vue', import.meta.url), + 'utf8' +) +const h5Detail = readFileSync( + new URL('../src/views/m/pl/plan/detail.vue', import.meta.url), + 'utf8' +) + +test('Web 计划详情:关联证件在装送货后且无危化品证书信息区', () => { + assert.doesNotMatch(viewTabs, /危化品证书信息/) + assert.doesNotMatch(viewTabs, /PlPlanHazardCertViewTable/) + const loadIdx = viewTabs.indexOf('装送货信息') + const certIdx = viewTabs.indexOf('关联购买证 / 运输证') + const freightIdx = viewTabs.indexOf('运费信息') + assert.ok(loadIdx >= 0 && certIdx > loadIdx && freightIdx > certIdx) + assert.match(viewTabs, /AttachmentUploadField/) + assert.match(viewTabs, /attachmentUrls/) +}) + +test('H5 计划详情:关联购买证/运输证替换危化品证书信息并展示附件', () => { + assert.doesNotMatch(h5Detail, /危化品证书信息/) + assert.match(h5Detail, /关联购买证 \/ 运输证/) + assert.match(h5Detail, /关联证件/) + const loadIdx = h5Detail.indexOf('plan-sec-load') + const certIdx = h5Detail.indexOf('plan-sec-cert') + const freightIdx = h5Detail.indexOf('plan-sec-freight') + assert.ok(loadIdx >= 0 && certIdx > loadIdx && freightIdx > certIdx) + assert.match(h5Detail, /AttachmentUploadField|CertAttachments/) + assert.match(h5Detail, /attachmentUrls/) +}) diff --git a/web-admin/src/components/business/pl-plan-view-tabs/index.vue b/web-admin/src/components/business/pl-plan-view-tabs/index.vue index f1d7c282a..3502a9e8b 100644 --- a/web-admin/src/components/business/pl-plan-view-tabs/index.vue +++ b/web-admin/src/components/business/pl-plan-view-tabs/index.vue @@ -71,23 +71,6 @@
暂无车辆信息
-- 证书齐备(待完善):请到「证书齐备 → 已标记待完善证书」挂证并完善并关闭 -
-暂无关联证件
-+ 证书齐备(待完善):请到「证书齐备 → 已标记待完善证书」挂证并完善并关闭 +
+暂无关联证件
+ 证书齐备(待完善):请到「证书齐备 → 已标记待完善证书」挂证并完善并关闭 +
+未上传
+暂无关联证件