Catalogue & Contracts Back-office PRD

日常业务与测试阅读请使用Catalogue 中文业务 PRD。本页保留工程细节、历史变更与取数参考;已知冲突见实现记录

Canonical product contract · Confirmed through 2026-09-03 · Owner: Operations / Finance / Insurance / Platform

1. Outcome

Catalogue & Contracts is the governed maintenance centre for commercial master data consumed by Consultation, Billing, Packages, Benefits and Inventory. It replaces the placeholder admin-master-data surface while retaining that App ID.

The current iteration changes the frontend structure and may adapt existing APIs, but must not introduce or migrate underlying catalogue pricing tables. Every missing persistence model is recorded in the Backend Adaptation Ledger in section 12 and must not be presented as completed.

2. Confirmed navigation

  1. Service Items
  2. Lab & Imaging
  3. Products
  4. Consultation Pricing
  5. Service Packages
  6. Coupons
  7. Insurance Cards
  8. Other Settings
  9. Change History & Controls

Fees & Offerings is hidden from current-phase navigation. Its code and data remain available for compatibility and migration analysis; it is not repurposed as a Discount page and is not deleted.

There is no Overview or explanatory bridge page. Insurance & Benefit Programmes opens card management directly.

3. Scope and ownership

Area Current-phase behavior Delivery state
Service Items Domain-specific list/editor, filters, shared List Price/Cost, clinic availability multi-select Frontend implemented; clinic availability persistence pending
Lab & Imaging One Test Catalogue; click Test Item to inspect Vendor mappings and Cost Schedule Frontend implemented; mapping/cost persistence pending
Products Domain-specific list/editor, filters, shared List Price/Cost, clinic availability multi-select Frontend implemented; clinic availability persistence pending
Consultation Pricing One doctor Standard Price, Clinic exceptions and Insurance rates Frontend implemented; all price persistence/resolution pending
Service Packages Open authoritative Package Setup Existing workspace; bulk-import requirement to be added there
Coupons Campaign CRUD/lifecycle and Excel import Foundation implemented; Cashier redemption pending
Insurance Cards Direct card gallery, single-card create/edit, private front/back image upload and source preview; insurance cards reference Policy while enterprise cooperation cards reference eligible Benefit Contracts Implemented on existing policy_card plus metadata reference
Policies Manage Policies opens a wide in-page Drawer; simple create/edit/import Implemented on existing policy_contract
Complex Benefit Contracts Administration workspace retained but hidden; existing corporate_membership / staff_benefit records are selectable from Insurance Card master Advanced administration deferred; source selection included
Patient-held insurance Patient & eMR owns member number, personal policy number, validity, primary flag, evidence and notes Existing patient-scoped feature; not duplicated here

4. Shared catalogue rules

4.1 Canonical identity

Service, Lab, Imaging and Product share clinic_service_items.id. item_kind controls the domain view:

Drug Master remains separate because dosage, route, interaction, controlled-drug and dispensing rules are materially different.

4.2 Lifecycle and historical integrity

4.3 List Price and Cost

List Price and Cost are separate concepts and separate columns:

For Service and Product, both values are unified across clinics. Clinic selection controls availability only and cannot override either price.

For Lab & Imaging, List Price is unified per Test Item. Cost varies by Vendor, Clinic and Doctor according to section 6; Clinic never changes List Price.

5. Service Items

5.1 List

Columns:

Service Code, Service Name, Category, List Price, Cost, Available Clinics, Status, Actions.

Filters:

5.2 Editor

Fields include Service Code, Service Name, Category, List Price, Cost, Available Clinics, Description and lifecycle metadata.

Available Clinics is a searchable multi-select dropdown with checkbox rows, All Clinics, select-all and clear behavior. A new Service defaults to All Clinics. An explicit empty selection means unavailable everywhere; it must never be silently interpreted as All Clinics.

6. Lab & Imaging — Test Catalogue

6.1 Page structure

The area is one Test Catalogue page. The previous four-tab proposal is superseded. Request Form Mapping is removed from this workspace.

The main table has one row per canonical Test Item:

Unique Code, Test Item Name, Category, Type, List Price, Vendors, Cost Coverage, Status, Actions.

Filters cover search, Category, Lab/Imaging type, Vendor and Status.

Lab & Imaging Test Items are available to all clinics by definition, so this area has no Available Clinics field or filter.

6.2 Test Item detail Drawer

Clicking a row opens a wide right-side Drawer. It is a detail surface, not a second table permanently squeezed beside the catalogue.

