diff --git a/tests/unit/test_dv2_postgres_ddl.py b/tests/unit/test_dv2_postgres_ddl.py index 769d2b9..877056a 100644 --- a/tests/unit/test_dv2_postgres_ddl.py +++ b/tests/unit/test_dv2_postgres_ddl.py @@ -182,3 +182,41 @@ def test_hubs_and_links_have_bytea_primary_keys(): assert links.count("BYTEA PRIMARY KEY") == 8 # link member hash keys are NOT NULL, not part of the PK. assert "BYTEA NOT NULL" in links + + +# --- bv_order_canonical smoke seed (G1) -------------------------------------- +# The live query is a Mac smoke (postgres/smoke/verify_bv_order.sh); the seed +# itself is gated no-Docker here, exactly like the rest of the PG vault. + +SMOKE_SEED = PG_DIR / "smoke" / "order_smoke_seed.sql" + + +def test_bv_order_smoke_seed_parses_as_postgres(): + sql = SMOKE_SEED.read_text(encoding="utf-8") + parsed = sqlglot.parse(sql, dialect="postgres") + assert parsed, "order_smoke_seed.sql produced no statements" + inserts = [s for s in parsed if s and s.key == "insert"] + # hubs (3) + links (2) + headers (bitrix ×5 + 1c ×1) + pricing (×4) + wb (1) + assert len(inserts) >= 15, f"expected >=15 INSERTs, got {len(inserts)}" + + +def test_bv_order_smoke_seed_has_no_clickhouse_constructs(): + body = _strip_comments(SMOKE_SEED.read_text(encoding="utf-8")).lower() + for token in CH_TOKENS: + assert token not in body, f"smoke seed still contains ClickHouse token {token!r}" + # PostgreSQL hash idiom, not the ClickHouse MD5(toString(...)) form. + assert "decode(md5(" in body + assert "md5(tostring(" not in body + + +def test_bv_order_smoke_seed_covers_every_order_source_and_branch(): + body = _strip_comments(SMOKE_SEED.read_text(encoding="utf-8")) + # all three order-satellite families the view reconstructs + assert "rv.sat_order_header__bitrix__" in body + assert "rv.sat_order_pricing__1c__" in body + assert "rv.sat_order_marketplace__wb__msk" in body + # marketplace state exists for msk (production seed never populated it) + assert body.count("rv.sat_order_marketplace__wb__msk") == 1 + # every jurisdiction present so split_part branch derivation is exercised + for branch in ("msk", "spb", "ekb", "dxb", "ala"): + assert f"bitrix__{branch}__" in body, f"smoke seed missing {branch} order" diff --git a/warehouse/agentflow/dv2/postgres/README.md b/warehouse/agentflow/dv2/postgres/README.md index 10e35dd..44d66b4 100644 --- a/warehouse/agentflow/dv2/postgres/README.md +++ b/warehouse/agentflow/dv2/postgres/README.md @@ -82,11 +82,15 @@ PGHOST=localhost PGUSER=agentflow PGDATABASE=agentflow ./apply.sh ``` No-Docker validation (Windows / CI): every generated and hand-authored DDL -statement is parsed with sqlglot's PostgreSQL dialect by +statement — plus the `bv_order_canonical` smoke seed (`smoke/order_smoke_seed.sql`) +— is parsed with sqlglot's PostgreSQL dialect by `tests/unit/test_dv2_postgres_ddl.py`. `apply.sh` itself has been applied live end-to-end (standalone PostgreSQL 17.5 on Windows, no Docker) as part of the governance verification — -`docs/perf/vault-pii-governance-pg-verify-2026-07-02.md`. A -`bv_order_canonical` query against real promoted data remains the next -sub-step (single-node Mac), mirroring how the Flink and ClickHouse smokes are -run live on the Mac. +`docs/perf/vault-pii-governance-pg-verify-2026-07-02.md`. + +A live `bv_order_canonical` query is exercised by `smoke/verify_bv_order.sh` +against a deterministic order seed (see `smoke/README.md` for the recipe and the +hand-derived expected output). The single-node Mac run is the remaining +sub-step, mirroring how the Flink and ClickHouse smokes are run live on the Mac; +the standalone no-Docker recipe from the governance verify applies unchanged. diff --git a/warehouse/agentflow/dv2/postgres/smoke/README.md b/warehouse/agentflow/dv2/postgres/smoke/README.md new file mode 100644 index 0000000..3326b73 --- /dev/null +++ b/warehouse/agentflow/dv2/postgres/smoke/README.md @@ -0,0 +1,88 @@ +# `bv_order_canonical` live smoke (G1) + +A small, hand-verifiable order set for the DV2 raw vault on PostgreSQL, plus a +script that queries `rv.bv_order_canonical` and asserts the reconstruction. This +is the order-side counterpart to `../governance/verify_live.sh`, which seeds and +exercises only the **customer** side (`bv_customer_mdm__`). Together they +cover both business-vault surfaces on a live PostgreSQL stand with no Docker. + +## Why this exists + +`bv_order_canonical` is the reconstruction-heavy view that motivates running the +vault on PostgreSQL (see `../README.md`): it collapses each satellite to its +latest version (`DISTINCT ON (order_hk) … ORDER BY load_ts DESC`) over a +`UNION ALL` of per-branch header and pricing satellites, then `LEFT JOIN`s +header + pricing + marketplace + customer + store. The governance verify never +touched it, so a live query against real order rows was the last unproven vault +sub-step (`../README.md`, "Apply and verify"). This seed makes that query +possible and deterministic. + +## Run (Mac / any running PostgreSQL, no Docker) + +```bash +# from warehouse/agentflow/dv2/postgres +PSQL="psql -h localhost -p 5432 -U agentflow -d agentflow" APPLY=1 \ + bash smoke/verify_bv_order.sh +``` + +`APPLY=1` runs `apply.sh` first (schema → hubs → links → 48 satellites → +`03_business_vault.sql`). Drop it if the vault is already applied. The seed +(`order_smoke_seed.sql`) is idempotent (`ON CONFLICT DO NOTHING`), so the script +is safe to re-run. Every printed line should start with `PASS`; the script exits +non-zero if any assertion fails. + +The standalone no-Docker recipe used for the governance verifies +(`initdb` + `pg_ctl`, port 55432, trust auth) applies here unchanged — see +`docs/perf/vault-pii-governance-pg-verify-2026-07-03.md`. + +## Expected canonical output (derived by hand from `order_smoke_seed.sql`) + +Eight orders, one canonical row each. RUB amounts; VAT is RU 20 % / UAE 5 %. + +| order_bk | branch | channel | status | total | subtotal | tax | ship | wb_status | header_src | pricing_src | mkt_src | +|---|---|---|---|---|---|---|---|---|---|---|---| +| mp__msk__0000001 | msk | marketplace | delivered | 2400.00 | 2000.00 | 400.00 | 199.00 | delivered | bitrix__msk | 1c__msk | wb__msk | +| mp__msk__0000002 | msk | marketplace | **shipped** | 2600.00 | **2166.67** | **433.33** | 199.00 | **delivering** | bitrix__msk | 1c__msk | wb__msk | +| site__msk__0008901 | msk | d2c | delivered | 3500.00 | 3000.00 | 600.00 | 299.00 | — | bitrix__msk | 1c__msk | — | +| bitrix__msk__0009181 | msk | b2b | **confirmed** | 60000.00 | 50000.00 | 10000.00 | 500.00 | — | bitrix__msk | 1c__msk | — | +| bitrix__spb__0009541 | spb | b2b | delivered | 48000.00 | 40000.00 | 8000.00 | 800.00 | — | bitrix__spb | 1c__spb | — | +| bitrix__ekb__0009721 | ekb | b2b | delivered | 36000.00 | 30000.00 | 6000.00 | 500.00 | — | bitrix__ekb | 1c__ekb | — | +| bitrix__dxb__0009851 | dxb | b2b | delivered | 42000.00 | 40000.00 | **2000.00** | 500.00 | — | bitrix__dxb | 1c__dxb | — | +| bitrix__ala__0009925 | ala | b2b | cancelled | 30000.00 | **—** | **—** | **—** | — | bitrix__ala | **—** | — | + +Bold cells are the invariants the smoke targets: + +- **SCD2 latest-wins** — `mp__msk__0000002` carries two Bitrix header versions + (pending @09:00, shipped @12:00) *and* an older 1C header (@08:00); the view + must return `shipped` (newest across the whole `UNION`). Pricing and + marketplace collapse the same way (`2166.67`/`433.33`, `delivering`). +- **Soft-delete tombstone** — `bitrix__msk__0009181` has an active `confirmed` + @10:00 and a newer `is_deleted=1` `cancelled` @14:00. The `WHERE is_deleted=0` + filter drops the tombstone *before* the collapse, so `confirmed` wins — a + soft-deleted latest version must never surface. +- **Marketplace = MSK only** — Wildberries state lights up for the two msk + marketplace orders and is `NULL` everywhere else (`marketplace_source` + non-null count = 2). The production seed never populated + `sat_order_marketplace__wb__msk` at all, so this is new coverage. +- **Pricing LEFT JOIN miss** — `bitrix__ala__0009925` has a header but no 1C + pricing row, so `pricing_source` and every pricing amount are `NULL`. +- **Per-jurisdiction VAT** — dxb effective rate `tax/subtotal = 5 %` (UAE), the + RU branches `20 %`. +- **Branch derivation** — `split_part(record_source,'__',2)` yields + msk×4 / spb / ekb / dxb / ala; `total_amount` sums to `224500.00`. + +## Honest scope + +- This is a **standalone seed smoke** of the view logic, not a run against + **promoted CDC volume**. On the Mac the same query also runs at the end of the + full CDC → Kafka → serving path; that end-to-end variant remains the true + Mac-tail. The view SQL exercised is identical either way. +- The seed writes headers to the Bitrix satellites and pricing to the 1C + satellites, matching the production `satellite_seed.sql` attribution + ("Bitrix wins" for the header). The single 1C header on + `mp__msk__0000002` is a deliberate fixture to exercise the cross-source + `UNION` collapse — it is not something the production loader emits. +- No-Docker CI gate: `order_smoke_seed.sql` is parsed under sqlglot's + PostgreSQL dialect and checked for stray ClickHouse constructs by + `tests/unit/test_dv2_postgres_ddl.py`, the same gate as the rest of the + PostgreSQL vault. diff --git a/warehouse/agentflow/dv2/postgres/smoke/order_smoke_seed.sql b/warehouse/agentflow/dv2/postgres/smoke/order_smoke_seed.sql new file mode 100644 index 0000000..034c739 --- /dev/null +++ b/warehouse/agentflow/dv2/postgres/smoke/order_smoke_seed.sql @@ -0,0 +1,184 @@ +-- ===================================================================== +-- bv_order_canonical smoke seed — PostgreSQL dialect (G1 Mac smoke). +-- ===================================================================== +-- Own-brand kitchen-appliance importer legend (docs/domain.md, +-- docs/generator-spec.md). RUB amounts, per-jurisdiction VAT (RU 20 % / +-- UAE 5 % / KZ 12 %), five branches (msk / spb / ekb / dxb / ala). +-- +-- Purpose: give rv.bv_order_canonical a small, hand-verifiable order set so a +-- live query proves the business-vault reconstruction end-to-end — the +-- sub-step the governance verify (which seeds only the CUSTOMER side, see +-- governance/verify_live.sh SEED_DEMO) never exercised. Expected output is +-- pinned in smoke/README.md and asserted by smoke/verify_bv_order.sh. +-- +-- Conventions mirror the ClickHouse synthetic_seed.sql exactly, ported to the +-- PostgreSQL idiom used by governance/verify_live.sh: +-- order_hk = decode(md5(order_bk), 'hex') -- BYTEA hash key +-- customer_hk = decode(md5(customer_bk), 'hex') +-- store_hk = decode(md5(store_code), 'hex') +-- link_hk = decode(md5(order_bk || '|' || ), 'hex') +-- branch = split_part(hub_order.record_source, '__', 2) +-- All inserts are idempotent (ON CONFLICT DO NOTHING), so the seed is +-- repeatable and safe to re-run on a stand that already carries it. +-- +-- Numbering follows synthetic_seed.sql order bands (§11): mp__msk marketplace, +-- site__msk D2C, bitrix__ B2B. Order business keys are +-- __<7-digit>, e.g. mp__msk__0000001. +-- +-- Coverage (one canonical row per hub_order, 8 orders total): +-- O1 mp__msk__0000001 msk marketplace — all three sources present, wb lit +-- O2 mp__msk__0000002 msk marketplace — SCD2 latest-wins across the header +-- UNION (bitrix beats an older 1c header) + per-sat +-- collapse of pricing and marketplace +-- O3 site__msk__0008901 msk D2C — no marketplace row (marketplace_source NULL) +-- O4 bitrix__msk__0009181 msk B2B — soft-delete tombstone: a newer is_deleted=1 +-- header must NOT win over the older active version +-- O5 bitrix__spb__0009541 spb B2B — branch derivation + RU 20 % VAT +-- O6 bitrix__ekb__0009721 ekb B2B — branch derivation +-- O7 bitrix__dxb__0009851 dxb B2B — UAE 5 % VAT +-- O8 bitrix__ala__0009925 ala B2B — KZ jurisdiction, cancelled, and NO pricing +-- row (pricing LEFT JOIN miss -> pricing_source NULL) +-- +-- NOTE on O2's 1C header: the production seed writes order headers only to the +-- Bitrix satellites (satellite_seed.sql — "Bitrix wins" for the header). O2 +-- carries a single, deliberately older 1c__msk header purely to exercise the +-- header UNION+DISTINCT ON collapse across sources; it is a smoke fixture, not +-- a claim about the production loader. + +-- ============ HUBS ============ +INSERT INTO rv.hub_store (store_hk, store_bk, record_source) VALUES + (decode(md5('msk-01'),'hex'),'msk-01','1c__global'), + (decode(md5('spb-01'),'hex'),'spb-01','1c__global'), + (decode(md5('ekb-01'),'hex'),'ekb-01','1c__global'), + (decode(md5('dxb-01'),'hex'),'dxb-01','1c__global'), + (decode(md5('ala-01'),'hex'),'ala-01','1c__global') +ON CONFLICT (store_hk) DO NOTHING; + +INSERT INTO rv.hub_customer (customer_hk, customer_bk, record_source) VALUES + (decode(md5('CUST-R-01'),'hex'),'CUST-R-01','1c__msk'), + (decode(md5('CUST-R-02'),'hex'),'CUST-R-02','mp__msk'), + (decode(md5('CUST-D-MSK'),'hex'),'CUST-D-MSK','1c__msk'), + (decode(md5('CUST-D-SPB'),'hex'),'CUST-D-SPB','1c__spb'), + (decode(md5('CUST-D-EKB'),'hex'),'CUST-D-EKB','1c__ekb'), + (decode(md5('CUST-D-DXB'),'hex'),'CUST-D-DXB','1c__dxb'), + (decode(md5('CUST-D-ALA'),'hex'),'CUST-D-ALA','1c__ala') +ON CONFLICT (customer_hk) DO NOTHING; + +INSERT INTO rv.hub_order (order_hk, order_bk, record_source) VALUES + (decode(md5('mp__msk__0000001'),'hex'),'mp__msk__0000001','mp__msk'), + (decode(md5('mp__msk__0000002'),'hex'),'mp__msk__0000002','mp__msk'), + (decode(md5('site__msk__0008901'),'hex'),'site__msk__0008901','site__msk'), + (decode(md5('bitrix__msk__0009181'),'hex'),'bitrix__msk__0009181','bitrix__msk'), + (decode(md5('bitrix__spb__0009541'),'hex'),'bitrix__spb__0009541','bitrix__spb'), + (decode(md5('bitrix__ekb__0009721'),'hex'),'bitrix__ekb__0009721','bitrix__ekb'), + (decode(md5('bitrix__dxb__0009851'),'hex'),'bitrix__dxb__0009851','bitrix__dxb'), + (decode(md5('bitrix__ala__0009925'),'hex'),'bitrix__ala__0009925','bitrix__ala') +ON CONFLICT (order_hk) DO NOTHING; + +-- ============ LINKS ============ +-- lnk_order_customer: marketplace/D2C draw from retail; B2B from own-branch dealer. +INSERT INTO rv.lnk_order_customer (link_hk, order_hk, customer_hk, record_source) VALUES + (decode(md5('mp__msk__0000001|CUST-R-02'),'hex'), decode(md5('mp__msk__0000001'),'hex'), decode(md5('CUST-R-02'),'hex'), 'mp__msk'), + (decode(md5('mp__msk__0000002|CUST-R-01'),'hex'), decode(md5('mp__msk__0000002'),'hex'), decode(md5('CUST-R-01'),'hex'), 'mp__msk'), + (decode(md5('site__msk__0008901|CUST-R-01'),'hex'), decode(md5('site__msk__0008901'),'hex'), decode(md5('CUST-R-01'),'hex'), 'site__msk'), + (decode(md5('bitrix__msk__0009181|CUST-D-MSK'),'hex'), decode(md5('bitrix__msk__0009181'),'hex'), decode(md5('CUST-D-MSK'),'hex'), 'bitrix__msk'), + (decode(md5('bitrix__spb__0009541|CUST-D-SPB'),'hex'), decode(md5('bitrix__spb__0009541'),'hex'), decode(md5('CUST-D-SPB'),'hex'), 'bitrix__spb'), + (decode(md5('bitrix__ekb__0009721|CUST-D-EKB'),'hex'), decode(md5('bitrix__ekb__0009721'),'hex'), decode(md5('CUST-D-EKB'),'hex'), 'bitrix__ekb'), + (decode(md5('bitrix__dxb__0009851|CUST-D-DXB'),'hex'), decode(md5('bitrix__dxb__0009851'),'hex'), decode(md5('CUST-D-DXB'),'hex'), 'bitrix__dxb'), + (decode(md5('bitrix__ala__0009925|CUST-D-ALA'),'hex'), decode(md5('bitrix__ala__0009925'),'hex'), decode(md5('CUST-D-ALA'),'hex'), 'bitrix__ala') +ON CONFLICT (link_hk) DO NOTHING; + +-- lnk_order_store: msk central warehouse fulfils mp/D2C/its own B2B; regionals fulfil own B2B. +INSERT INTO rv.lnk_order_store (link_hk, order_hk, store_hk, record_source) VALUES + (decode(md5('mp__msk__0000001|msk-01'),'hex'), decode(md5('mp__msk__0000001'),'hex'), decode(md5('msk-01'),'hex'), '1c__global'), + (decode(md5('mp__msk__0000002|msk-01'),'hex'), decode(md5('mp__msk__0000002'),'hex'), decode(md5('msk-01'),'hex'), '1c__global'), + (decode(md5('site__msk__0008901|msk-01'),'hex'), decode(md5('site__msk__0008901'),'hex'), decode(md5('msk-01'),'hex'), '1c__global'), + (decode(md5('bitrix__msk__0009181|msk-01'),'hex'), decode(md5('bitrix__msk__0009181'),'hex'), decode(md5('msk-01'),'hex'), '1c__global'), + (decode(md5('bitrix__spb__0009541|spb-01'),'hex'), decode(md5('bitrix__spb__0009541'),'hex'), decode(md5('spb-01'),'hex'), '1c__global'), + (decode(md5('bitrix__ekb__0009721|ekb-01'),'hex'), decode(md5('bitrix__ekb__0009721'),'hex'), decode(md5('ekb-01'),'hex'), '1c__global'), + (decode(md5('bitrix__dxb__0009851|dxb-01'),'hex'), decode(md5('bitrix__dxb__0009851'),'hex'), decode(md5('dxb-01'),'hex'), '1c__global'), + (decode(md5('bitrix__ala__0009925|ala-01'),'hex'), decode(md5('bitrix__ala__0009925'),'hex'), decode(md5('ala-01'),'hex'), '1c__global') +ON CONFLICT (link_hk) DO NOTHING; + +-- ============ ORDER HEADER (Bitrix — production source of truth) ============ +-- hash_diff distinguishes SCD2 versions (msk O2 v1/v2, O4 v1/v2). +INSERT INTO rv.sat_order_header__bitrix__msk + (order_hk, load_ts, hash_diff, order_date, channel, order_status, total_amount, is_deleted) VALUES + -- O1: single delivered marketplace header + (decode(md5('mp__msk__0000001'),'hex'), '2026-07-01 09:00:00', decode(md5('mp__msk__0000001|hdr|v1'),'hex'), '2026-06-28 14:12:00', 'marketplace', 'delivered', 2400.00, 0), + -- O2: two versions — pending@09:00 then shipped@12:00 (latest wins) + (decode(md5('mp__msk__0000002'),'hex'), '2026-07-01 09:00:00', decode(md5('mp__msk__0000002|hdr|v1'),'hex'), '2026-06-29 10:05:00', 'marketplace', 'pending', 2600.00, 0), + (decode(md5('mp__msk__0000002'),'hex'), '2026-07-01 12:00:00', decode(md5('mp__msk__0000002|hdr|v2'),'hex'), '2026-06-29 10:05:00', 'marketplace', 'shipped', 2600.00, 0), + -- O3: D2C delivered + (decode(md5('site__msk__0008901'),'hex'), '2026-07-01 10:00:00', decode(md5('site__msk__0008901|hdr|v1'),'hex'), '2026-06-30 18:40:00', 'd2c', 'delivered', 3500.00, 0), + -- O4: active confirmed@10:00, then a soft-delete tombstone@14:00 (is_deleted=1, must NOT win) + (decode(md5('bitrix__msk__0009181'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__msk__0009181|hdr|v1'),'hex'), '2026-06-27 09:15:00', 'b2b', 'confirmed', 60000.00, 0), + (decode(md5('bitrix__msk__0009181'),'hex'), '2026-07-01 14:00:00', decode(md5('bitrix__msk__0009181|hdr|v2'),'hex'), '2026-06-27 09:15:00', 'b2b', 'cancelled', 60000.00, 1) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +INSERT INTO rv.sat_order_header__bitrix__spb + (order_hk, load_ts, hash_diff, order_date, channel, order_status, total_amount, is_deleted) VALUES + (decode(md5('bitrix__spb__0009541'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__spb__0009541|hdr|v1'),'hex'), '2026-06-26 11:00:00', 'b2b', 'delivered', 48000.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +INSERT INTO rv.sat_order_header__bitrix__ekb + (order_hk, load_ts, hash_diff, order_date, channel, order_status, total_amount, is_deleted) VALUES + (decode(md5('bitrix__ekb__0009721'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__ekb__0009721|hdr|v1'),'hex'), '2026-06-25 16:30:00', 'b2b', 'delivered', 36000.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +INSERT INTO rv.sat_order_header__bitrix__dxb + (order_hk, load_ts, hash_diff, order_date, channel, order_status, total_amount, is_deleted) VALUES + (decode(md5('bitrix__dxb__0009851'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__dxb__0009851|hdr|v1'),'hex'), '2026-06-24 08:20:00', 'b2b', 'delivered', 42000.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +INSERT INTO rv.sat_order_header__bitrix__ala + (order_hk, load_ts, hash_diff, order_date, channel, order_status, total_amount, is_deleted) VALUES + (decode(md5('bitrix__ala__0009925'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__ala__0009925|hdr|v1'),'hex'), '2026-06-23 12:00:00', 'b2b', 'cancelled', 30000.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +-- O2 only: an OLDER 1C header (08:00) to exercise the header UNION+DISTINCT ON +-- collapse across sources — the newer Bitrix shipped@12:00 row must still win. +INSERT INTO rv.sat_order_header__1c__msk + (order_hk, load_ts, hash_diff, order_date, channel, order_status, total_amount, is_deleted) VALUES + (decode(md5('mp__msk__0000002'),'hex'), '2026-07-01 08:00:00', decode(md5('mp__msk__0000002|hdr|1c'),'hex'), '2026-06-29 10:05:00', 'marketplace', 'pending', 2350.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +-- ============ ORDER PRICING (1C — RUB, per-jurisdiction VAT) ============ +-- O8 (ala) intentionally has NO pricing row -> pricing LEFT JOIN miss. +INSERT INTO rv.sat_order_pricing__1c__msk + (order_hk, load_ts, hash_diff, subtotal_amount, discount_amount, tax_amount, shipping_cost, is_deleted) VALUES + -- O1: RU 20 % VAT + (decode(md5('mp__msk__0000001'),'hex'), '2026-07-01 09:00:00', decode(md5('mp__msk__0000001|prc|v1'),'hex'), 2000.00, 0.00, 400.00, 199.00, 0), + -- O2: two pricing versions — latest (12:00) wins + (decode(md5('mp__msk__0000002'),'hex'), '2026-07-01 09:00:00', decode(md5('mp__msk__0000002|prc|v1'),'hex'), 2100.00, 0.00, 420.00, 199.00, 0), + (decode(md5('mp__msk__0000002'),'hex'), '2026-07-01 12:00:00', decode(md5('mp__msk__0000002|prc|v2'),'hex'), 2166.67, 0.00, 433.33, 199.00, 0), + -- O3: D2C + (decode(md5('site__msk__0008901'),'hex'), '2026-07-01 10:00:00', decode(md5('site__msk__0008901|prc|v1'),'hex'), 3000.00, 60.00, 600.00, 299.00, 0), + -- O4: B2B msk + (decode(md5('bitrix__msk__0009181'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__msk__0009181|prc|v1'),'hex'), 50000.00, 0.00, 10000.00, 500.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +INSERT INTO rv.sat_order_pricing__1c__spb + (order_hk, load_ts, hash_diff, subtotal_amount, discount_amount, tax_amount, shipping_cost, is_deleted) VALUES + (decode(md5('bitrix__spb__0009541'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__spb__0009541|prc|v1'),'hex'), 40000.00, 0.00, 8000.00, 800.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +INSERT INTO rv.sat_order_pricing__1c__ekb + (order_hk, load_ts, hash_diff, subtotal_amount, discount_amount, tax_amount, shipping_cost, is_deleted) VALUES + (decode(md5('bitrix__ekb__0009721'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__ekb__0009721|prc|v1'),'hex'), 30000.00, 0.00, 6000.00, 500.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +-- O7 dxb: UAE VAT 5 % (2000 on 40000). +INSERT INTO rv.sat_order_pricing__1c__dxb + (order_hk, load_ts, hash_diff, subtotal_amount, discount_amount, tax_amount, shipping_cost, is_deleted) VALUES + (decode(md5('bitrix__dxb__0009851'),'hex'), '2026-07-01 10:00:00', decode(md5('bitrix__dxb__0009851|prc|v1'),'hex'), 40000.00, 0.00, 2000.00, 500.00, 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; + +-- ============ MARKETPLACE (Wildberries — MSK integration only) ============ +-- Only the two msk marketplace orders carry a wb row; O2 carries two versions. +INSERT INTO rv.sat_order_marketplace__wb__msk + (order_hk, load_ts, hash_diff, wb_status, wb_commission, return_window_until, is_deleted) VALUES + (decode(md5('mp__msk__0000001'),'hex'), '2026-07-01 09:00:00', decode(md5('mp__msk__0000001|wb|v1'),'hex'), 'delivered', 240.00, '2026-07-31', 0), + (decode(md5('mp__msk__0000002'),'hex'), '2026-07-01 09:00:00', decode(md5('mp__msk__0000002|wb|v1'),'hex'), 'sold', 260.00, '2026-08-01', 0), + (decode(md5('mp__msk__0000002'),'hex'), '2026-07-01 12:00:00', decode(md5('mp__msk__0000002|wb|v2'),'hex'), 'delivering', 260.00, '2026-08-01', 0) +ON CONFLICT (order_hk, load_ts) DO NOTHING; diff --git a/warehouse/agentflow/dv2/postgres/smoke/verify_bv_order.sh b/warehouse/agentflow/dv2/postgres/smoke/verify_bv_order.sh new file mode 100644 index 0000000..61a20d9 --- /dev/null +++ b/warehouse/agentflow/dv2/postgres/smoke/verify_bv_order.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# Live smoke of rv.bv_order_canonical on a running PostgreSQL (G1 Mac smoke). +# Evidence transcript target: docs/perf/bv-order-canonical-pg-smoke-*.md +# +# Prereqs: a running PostgreSQL with the DV2 vault applied (postgres/apply.sh, +# which creates the schema, hubs, links, satellites and the bv_order_canonical +# view). Configure the client via PSQL (apply.sh convention) as the vault owner: +# PSQL="psql -h localhost -p 55432 -U agentflow -d agentflow" bash verify_bv_order.sh +# +# APPLY=1 additionally runs postgres/apply.sh first (fresh stand). +# The order seed (order_smoke_seed.sql) is idempotent, so re-runs are safe. +# +# Expected result: every line starts with PASS. Expected values are pinned in +# smoke/README.md and derived by hand from order_smoke_seed.sql. +set -u +PSQL="${PSQL:-psql}" +Q="$PSQL -v ON_ERROR_STOP=1 -qtA" +DIR="$(cd "$(dirname "$0")" && pwd)" +PGDIR="$(cd "$DIR/.." && pwd)" + +pass=0; fail=0 + +if [ "${APPLY:-0}" = "1" ]; then + echo "=== setup: apply.sh (fresh vault) ===" + PSQL="$PSQL" bash "$PGDIR/apply.sh" >/dev/null && echo "vault applied" +fi + +echo "=== setup: order smoke seed (idempotent) ===" +$Q -f "$DIR/order_smoke_seed.sql" >/dev/null && echo "seed applied" +echo + +# Scope every aggregate to this smoke's eight orders so the script is also +# correct on a stand that already carries promoted data. +SCOPE="order_bk IN ( + 'mp__msk__0000001','mp__msk__0000002','site__msk__0008901','bitrix__msk__0009181', + 'bitrix__spb__0009541','bitrix__ekb__0009721','bitrix__dxb__0009851','bitrix__ala__0009925')" +BV="rv.bv_order_canonical" + +assert_eq() { # assert_eq