Architecture, schema map, rebuild order, and testing notes for the database-first pricing engine. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
This is a database-first pricing engine that computes product pricing recommendations. Business logic lives entirely in SQL stored procedures and functions. There is no application server — consumers call the database directly. Both SQL Server (.ms.sql) and PostgreSQL (.pg.sql) implementations exist in parallel.
Testing a Pricing Scenario
-- PostgreSQL (primary)
SELECT *, ui_json->'data'
FROM pricequote.single_price_call(
'GRIF0001', -- bill-to customer code
'GRIF0001', -- ship-to customer code
'XNS0T1G3G18B096', -- part number
'v1:B..PLT..', -- product data segment
9600 -- volume (pieces)
);
-- SQL Server
EXEC pricing.single_price_call
@bill = 'GRIF0001',
@ship = 'GRIF0001',
@part = 'XNS0T1G3G18B096',
@v1ds = 'v1:B..PLT..',
@vol = 9600;
Test guidance_logic in isolation (SQL Server):
SELECT g.*
FROM (SELECT
TRY_CAST(.33275 AS NUMERIC(20,5)) AS tprice,
TRY_CAST(.758 AS NUMERIC(20,5)) AS last_price_norm,
TRY_CAST(null AS NUMERIC(20,5)) AS listprice_eff,
TRY_CAST('2025-06-01' AS DATE) AS last_date
) q
CROSS APPLY pricing.guidance_logic(q.tprice, q.last_price_norm, q.listprice_eff, q.last_date, 0.05, 1.0, 1.0) g;
Data Rebuild (PostgreSQL)
Run in order after source data changes:
REFRESH MATERIALIZED VIEW pricequote.lastpricedetail; -- ~37s
CALL pricequote.rebuild_pricelist(); -- ~32s
CALL pricequote.refresh_target_prices_base(); -- ~45s
REFRESH MATERIALIZED VIEW rlarp.cost_v1ds;
REFRESH MATERIALIZED VIEW rlarp.cost_v0ds;
Batch pricing for the full matrix (seeds pricequote.queue from rlarp.osm_stack, then merges results back):
CALL pricequote.process_queue();
Architecture
single_price_call — the main entry point
Located in procs/single_price_call.pg.sql (and .ms.sql). Accepts (bill, ship, part, v1ds, vol) and returns a fully enriched pricing row including guidance_price, expl (JSONB explanation), and ui_json (structured panels for UI rendering).
Processing steps:
- Resolve
chan/tier/custfrom bill/ship codes viarlarp.cust - Look up historical prices from
pricequote.lastpricedetail(materialized view overrlarp.osm_stack) - Look up target price from
pricequote.target_prices_base(keyed by stlc, ds, chan, tier, volume range) - Resolve cost standards for cross-segment normalization (
rlarp.cost_v1ds,rlarp.cost_v0ds) - Normalize last price when the historical sale was a different data segment (
last_isdiff) - Look up list price from
pricequote.pricelist_rangedviaCMS.CUSLG.IPRCBHCprice-level mapping - Apply
pricequote.guidance_logicto produce final recommended price - Build
explandui_jsonJSON
process_queue — batch version
procs/matrix_guidance.pg.sql. Seeds pricequote.queue from rlarp.osm_stack, runs the same 12-step logic as single_price_call via set-based UPDATEs, then merges expl/ui back into rlarp.osm_stack.pricing.
Key helper functions
pricequote.pick_last_price_from_hist(hist JSONB, v1ds TEXT)— selects the best historical price from the packedpart_statsJSONB using source precedence:dss→mrs→ fallback (18-month age threshold). Source codes:dss=direct sale,dsq=direct quote,mrs=most-recent-similar sale,mrq=most-recent-similar quote.pricequote.guidance_logic(target, last_norm, list_eff, last_date, ...)— picks base price (target → last → list), prevents price drops below last, caps at list price.pricequote.build_pricing_path_base(options JSONB)— recursive CTE that expands a JSON array of pricing options (Anchor, Color Tier, Packaging, Channel, Volume, etc.) into a full combinatorial pricing matrix.
Target price computation
pricequote.core_target holds anchor prices + options JSON per product line. pricequote.refresh_target_prices_base() calls build_pricing_path_base() to expand those into every (stlc, ds, chan, tier, vol_range) combination and stores results in pricequote.target_prices_base.
Product data segments (v1ds / v0ds)
Format: v1:B..PLT.. encodes color tier (position 4), branding (position 6), packaging (position 8-10), etc. The v0ds simplification reduces to just color tier + branding suffix. Customized parts have a v1ds that differs from their item master v1ds.
Channel/tier resolution
- Bill-to class
DIS+ ship-to classDIS→ channelWHS(wholesale); tier/plevel from bill-to distributor - Bill-to class
DIS+ ship-to class non-DIS→ channelDRP(drop-ship); tier/plevel from ship-to - Bill-to class
DIR→ channelDIR; tier/plevel from bill-to
Database Schemas
| Schema | Database | Purpose |
|---|---|---|
pricequote.* |
PostgreSQL | All pricing functions, tables, materialized views |
pricing.* |
SQL Server | Parallel SQL Server implementation |
rlarp.* |
Both | Source data: osm_stack (order/quote history), cust, cost_v1ds, cost_v0ds |
CMS.CUSLG.* |
Both | ERP item master (itemm), price level assignments (IPRCBHC) |
File Layout
procs/ -- stored procedures and functions (core logic)
tables/ -- table/view DDL
builder/ -- build_pricing_path_base and related option-cost helpers
rebuild/ -- refresh scripts for materialized views and derived tables
quote_review/ -- reporting views over osm_stack
archive/ -- legacy TypeScript and older SQL approaches (reference only)
Quote Review (Power BI dashboard source)
rlarp.quote_review (PostgreSQL view, DDL at quote_review/quote_review.pg.sql) is the source for the Power BI quote dashboard — a per-quote-line list with a related-sales scatter plot. Full lineage crosses platforms:
SQL Server view fanalysis.rlarp.live_quotes DDL: SQL repo quotes/live_quotes.sql (ALTER VIEW)
↓ postgres_fdw (server usmidsql01)
Postgres foreign table pricequote.live_quotes DDL: /opt/sync/usmidsql01/live_quotes/ddl.sql
↓
Postgres view rlarp.quote_review DDL: quote_review/quote_review.pg.sql
↓
Power BI dashboard
rlarp.live_quotesis a SQL Server view (in the SQL repo), surfaced to Postgres only as the FDW tablepricequote.live_quotes(note:pricequoteschema, notrlarp). It filters to quotes touched in the last 60 days, status IN (3,8,9,11,4).quote_review.pg.sqlfilterspricequote.live_quotestoqstat LIKE '%Submitted%', then layers guidance logic, customer tier, price limits/midrange, target price, part group.- Name collision: SQL Server objects also named
rlarp.quote_reviewexist in the SQL repo (quotes/quote_reviews/quote_review.ms.sqlis anALTER PROC;sql/mssql/pricing/reporting/quote_review.sqlis anALTER VIEW) — these are NOT the dashboard source. - CASCADE dependent of
osm_stack; auto-rebuilt by salesmatrix'stables/pg/rebuild_osm_stack_dependents.sh.
Open Issue: Last Price Is Net of Discounts
Problem
Last price is currently computed as sales_usd / qty where sales_usd = fb_val_loc * r_rate. fb_val_loc is the net value after discounts, so last price reflects a discounted unit price rather than a base/list price. This causes price_api to anchor guidance off a lower-than-intended price for customers who receive line discounts.
Data Available to Fix It
In rlarp.osm (both PG and MS SQL), fb_val_loc_dis holds the discount amount and fb_val_loc holds the net value. Gross/base value = fb_val_loc + fb_val_loc_dis. However, fb_val_loc_dis is not carried into osm_stack on either database — it is dropped during osm_stack_refresh().
Required Changes
To fix this properly, changes are needed in both databases (MS SQL feeds rebuild_lastprice; PostgreSQL feeds pricequote.lastpricedetail):
PostgreSQL (/opt/salesmatrix):
tables/pg/osm_stack.pg.sql— addgross_usd NUMERICandgross_local NUMERICcolumnsprocs/pg/osm_stack_refresh.pg.sql— populate:gross_local = fb_val_loc + fb_val_loc_dis,gross_usd = gross_local * r_ratetables/pg/lastpricedetail.pg.sql(pricequote.lastpricedetail) — change price calc fromsales_usd/qtytogross_usd/qty
MS SQL (/opt/salesmatrix):
tables/ms/osm_stack.ms.sql— addgross_usdandgross_localcolumnsprocs/ms/osm_stack_refresh.sql— populate same as PGrebuild/rebuild_lastprice.ms.sql— change price calc to usegross_usd/qty
Note on PG DROP CASCADE: Changing osm_stack DDL cascades to osm_stack_pretty, osm_powerbi, price_pool, quote_review, pricequote.lastpricedetail — all must be rebuilt after any DDL change. Adding a column avoids the drop; use ALTER TABLE rlarp.osm_stack ADD COLUMN.
Deployment Order
When making schema changes, deploy in dependency order:
tables/DDLbuilder/functionsprocs/guidance_logic,procs/lastprice_logic,procs/approval_logicprocs/single_price_call,procs/matrix_guidance