The Drawer contains:

  1. Basic Test Item information and unified List Price.
  2. Vendor & Code Mapping: Vendor, Vendor Item Name, Vendor Code, Vendor Default Cost, lifecycle/effective information.
  3. Clinic / Doctor Cost Schedule: Vendor, Clinic, optional Doctor, Cost, effective information and Status.

One Test Item may be offered by multiple Vendors. Vendor Code and Vendor Cost belong to a specific Vendor mapping, not to the canonical Test Item row.

6.3 Diagnostic cost key and resolution

The Cost key is:

Test Item + Vendor + Clinic + Doctor(optional).

Resolution order:

  1. Doctor + Clinic + Vendor;
  2. Clinic + Vendor;
  3. Vendor Default Cost;
  4. Cost unavailable.

Different Clinics cannot define a different List Price. Different Clinics and Doctors may define a Cost override only.

7. Products

Columns:

SKU / Code, Product Name, Category, List Price, Cost, Available Clinics, Inventory Tracking, Status, Actions.

Filters cover search, Category, Available Clinic, Inventory Tracking and Status.

The editor supports SKU, barcode, unit of measure and Inventory Tracking in addition to the shared fields. Availability uses the same searchable checkbox multi-select as Service Items. Quantity, lots, purchasing and stock movement remain in Inventory.

8. Consultation Pricing

8.1 Main list

The label is Consultation Pricing, not Doctor Consultation Fees.

Columns:

Doctor, Standard Price, Clinic Exceptions, Insurance Rates, Status, Actions.

Every doctor has one Standard Consultation Price. It is the doctor's base consultation price across consultation types and clinics.

8.2 Exceptions

Resolution order:

  1. Doctor + Policy + Clinic;
  2. Doctor + Policy;
  3. Doctor + Clinic;
  4. Doctor Standard Price;
  5. Default consultation price: HKD 1,000.00 (confirmed 2026-09-08).

The current backend has no authoritative model or Billing resolver for this contract. All edit/import actions in this section remain explicitly marked pending until section 12 is implemented.

The executable interim fallback is in ordinary Consultation Billing & Finish: an unbound Visit receives the server-owned HKD 1,000.00 default, while an explicit valid Service Offering remains authoritative, including a zero price. An explicitly bound invalid/expired/cross-clinic/non-HKD Offering still requires correction; other charge lines never inherit this default. The applied default code/version is retained in existing signed snapshots, and signed history is not repriced. This does not implement the pending doctor-standard and Policy/Clinic exception tables or their editing/import UI.

9. Coupons

Coupon Campaign supports code/name, discount type/value, minimum spend, optional maximum discount, dates, redemption limits, target, description and stacking.

Coupon does not stack with a Contract or Benefit discount unless stackable = true is explicitly configured.

Campaign maintenance and import do not imply Cashier redemption is integrated. Cashier must eventually store the applied campaign/version and reversal history.

10. Insurance Cards and Policies

10.1 Insurance Card master

Card master inherits the existing policy_card mapping:

The source contract is mutually exclusive by programme type:

Corporate membership and Staff benefit are the only enterprise cooperation types available here. Promotion and Insurance-type Benefit Contracts are not selectable for this purpose. The card master stores only the source identity in metadata; contract rules, plans, eligibility, limits and utilization remain authoritative in Benefit Contracts and are not copied into policy_card. Existing cards without metadata.programme_type are interpreted as insurance for backward compatibility.

The page renders a card-face gallery even when the list is empty, with a real New Card action. It never inserts demo cards.

Front/back JPEG, PNG or WebP files up to 5 MB can be uploaded, previewed, replaced or removed. Bytes are stored in the existing private file registry and the resulting private file URL is written to policy_card.image1/image2.

Insurance Card does not provide bulk Excel import. A usable card requires reviewed card-face files and an intentional Policy link, so Card creation and maintenance remains one record at a time. Policy master data may still be imported in bulk.

10.2 Policy management

Manage Policies opens a wide Drawer on the Insurance Cards page. The current phase supports simple Policy number/name, effective/expiry dates, holder type, contract type, insurance type and Billing visibility on the existing policy_contract table.

The card detail displays one source preview: linked Policy for an Insurance card, or linked Benefit Contract code/name, sponsor, type, effective period and status for Corporate membership / Staff benefit. Complex benefit rules, caps, beneficiaries, rotating QR, utilization and advanced pricing remain hidden.

11. Bulk Excel import — shared product contract

Every bulk-import-eligible area that creates master data must provide the following contract. Insurance Card is the confirmed exception in this phase because each card requires image and Policy-link review:

