Skip to content

Product & Vendor Management (PVM)

PVM — Product & Vendor Management

Status: deployed (supplier layer, product metadata, taxonomy CRUD/status, comments with presigned attachments + retention cleanup, kits, alternatives, supplementary links, status history, comment abuse reporting, and Brand). External docs/MCP are published (see /common/start-here.html for latest build). Contract-only; infra/alarms/internal storage are out of scope.

Surface map

  • Expected base (API Gateway): https://api.g3nretailstack.com/pvm (custom domain; clean URLs disabled).
  • Auth: USM session or service-account API key required on all routes. Org-scoped; non-associated callers receive 404 not-found (anti-enumeration). Owners full access. Members need roles: Product Model Administrator (all PVM mutations), Vendor Contract Administrator (supplier ops), Product and Vendor Viewer (read/comment-only).
  • Pagination: limit default 8, clamp 1–256; next_token opaque JSON cursor.
  • MCP: protocol publishes to https://mcp.g3nretailstack.com/pvm/PROTOCOL.md (mirrored here at /pvm/PROTOCOL.md).
  • OpenAPI: https://doc.g3nretailstack.com/pvm/openapi.yaml
  • Role matrix: /common/role-matrix.html
  • Troubleshooting (404): /common/troubleshooting.html
  • Headers and identity cheat sheet: /common/headers-identity.html

Responses use { success, data?, error?, build, stats?, revision? } with build ID + UTC timestamp in the footer. Stateful record responses include revision. Any mutation that updates an existing revisioned record must include expected_revision; missing → expected-revision-required (HTTP 428), mismatch → conflict (HTTP 409) with the current record snapshot + current revision.

Lifecycles and rules (contract)

  • Codes & aliases: codes ^[A-Z][A-Z0-9_-]{0,9}$, immutable. Up to 16 aliases per record {tag ≤24 [A-Za-z0-9_.-], value ≤96}, unique per org; edits while inactive.
  • Code generation (create): for create endpoints that accept code, the request may omit code and instead provide code_pattern? (≤10 chars, A-Z 0-9 _ - ?, ? = wildcard) plus optional code_max_attempts? (int 1–64). On exhaustion: code-generation-exhausted (HTTP 409).
  • Status model: active↔inactive; any → doomed (terminal). Edits only while inactive. Style doom blocked while non-doomed variants exist; variant doom blocked while outbound kit components/alternatives/supplementary links exist, and while active inbound links exist.
  • Taxonomy: division/department/category/season are org-scoped. Category is a tree inside a department; parent must be in the same department. Enforced: acyclic, max depth 16. Dooming a division/department/category is blocked while it still has non-doomed children.
  • Suppliers: style requires ≥1 vendor AND ≥1 manufacturer with primaries; required category_id (must reference a non-doomed taxonomy category); optional brand_id. If brand_id is set the brand must be linked to all referenced vendors/manufacturers. If a brand has supplier links and you want to set brand.status=active, you must set primary vendor/manufacturer links first (POST /brand/link/set_primary). Supplier-scoped options must match style suppliers.
  • Org currency & FX (template econ): each org has an immutable org_currency (stored internally by PVM). template_econ.currency is required when monetary fields are set. If currency != org_currency then template_econ.fx is required and must include { org_currency, rate_to_org, as_of_utc } (and org_currency must match the org’s configured currency).
  • OGM (option matrix): revisioned, immutable per rev. Clone to edit; switch via style/ogm/set. ogm.groups[] defines the required option group codes (with priority order). Variants carry matrix_rev; stale when matrix_rev != style.ogm_rev. If ogm.groups[] is empty, then variant/create requires empty selections and signature is empty (so only one non-doomed variant can exist).
  • Variants: selections are resolved/validated against Option Group/Option records (by group_code + option_code). Signature is built in OGM group order (GROUP=OPTION|...), unique among non-doomed variants per style (freed on doom). selections[].size may include { waist, inseam, cup, width } (ignored for signature). Size values are stored as-provided (no unit conversion); interpretation is domain-specific. Edits inactive-only.
  • UOM dims: when provided, base_uom is required; sell_uom defaults to ea; conversion_factor defaults to 1 when sell_uom==base_uom, otherwise required. If control_type is serial/both, then serial_mode is required.
  • Identifiers/Aliases: uniqueness across non-doomed variants; transfer explicitly moves ownership with history and requires reason + expected_revision.
  • Barcodes: digits-only, valid check digit, (org, value) unique among non-doomed. Reuse only if prior value inactive/doomed AND allow_reuse=true + reason (reason required when reassigning to a different variant); otherwise 409. One primary per packaging_level. issued_by ∈ {gs1, vendor, org, unknown}.
  • CLI: direct API Gateway commands under g3n pvm ... (session + orgcode required). Generic pvm post/get cover any path.
  • Comments: immutable body; attachments ≤128 MB each (filename/content_type/size_bytes/caption). Status {current, archived, doomed}. Attachments are subject to retention cleanup (defaults: current 90d; archived 30d; doomed 7d). GET /comment/report reports the largest comments by attachment size (PMA/owner only; no backfill).
  • Kits/links: kit components (qty/uom). Alternatives (replacement-style) and supplementary (upsell/cross-sell/accessory/etc.) are variant→variant links with priority/time windows.

Quick start (current slice)

  1. Create OGM (optional empty): POST /ogmogm_id/ogm_rev.
  2. Create brand and link to suppliers: POST /brand then POST /brand/link/add for each vendor/manufacturer you plan to reference on a style. If you plan to activate the brand, set primaries first: POST /brand/link/set_primary (vendor + manufacturer).
  3. Create style (inactive): POST /style with category_id + suppliers (vendor/manufacturer primaries; suppliers must be pre-created + verified) + ogm_id + optional brand_id (must be linked to all suppliers when set).
  4. Create variants: POST /variant with selections (must resolve to existing option groups/options and match OGM groups[] when present; optional structured size fields on selections); identifiers/aliases optional; signature uniqueness enforced per style.
  5. Set primaries: POST /barcode/add then /barcode/set_primary; add identifiers/aliases as needed.
  6. Switch OGM rev: POST /style/ogm/set marks variants stale; list stale via /variant/stale/list; recreate variants on new rev via /variant/recreate.
  7. Comments/kits/links are available: POST /comment + GET /comment/list + GET /comment/report, kit components under /kit/component/*, and link management under /alternative/* and /supplementary/*.

Clarifications (B5)

  • Supplier list capping: styles cap supplier links at 128 vendors and 128 manufacturers. When the cap is exceeded, the oldest non-primary entries are evicted (primary is preserved when present).
  • OGM revisions: OGM is immutable; edits require clone → style/ogm/set; variants become stale when matrix_rev changes.
  • Barcodes: check-digit validation enforced; one primary per packaging level; reuse requires explicit allow_reuse + reason.
  • Comments: PVM comments are immutable (create-only) until the shared comment model is adopted.