The list page exposes one compact secondary Bulk import launcher only. It is placed in the page-title header at the upper right, immediately before the page's primary New … action; the stable action order is Refresh → Bulk import → New …. A Policy-management Drawer applies the same rule to its Drawer header. Template download and file selection must not remain as large permanent page actions, separate body rows or controls inside a search/filter toolbar. The launcher opens the platform-standard right-side Bulk Import Drawer containing the complete import flow, so search, filters and record tables keep visual priority. This is a shared administrator-maintenance pattern, not a rule imposed on clinical or task-oriented pages: every approved executable admin import must reuse the shared Drawer component, while pending persistence models may only show a clearly non-executable requirement preview in the same pattern. Insurance Card itself has no launcher; the separate Policy manager may use the shared Drawer.

  1. Download Excel Template specific to that module;
  2. upload .xlsx or .csv;
  3. full-file validation before write;
  4. preview with Pass / Warning / Error counts and sample rows;
  5. explicit duplicate policy (UPDATE or SKIP where permitted);
  6. user confirmation before execution;
  7. batch execution with partial-success reporting;
  8. row-level validation messages and downloadable error detail;
  9. refresh of the list after success;
  10. import job audit with actor, permission, time, source filename, result counts and status.

Imports create Draft records unless the target model has an explicitly approved alternative. Import does not bypass publish permission or lifecycle rules.

Module Template/import state
Service Items Executable for item fields; Available Clinics column pending clinic mapping model
Lab & Imaging Test Items Executable for canonical Test Item fields
Diagnostic Vendor Mapping Required with vendor-mapping model; currently pending
Diagnostic Cost Schedule Required with cost-schedule model; currently pending
Products Executable for item/product fields; Available Clinics column pending clinic mapping model
Consultation Pricing Required with doctor-pricing model; currently pending
Service Packages Required in authoritative Package Setup; current Catalogue link does not duplicate it
Coupons Executable for Campaign draft fields
Insurance Cards No bulk import by confirmed exception; create/edit one card at a time with card-face review
Policies Executable for current-phase policy_contract fields

Template download requires read permission. Preview and execute require the corresponding non-assumed manage permission. A disabled or missing persistence model must display Backend adaptation pending; it must not simulate a successful import.

12. Backend Adaptation Ledger

This ledger is part of the approved product contract. Current describes verified reuse; Required / Recommended is future work and is not authorized for schema implementation in this frontend iteration.

Area Current table / behavior Required adaptation Design recommendation / migration note Current state
Canonical Item clinic_service_items stores Service/Product/Lab/Imaging identity and several specialized fields No immediate split required Retain one billing identity; later move Product-only attributes to a 1:1 detail table only if schema ownership becomes unclear Reused
Service/Product availability UI can infer clinic coverage from clinic_service_offerings, but Offering mixes routing and price Persist explicit many-to-many availability Add clinic_item_availability(service_item_id, operating_unit_id, is_available, effective_from/to, status, audit fields) plus explicit all-clinics semantics; migrate active Offering coverage, manually review conflicts Pending; UI-only
Service/Product pricing clinic_service_items.list_price/default_cost Prevent Clinic overrides in new contract Keep one value per item; remove pricing responsibility from new availability mapping Reused with rule change
Diagnostic Vendor mapping Legacy clinic_service_offerings partially contains vendor/code and Clinic-linked price Dedicated Vendor Item mapping Add diagnostic_vendor_items(service_item_id, vendor_id, vendor_item_name, vendor_code, default_cost, currency, effective dates, status, audit fields); unique active vendor/code constraint Pending
Diagnostic Cost Schedule No authoritative Doctor/Clinic cost model Effective-dated Cost overrides Add diagnostic_cost_schedules(vendor_item_id, operating_unit_id, doctor_user_id nullable, cost, currency, effective dates, status, audit fields); Doctor row requires Clinic; enforce non-overlapping active periods Pending
Diagnostic resolver No confirmed resolver Implement precedence from section 6.3 and expose trace Resolver must return winning row, reason and version; orders/invoices snapshot outcome Pending
Request Form Mapping Previous UI proposal only Remove from this workspace If later required for ordering, design it under V-Lab request/form configuration, not Catalogue pricing Removed / deferred
Consultation Standard Price Current Visit pricing binds an Offering; no doctor standard table Persist one standard per Doctor Add doctor_consultation_prices(doctor_user_id, price, currency, effective dates, status, audit fields) Pending
Consultation exceptions No Clinic/Insurance exception model Persist Clinic, Policy and Policy+Clinic overrides Add doctor_consultation_price_exceptions(doctor_user_id, policy_contract_id nullable, operating_unit_id nullable, price, currency, effective dates, status, audit fields) with valid-scope and non-overlap constraints Pending
Consultation resolver No resolver matching section 8.2 Implement and integrate with Consultation/Billing Return winning rule and fallback trace; Invoice snapshots doctor/policy/clinic inputs, row/version and final price Pending
Policy Existing policy_contract supports simple current-phase fields No schema change now Later add explicit lifecycle status/version/audit instead of relying on delete_flag; keep Benefit rule structures separate Reused; recommendation logged
Insurance Card Existing policy_card supports directory/basic fields and Policy link; JSONB metadata supports a governed Benefit Contract reference No card schema change or bulk import Keep private images in file registry; use mutually exclusive Policy or typed Benefit Contract source and require one-card review Reused / adapted
Legacy Fees & Offerings Existing code/data may still be operationally referenced Hide UI now; inventory dependencies before deprecation Make read-only migration report, map routing/availability/vendor fields to their target models, then deprecate only after resolver consumers are cut over Hidden, retained
Excel jobs Shared import framework provides template/preview/execute/job result Add task schemas/callbacks as each pending model lands Same framework, permission wrapper and error workbook; do not create one-off parsers Partly implemented
Package import Owned by Service Packages app Add component/template/callback in that app Import package header + component rows transactionally; validate canonical item codes before write Pending owner work
Billing snapshots Existing downstream snapshots are incomplete across new resolvers Persist applied version and trace No historical repricing after master-data change Pending integration

12.1 Required rollout order

  1. Approve table design and constraints separately.
  2. Create migrations and import callbacks.
  3. Backfill legacy Offerings into new availability/vendor/cost models with reconciliation reports.
  4. Implement resolvers with unit and precedence tests.
  5. Run dual-read comparison without changing charges.
  6. Cut over write/read consumers by feature flag.
  7. Verify Billing snapshots, import audit and rollback path.
  8. Only then remove Backend adaptation pending labels or deprecate legacy Offering paths.

13. Permissions and audit

Permission Purpose
catalogue.read View catalogue and download Service/Product/Test templates
catalogue.manage Create/update drafts and execute Service/Product/Test imports
catalogue.publish Publish/deactivate catalogue items
coupon.read View campaigns and download Coupon template
coupon.manage Create/update Campaign drafts and execute imports
coupon.publish Publish/deactivate Campaigns
benefits.read View Insurance Cards/Policies and download templates
benefits.manage Create/update Cards/Policies, upload images and execute imports

Assumed/restricted support roles cannot mutate, publish or execute imports. Role, permission, test-account and membership bootstrap data must not be deleted while simplifying this workspace.

14. Acceptance criteria

  1. The nine confirmed navigation entries and ordering match section 2; Fees & Offerings and complex Benefit Contracts are hidden but retained.
  2. Service and Product show separate List Price and Cost, confirmed filters and searchable checkbox Clinic multi-select.
  3. No Clinic can change a Service/Product List Price or Cost.
  4. Lab & Imaging renders one Test Catalogue and one detail Drawer; Request Form Mapping is absent.
  5. Test Items have unified List Price and all-clinic availability; Vendor/Clinic/Doctor variation applies only to Cost.
  6. Consultation Pricing shows every Doctor with Standard Price, Clinic exception and Insurance rate structure, while pending persistence is explicit.
  7. Insurance Cards opens direct card management, supports private front/back upload, exposes Insurance policy / Corporate membership / Staff benefit types, and shows the matching Policy or typed Benefit Contract source preview.
  8. Policies are created/edited/imported from an in-page Drawer using existing policy_contract; there is no new top-level Policy page.
  9. Service, Test Item, Product, Coupon and Policy each provide one compact Bulk import launcher in the page/Drawer header right, immediately before the primary New action, and open a right-side Drawer with module-specific template download and preview/validate/confirm import. No duplicate launcher remains in the page body or filter toolbar. Insurance Card is the confirmed exception and is maintained one card at a time.
  10. Pending models show pending import status rather than fake data or fake success.
  11. Publish/deactivate reason, permissions, import audit and historical snapshots follow this PRD.
  12. PRD publication, application deployment, end-to-end integration and UAT are reported separately.
  13. Corporate membership and Staff benefit cards cannot save without a matching Benefit Contract; changing type clears the incompatible Policy / Benefit Contract reference instead of keeping two active sources.

15. Delivery status at this revision

Insurance & Benefit Programmes

名称、卡片详情、双语说明、HTML净化与来源边界统一见 Catalogue 业务 PRD。本文件仅补充底表与迁移设计,不重复维护后续界面变更或发布流水。