V13-FE-011: finalize search list layout slice
This commit is contained in:
+90
@@ -0,0 +1,90 @@
|
||||
-- KBX Golden Screen reference schema.
|
||||
-- In a real modular monolith, schema ownership should stay with the owning module.
|
||||
|
||||
create schema if not exists oms;
|
||||
create schema if not exists catalog;
|
||||
create schema if not exists inventory;
|
||||
create schema if not exists audit;
|
||||
create schema if not exists integration;
|
||||
|
||||
create table if not exists oms.customers (
|
||||
id uuid primary key,
|
||||
code varchar(50) not null unique,
|
||||
name varchar(200) not null,
|
||||
is_active boolean not null default true
|
||||
);
|
||||
|
||||
create table if not exists catalog.items (
|
||||
id uuid primary key,
|
||||
code varchar(80) not null unique,
|
||||
name varchar(300) not null,
|
||||
is_active boolean not null default true
|
||||
);
|
||||
|
||||
create table if not exists inventory.warehouses (
|
||||
id uuid primary key,
|
||||
code varchar(50) not null unique,
|
||||
name varchar(200) not null,
|
||||
is_active boolean not null default true
|
||||
);
|
||||
|
||||
create table if not exists oms.orders (
|
||||
id uuid primary key,
|
||||
order_no varchar(80) not null unique,
|
||||
order_date date not null,
|
||||
customer_id uuid not null references oms.customers(id),
|
||||
warehouse_id uuid not null references inventory.warehouses(id),
|
||||
receiver_name varchar(100) not null,
|
||||
phone varchar(50) not null,
|
||||
postal_code varchar(20),
|
||||
address1 varchar(500) not null,
|
||||
address2 varchar(500),
|
||||
status varchar(30) not null,
|
||||
version bigint not null default 1 check (version > 0),
|
||||
created_at timestamptz not null default now(),
|
||||
created_by varchar(200) not null,
|
||||
updated_at timestamptz,
|
||||
updated_by varchar(200)
|
||||
);
|
||||
|
||||
create index if not exists ix_orders_order_date on oms.orders(order_date desc);
|
||||
create index if not exists ix_orders_customer on oms.orders(customer_id, order_date desc);
|
||||
create index if not exists ix_orders_status on oms.orders(status, order_date desc);
|
||||
|
||||
create table if not exists oms.order_lines (
|
||||
id uuid primary key,
|
||||
order_id uuid not null references oms.orders(id),
|
||||
line_no integer not null check (line_no > 0),
|
||||
item_id uuid not null references catalog.items(id),
|
||||
quantity numeric(18,4) not null check (quantity > 0),
|
||||
unit_price numeric(18,4) not null check (unit_price >= 0),
|
||||
amount numeric(20,4) not null check (amount >= 0),
|
||||
remark varchar(500),
|
||||
unique(order_id, line_no)
|
||||
);
|
||||
|
||||
create index if not exists ix_order_lines_item on oms.order_lines(item_id);
|
||||
|
||||
create table if not exists audit.entries (
|
||||
id uuid primary key,
|
||||
aggregate_type varchar(100) not null,
|
||||
aggregate_id uuid not null,
|
||||
action varchar(100) not null,
|
||||
actor varchar(200) not null,
|
||||
occurred_at timestamptz not null,
|
||||
data jsonb not null default '{}'::jsonb
|
||||
);
|
||||
create index if not exists ix_audit_aggregate on audit.entries(aggregate_type, aggregate_id, occurred_at desc);
|
||||
|
||||
create table if not exists integration.outbox (
|
||||
id uuid primary key,
|
||||
event_type varchar(200) not null,
|
||||
aggregate_id uuid not null,
|
||||
payload jsonb not null,
|
||||
occurred_at timestamptz not null,
|
||||
status varchar(30) not null,
|
||||
processed_at timestamptz,
|
||||
retry_count integer not null default 0 check (retry_count >= 0),
|
||||
last_error text
|
||||
);
|
||||
create index if not exists ix_outbox_pending on integration.outbox(status, occurred_at) where status = 'PENDING';
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
-- WMS-PICK-001 Golden Screen reference schema.
|
||||
create schema if not exists wms;
|
||||
|
||||
create table if not exists wms.picking_tasks (
|
||||
id uuid primary key,
|
||||
task_no varchar(80) not null unique,
|
||||
status varchar(30) not null check (status in ('READY','IN_PROGRESS','BLOCKED','COMPLETED','CANCELLED')),
|
||||
assigned_to varchar(200),
|
||||
version bigint not null default 1 check (version > 0),
|
||||
started_at timestamptz,
|
||||
completed_at timestamptz,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz
|
||||
);
|
||||
|
||||
create index if not exists ix_picking_tasks_status on wms.picking_tasks(status, created_at);
|
||||
|
||||
create table if not exists wms.picking_lines (
|
||||
id uuid primary key,
|
||||
task_id uuid not null references wms.picking_tasks(id),
|
||||
line_no integer not null check (line_no > 0),
|
||||
location_code varchar(80) not null,
|
||||
location_barcode varchar(120) not null,
|
||||
location_confirmed boolean not null default false,
|
||||
item_id uuid not null references catalog.items(id),
|
||||
item_option varchar(200),
|
||||
barcode varchar(120) not null,
|
||||
required_qty numeric(18,4) not null check (required_qty > 0),
|
||||
picked_qty numeric(18,4) not null default 0 check (picked_qty >= 0 and picked_qty <= required_qty),
|
||||
status varchar(30) not null default 'READY' check (status in ('READY','IN_PROGRESS','COMPLETED','BLOCKED')),
|
||||
completed_at timestamptz,
|
||||
updated_at timestamptz,
|
||||
unique(task_id, line_no)
|
||||
);
|
||||
|
||||
create index if not exists ix_picking_lines_task_status on wms.picking_lines(task_id, status, line_no);
|
||||
create index if not exists ix_picking_lines_barcode on wms.picking_lines(barcode);
|
||||
|
||||
-- Stores the exact response for each scanner command so network retries are safe and deterministic.
|
||||
create table if not exists wms.scan_receipts (
|
||||
id uuid primary key,
|
||||
task_id uuid not null references wms.picking_tasks(id),
|
||||
idempotency_key varchar(200) not null,
|
||||
barcode varchar(200) not null,
|
||||
source varchar(40) not null,
|
||||
occurred_at timestamptz not null,
|
||||
actor varchar(200) not null,
|
||||
response_payload jsonb not null,
|
||||
created_at timestamptz not null default now(),
|
||||
unique(task_id, idempotency_key)
|
||||
);
|
||||
|
||||
create index if not exists ix_scan_receipts_task_created on wms.scan_receipts(task_id, created_at desc);
|
||||
|
||||
create table if not exists wms.picking_exceptions (
|
||||
id uuid primary key,
|
||||
task_id uuid not null references wms.picking_tasks(id),
|
||||
line_id uuid references wms.picking_lines(id),
|
||||
exception_type varchar(50) not null,
|
||||
memo varchar(300),
|
||||
status varchar(30) not null check (status in ('OPEN','RESOLVED','CANCELLED')),
|
||||
reported_at timestamptz not null,
|
||||
reported_by varchar(200) not null,
|
||||
resolved_at timestamptz,
|
||||
resolved_by varchar(200),
|
||||
idempotency_key varchar(200) not null,
|
||||
unique(task_id, idempotency_key)
|
||||
);
|
||||
|
||||
create index if not exists ix_picking_exceptions_open on wms.picking_exceptions(task_id, status) where status='OPEN';
|
||||
|
||||
create table if not exists wms.quantity_receipts (
|
||||
id uuid primary key,
|
||||
task_id uuid not null references wms.picking_tasks(id),
|
||||
idempotency_key varchar(200) not null,
|
||||
line_id uuid not null references wms.picking_lines(id),
|
||||
picked_qty numeric(18,4) not null,
|
||||
actor varchar(200) not null,
|
||||
response_payload jsonb not null,
|
||||
created_at timestamptz not null default now(),
|
||||
unique(task_id, idempotency_key)
|
||||
);
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
-- KBX v4 reference projections. In production these can be maintained by application writes,
|
||||
-- triggers, or projectors; the UX contract only requires stable read models.
|
||||
create table if not exists erp_item_search_projection (
|
||||
id uuid primary key,
|
||||
code varchar(50) not null,
|
||||
name varchar(200) not null,
|
||||
category_name varchar(120) not null default '',
|
||||
specification varchar(200) not null default '',
|
||||
unit varchar(20) not null default 'EA',
|
||||
barcode varchar(100) not null default '',
|
||||
default_warehouse_id uuid null,
|
||||
default_warehouse_name varchar(120) not null default '',
|
||||
lot_managed boolean not null default false,
|
||||
expiry_managed boolean not null default false,
|
||||
active boolean not null default true,
|
||||
version bigint not null default 1,
|
||||
search_text text not null default ''
|
||||
);
|
||||
create table if not exists erp_inventory_snapshot_projection (
|
||||
snapshot_key varchar(100) primary key,
|
||||
item_id uuid not null,
|
||||
item_code varchar(50) not null,
|
||||
item_name varchar(200) not null,
|
||||
specification varchar(200) not null default '',
|
||||
warehouse_id uuid not null,
|
||||
warehouse_name varchar(120) not null,
|
||||
location_id uuid null,
|
||||
location_code varchar(80) null,
|
||||
on_hand_qty numeric(18,4) not null default 0,
|
||||
allocated_qty numeric(18,4) not null default 0,
|
||||
hold_qty numeric(18,4) not null default 0,
|
||||
available_qty numeric(18,4) not null default 0,
|
||||
search_text text not null default ''
|
||||
);
|
||||
create index if not exists ix_erp_inventory_snapshot_item on erp_inventory_snapshot_projection(item_id);
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
create schema if not exists kbx;
|
||||
|
||||
create table if not exists kbx.import_sessions (
|
||||
id uuid primary key,
|
||||
tenant_id varchar(100) not null,
|
||||
user_id varchar(200) not null,
|
||||
screen_id varchar(100) not null,
|
||||
import_type varchar(150) not null,
|
||||
original_file_name varchar(300) not null,
|
||||
status varchar(40) not null,
|
||||
source_columns jsonb not null default '[]'::jsonb,
|
||||
mapping jsonb not null default '[]'::jsonb,
|
||||
total_rows integer not null default 0 check (total_rows >= 0),
|
||||
valid_rows integer not null default 0 check (valid_rows >= 0),
|
||||
invalid_rows integer not null default 0 check (invalid_rows >= 0),
|
||||
warning_rows integer not null default 0 check (warning_rows >= 0),
|
||||
created_rows integer not null default 0 check (created_rows >= 0),
|
||||
updated_rows integer not null default 0 check (updated_rows >= 0),
|
||||
progress_percent integer not null default 0 check (progress_percent between 0 and 100),
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
completed_at timestamptz,
|
||||
expires_at timestamptz not null default (now() + interval '14 days'),
|
||||
failure_code varchar(120),
|
||||
failure_message text
|
||||
);
|
||||
|
||||
create index if not exists ix_import_sessions_owner
|
||||
on kbx.import_sessions(tenant_id, user_id, created_at desc);
|
||||
create index if not exists ix_import_sessions_status
|
||||
on kbx.import_sessions(status, updated_at);
|
||||
create index if not exists ix_import_sessions_expiry
|
||||
on kbx.import_sessions(expires_at);
|
||||
|
||||
-- File bytes are separated from session metadata so normal session queries never load XLSX blobs.
|
||||
-- This PostgreSQL-backed starter avoids a distributed filesystem dependency. The interface can
|
||||
-- later be replaced with object storage without changing the import workflow.
|
||||
create table if not exists kbx.import_files (
|
||||
session_id uuid primary key references kbx.import_sessions(id) on delete cascade,
|
||||
content_type varchar(150) not null,
|
||||
file_size bigint not null check (file_size >= 0),
|
||||
sha256 varchar(64) not null,
|
||||
data bytea not null
|
||||
);
|
||||
|
||||
create table if not exists kbx.import_rows (
|
||||
session_id uuid not null references kbx.import_sessions(id) on delete cascade,
|
||||
row_number integer not null check (row_number >= 2),
|
||||
raw_data jsonb not null,
|
||||
normalized_data jsonb,
|
||||
status varchar(30) not null default 'PENDING',
|
||||
errors jsonb not null default '[]'::jsonb,
|
||||
warnings jsonb not null default '[]'::jsonb,
|
||||
domain_key varchar(300),
|
||||
primary key(session_id, row_number)
|
||||
);
|
||||
|
||||
create index if not exists ix_import_rows_status
|
||||
on kbx.import_rows(session_id, status, row_number);
|
||||
create index if not exists ix_import_rows_domain_key
|
||||
on kbx.import_rows(session_id, domain_key)
|
||||
where domain_key is not null;
|
||||
|
||||
create table if not exists kbx.saved_import_mappings (
|
||||
id uuid primary key,
|
||||
tenant_id varchar(100) not null,
|
||||
user_id varchar(200) not null,
|
||||
import_type varchar(150) not null,
|
||||
name varchar(200) not null,
|
||||
source_signature varchar(64) not null,
|
||||
mapping jsonb not null,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
unique(tenant_id, user_id, import_type, name)
|
||||
);
|
||||
|
||||
create index if not exists ix_saved_import_mapping_signature
|
||||
on kbx.saved_import_mappings(tenant_id, user_id, import_type, source_signature);
|
||||
|
||||
create table if not exists kbx.import_job_receipts (
|
||||
session_id uuid not null references kbx.import_sessions(id) on delete cascade,
|
||||
operation varchar(40) not null,
|
||||
job_id varchar(100) not null,
|
||||
requested_at timestamptz not null default now(),
|
||||
primary key(session_id, operation)
|
||||
);
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
create schema if not exists kbx;
|
||||
|
||||
-- Operational projection only. OMS/WMS/ERP remain the source of truth.
|
||||
create table if not exists kbx.work_items (
|
||||
id uuid primary key,
|
||||
tenant_id varchar(100) not null,
|
||||
source_module varchar(20) not null,
|
||||
source_type varchar(100) not null,
|
||||
source_id varchar(200) not null,
|
||||
reference_no varchar(200) not null,
|
||||
source_screen_id varchar(100),
|
||||
source_version bigint,
|
||||
code varchar(120) not null,
|
||||
title varchar(240) not null,
|
||||
detail text,
|
||||
severity varchar(20) not null check (severity in ('info','warning','critical')),
|
||||
status varchar(20) not null check (status in ('open','claimed','resolved','ignored')),
|
||||
owner_id varchar(200),
|
||||
owner_name varchar(200),
|
||||
retry_action_key varchar(160),
|
||||
allow_manual_resolution boolean not null default false,
|
||||
context jsonb not null default '{}'::jsonb,
|
||||
occurred_at timestamptz not null,
|
||||
due_at timestamptz,
|
||||
resolved_at timestamptz,
|
||||
resolution_reason text,
|
||||
version bigint not null default 1,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
unique(tenant_id, source_module, source_type, source_id, code)
|
||||
);
|
||||
|
||||
create index if not exists ix_work_items_queue
|
||||
on kbx.work_items(tenant_id, status, severity, occurred_at desc);
|
||||
create index if not exists ix_work_items_owner
|
||||
on kbx.work_items(tenant_id, owner_id, status, occurred_at desc);
|
||||
create index if not exists ix_work_items_source
|
||||
on kbx.work_items(tenant_id, source_module, source_type, source_id);
|
||||
create index if not exists ix_work_items_reference
|
||||
on kbx.work_items(tenant_id, reference_no);
|
||||
|
||||
create table if not exists kbx.work_item_audit (
|
||||
id uuid primary key,
|
||||
work_item_id uuid not null references kbx.work_items(id) on delete cascade,
|
||||
tenant_id varchar(100) not null,
|
||||
action varchar(80) not null,
|
||||
actor_id varchar(200) not null,
|
||||
actor_name varchar(200) not null,
|
||||
reason text,
|
||||
before_status varchar(20),
|
||||
after_status varchar(20),
|
||||
occurred_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create index if not exists ix_work_item_audit_item
|
||||
on kbx.work_item_audit(work_item_id, occurred_at desc);
|
||||
|
||||
-- Comparison projection. It stores evidence/result, not authoritative inventory/order state.
|
||||
create table if not exists kbx.reconcile_items (
|
||||
id uuid primary key,
|
||||
tenant_id varchar(100) not null,
|
||||
reconcile_type varchar(120) not null,
|
||||
reference_no varchar(200) not null,
|
||||
source_label varchar(160) not null,
|
||||
target_label varchar(160) not null,
|
||||
expected_value varchar(300) not null,
|
||||
actual_value varchar(300) not null,
|
||||
difference_value varchar(300),
|
||||
reason_code varchar(120),
|
||||
reason_text text,
|
||||
status varchar(20) not null check (status in ('matched','mismatch','pending','resolved')),
|
||||
source_id varchar(200),
|
||||
target_id varchar(200),
|
||||
identity_key varchar(420) not null,
|
||||
evidence jsonb not null default '{}'::jsonb,
|
||||
occurred_at timestamptz not null,
|
||||
resolved_at timestamptz,
|
||||
version bigint not null default 1,
|
||||
updated_at timestamptz not null default now(),
|
||||
unique(tenant_id, reconcile_type, reference_no, identity_key)
|
||||
);
|
||||
|
||||
create index if not exists ix_reconcile_queue
|
||||
on kbx.reconcile_items(tenant_id, reconcile_type, status, occurred_at desc);
|
||||
create index if not exists ix_reconcile_reference
|
||||
on kbx.reconcile_items(tenant_id, reference_no);
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
create schema if not exists kbx;
|
||||
|
||||
create table if not exists kbx.user_suggestions (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
user_id uuid not null,
|
||||
category varchar(32) not null check (category in ('inconvenience','bug','improvement')),
|
||||
message varchar(2000) not null,
|
||||
screen_id varchar(64) not null,
|
||||
screen_version varchar(32) not null,
|
||||
route varchar(512) not null,
|
||||
app_version varchar(64) not null,
|
||||
user_role varchar(128) not null,
|
||||
context jsonb null,
|
||||
status varchar(32) not null default 'NEW',
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
create index if not exists ix_user_suggestions_screen_created
|
||||
on kbx.user_suggestions(screen_id, created_at desc);
|
||||
create index if not exists ix_user_suggestions_status_created
|
||||
on kbx.user_suggestions(status, created_at desc);
|
||||
|
||||
-- AI telemetry intentionally excludes the user's full question and business payload.
|
||||
create table if not exists kbx.ai_interactions (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
user_id uuid not null,
|
||||
screen_id varchar(64) not null,
|
||||
screen_version varchar(32) not null,
|
||||
capability varchar(32) not null,
|
||||
result_kind varchar(32) not null,
|
||||
provider varchar(64) null,
|
||||
duration_ms integer null,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
create index if not exists ix_ai_interactions_screen_created
|
||||
on kbx.ai_interactions(screen_id, created_at desc);
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
|
||||
-- KBX v8 reference process coverage. Source modules remain owners of their business state.
|
||||
create schema if not exists oms;
|
||||
create schema if not exists erp;
|
||||
|
||||
create table if not exists oms.claims (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
claim_no varchar(50) not null,
|
||||
order_id uuid not null,
|
||||
order_no varchar(50) not null,
|
||||
channel_name varchar(100) not null,
|
||||
type varchar(20) not null,
|
||||
reason varchar(300) not null,
|
||||
requested_qty numeric(18,4) not null check (requested_qty > 0),
|
||||
status varchar(30) not null,
|
||||
owner_name varchar(100),
|
||||
requested_at timestamptz not null,
|
||||
version bigint not null default 1,
|
||||
unique(tenant_id, claim_no)
|
||||
);
|
||||
create or replace view oms.claim_search_projection as
|
||||
select id,claim_no,order_no,channel_name,type,reason,requested_qty,status,owner_name,requested_at from oms.claims;
|
||||
|
||||
create table if not exists erp.purchase_orders (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
purchase_no varchar(50) not null,
|
||||
purchase_date date not null,
|
||||
supplier_id uuid not null,
|
||||
warehouse_id uuid not null,
|
||||
status varchar(30) not null,
|
||||
version bigint not null default 1,
|
||||
unique(tenant_id,purchase_no)
|
||||
);
|
||||
create table if not exists erp.purchase_order_lines (
|
||||
id uuid primary key, purchase_order_id uuid not null references erp.purchase_orders(id), item_id uuid not null,
|
||||
quantity numeric(18,4) not null check(quantity>0), unit_price numeric(18,4) not null check(unit_price>=0), due_date date
|
||||
);
|
||||
|
||||
create table if not exists erp.inventory_moves (
|
||||
id uuid primary key, tenant_id uuid not null, move_no varchar(50) not null, move_date date not null,
|
||||
from_warehouse_id uuid not null, to_warehouse_id uuid not null, status varchar(30) not null, version bigint not null default 1,
|
||||
check(from_warehouse_id <> to_warehouse_id), unique(tenant_id,move_no)
|
||||
);
|
||||
create table if not exists erp.inventory_move_lines (
|
||||
id uuid primary key, move_id uuid not null references erp.inventory_moves(id), item_id uuid not null,
|
||||
move_qty numeric(18,4) not null check(move_qty>0), lot_no varchar(100), remark varchar(500)
|
||||
);
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
-- KBX v9 production runtime projections.
|
||||
-- These tables hold operational state, not domain truth.
|
||||
create schema if not exists kbx;
|
||||
|
||||
create table if not exists kbx.operation_runs (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
user_id uuid,
|
||||
operation_type varchar(80) not null,
|
||||
title varchar(200) not null,
|
||||
source_screen_id varchar(60),
|
||||
status varchar(30) not null,
|
||||
requested_at timestamptz not null default now(),
|
||||
started_at timestamptz,
|
||||
completed_at timestamptz,
|
||||
processed bigint not null default 0 check(processed >= 0),
|
||||
total bigint check(total is null or total >= 0),
|
||||
succeeded bigint not null default 0 check(succeeded >= 0),
|
||||
failed bigint not null default 0 check(failed >= 0),
|
||||
correlation_id varchar(100),
|
||||
idempotency_key varchar(160),
|
||||
result_message varchar(1000),
|
||||
result jsonb,
|
||||
expires_at timestamptz,
|
||||
unique(tenant_id, idempotency_key)
|
||||
);
|
||||
create index if not exists ix_kbx_operation_runs_user_recent on kbx.operation_runs(tenant_id, user_id, requested_at desc);
|
||||
create index if not exists ix_kbx_operation_runs_status on kbx.operation_runs(tenant_id, status, requested_at desc);
|
||||
|
||||
create table if not exists kbx.user_notifications (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
user_id uuid not null,
|
||||
severity varchar(20) not null,
|
||||
category varchar(50) not null,
|
||||
title varchar(200) not null,
|
||||
message varchar(1000),
|
||||
screen_id varchar(60),
|
||||
entity_type varchar(80),
|
||||
entity_id varchar(120),
|
||||
action jsonb,
|
||||
created_at timestamptz not null default now(),
|
||||
read_at timestamptz,
|
||||
expires_at timestamptz
|
||||
);
|
||||
create index if not exists ix_kbx_user_notifications_unread on kbx.user_notifications(tenant_id, user_id, created_at desc) where read_at is null;
|
||||
|
||||
create table if not exists kbx.runtime_incidents (
|
||||
id uuid primary key,
|
||||
tenant_id uuid,
|
||||
mode varchar(20) not null,
|
||||
title varchar(200) not null,
|
||||
message varchar(1000),
|
||||
started_at timestamptz not null,
|
||||
ended_at timestamptz,
|
||||
correlation_id varchar(100),
|
||||
metadata jsonb
|
||||
);
|
||||
create index if not exists ix_kbx_runtime_incidents_active on kbx.runtime_incidents(started_at desc) where ended_at is null;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
-- KBX v14 API command idempotency receipt.
|
||||
-- This is infrastructure state, not a domain aggregate.
|
||||
create schema if not exists kbx;
|
||||
|
||||
create table if not exists kbx.command_receipts (
|
||||
operation_id varchar(150) not null,
|
||||
idempotency_key varchar(200) not null,
|
||||
response_json jsonb not null,
|
||||
created_at timestamptz not null default now(),
|
||||
primary key(operation_id, idempotency_key)
|
||||
);
|
||||
|
||||
create index if not exists ix_command_receipts_created_at
|
||||
on kbx.command_receipts(created_at);
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
create schema if not exists kbx;
|
||||
create table if not exists kbx.sensitive_data_disclosures (
|
||||
id bigserial primary key,
|
||||
tenant_id uuid not null,
|
||||
user_id uuid not null,
|
||||
policy_id text not null,
|
||||
entity_type text not null,
|
||||
entity_id text not null,
|
||||
fields jsonb not null,
|
||||
reason text null,
|
||||
correlation_id text not null,
|
||||
occurred_at timestamptz not null default now()
|
||||
);
|
||||
create index if not exists ix_sensitive_disclosures_lookup
|
||||
on kbx.sensitive_data_disclosures(tenant_id, occurred_at desc, policy_id);
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
create schema if not exists kbx;
|
||||
|
||||
create table if not exists kbx.ux_events (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
event_name varchar(80) not null,
|
||||
screen_id varchar(80),
|
||||
screen_version varchar(20),
|
||||
session_id uuid not null,
|
||||
task_session_id uuid,
|
||||
app_version varchar(40),
|
||||
occurred_at timestamptz not null,
|
||||
received_at timestamptz not null default now(),
|
||||
duration_ms integer,
|
||||
event_count integer not null default 1 check(event_count > 0),
|
||||
attributes jsonb not null default '{}'::jsonb,
|
||||
check(duration_ms is null or duration_ms between 0 and 86400000)
|
||||
);
|
||||
create index if not exists ix_ux_events_tenant_time on kbx.ux_events(tenant_id,occurred_at desc);
|
||||
create index if not exists ix_ux_events_tenant_event_time on kbx.ux_events(tenant_id,event_name,occurred_at desc);
|
||||
create index if not exists ix_ux_events_screen_time on kbx.ux_events(tenant_id,screen_id,occurred_at desc) where screen_id is not null;
|
||||
|
||||
-- Server-side business outcome counters are intentionally separate from browser interaction events.
|
||||
-- No order/customer/item identity is stored; only low-cardinality work type and counts.
|
||||
create table if not exists kbx.ux_business_outcomes (
|
||||
tenant_id uuid not null,
|
||||
business_date date not null,
|
||||
screen_id varchar(80) not null default '',
|
||||
work_type varchar(80) not null,
|
||||
reason_code varchar(80) not null default '',
|
||||
observed_count integer not null check(observed_count >= 0),
|
||||
auto_processed_count integer not null check(auto_processed_count >= 0),
|
||||
manual_intervention_count integer not null check(manual_intervention_count >= 0),
|
||||
updated_at timestamptz not null default now(),
|
||||
primary key(tenant_id,business_date,screen_id,work_type,reason_code),
|
||||
check(auto_processed_count + manual_intervention_count <= observed_count)
|
||||
);
|
||||
|
||||
comment on table kbx.ux_events is 'KBX semantic UX telemetry. Raw business values and sensitive data are prohibited.';
|
||||
comment on table kbx.ux_business_outcomes is 'Aggregated business workload counts used for Manual Intervention Rate; no business entity identifiers.';
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
create schema if not exists kbx;
|
||||
|
||||
-- Runtime overrides are operational controls; code contracts remain the canonical definition of safe experiment scope.
|
||||
create table if not exists kbx.experiment_runtime (
|
||||
tenant_id uuid not null,
|
||||
experiment_id varchar(160) not null,
|
||||
state varchar(24) not null check(state in ('draft','running','paused','rolled-back','graduated','completed')),
|
||||
rollout_percent integer not null check(rollout_percent between 0 and 100),
|
||||
kill_switch boolean not null default false,
|
||||
version integer not null default 1,
|
||||
updated_by uuid,
|
||||
updated_at timestamptz not null default now(),
|
||||
primary key(tenant_id,experiment_id)
|
||||
);
|
||||
|
||||
create table if not exists kbx.experiment_assignments (
|
||||
tenant_id uuid not null,
|
||||
experiment_id varchar(160) not null,
|
||||
user_id uuid not null,
|
||||
variant varchar(80) not null,
|
||||
assigned_at timestamptz not null default now(),
|
||||
primary key(tenant_id,experiment_id,user_id)
|
||||
);
|
||||
create index if not exists ix_experiment_assignments_experiment on kbx.experiment_assignments(tenant_id,experiment_id,variant);
|
||||
|
||||
create table if not exists kbx.experiment_audit (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
experiment_id varchar(160) not null,
|
||||
action varchar(40) not null,
|
||||
before_state jsonb,
|
||||
after_state jsonb,
|
||||
actor_id uuid,
|
||||
reason varchar(500) not null,
|
||||
correlation_id varchar(100),
|
||||
occurred_at timestamptz not null default now()
|
||||
);
|
||||
create index if not exists ix_experiment_audit_time on kbx.experiment_audit(tenant_id,experiment_id,occurred_at desc);
|
||||
|
||||
-- Optional experiment context is low-cardinality metadata only; no business/customer identity is added.
|
||||
alter table kbx.ux_events add column if not exists experiment_id varchar(160);
|
||||
alter table kbx.ux_events add column if not exists experiment_variant varchar(80);
|
||||
create index if not exists ix_ux_events_experiment_time on kbx.ux_events(tenant_id,experiment_id,experiment_variant,occurred_at desc) where experiment_id is not null;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
create schema if not exists kbx;
|
||||
|
||||
create table if not exists kbx.integration_attempts (
|
||||
id uuid primary key,
|
||||
tenant_id uuid not null,
|
||||
integration_id text not null,
|
||||
message_id uuid not null,
|
||||
aggregate_type text not null,
|
||||
aggregate_id text not null,
|
||||
aggregate_version bigint null,
|
||||
attempt_no integer not null check (attempt_no > 0),
|
||||
state text not null check (state in ('queued','delivering','retrying','delivered','failed','suspended')),
|
||||
failure_kind text null check (failure_kind in ('transient','permanent')),
|
||||
failure_code text null,
|
||||
detail text null,
|
||||
http_status integer null,
|
||||
external_reference text null,
|
||||
correlation_id text not null,
|
||||
manual_retry_key text null,
|
||||
started_at timestamptz not null,
|
||||
completed_at timestamptz null,
|
||||
next_retry_at timestamptz null,
|
||||
unique (tenant_id, integration_id, message_id, attempt_no)
|
||||
);
|
||||
create index if not exists ix_integration_attempts_retry on kbx.integration_attempts(tenant_id,state,next_retry_at) where state='retrying';
|
||||
create index if not exists ix_integration_attempts_aggregate on kbx.integration_attempts(tenant_id,aggregate_type,aggregate_id,started_at desc);
|
||||
create unique index if not exists ux_integration_manual_retry on kbx.integration_attempts(tenant_id,manual_retry_key) where manual_retry_key is not null;
|
||||
|
||||
create table if not exists kbx.integration_receipts (
|
||||
tenant_id uuid not null,
|
||||
integration_id text not null,
|
||||
message_key text not null,
|
||||
received_at timestamptz not null,
|
||||
processed_at timestamptz null,
|
||||
result jsonb null,
|
||||
primary key (tenant_id,integration_id,message_key)
|
||||
);
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
create schema if not exists kbx;
|
||||
|
||||
create table if not exists kbx.external_data_cache (
|
||||
tenant_id uuid not null,
|
||||
dataset_id text not null,
|
||||
provider_id text not null,
|
||||
cache_key text not null,
|
||||
request_descriptor jsonb not null,
|
||||
normalized_data jsonb not null,
|
||||
provider_observed_at timestamptz null,
|
||||
requested_at timestamptz not null,
|
||||
received_at timestamptz not null,
|
||||
ingested_at timestamptz not null,
|
||||
fresh_until timestamptz null,
|
||||
usable_until timestamptz null,
|
||||
payload_sha256 char(64) not null,
|
||||
normalizer_version text not null,
|
||||
state text not null check(state in ('fresh','stale','expired','unavailable')),
|
||||
correlation_id text not null,
|
||||
primary key(tenant_id,dataset_id,cache_key),
|
||||
check(usable_until is null or fresh_until is null or usable_until >= fresh_until)
|
||||
);
|
||||
create index if not exists ix_external_data_cache_state on kbx.external_data_cache(tenant_id,dataset_id,state,fresh_until);
|
||||
|
||||
create table if not exists kbx.external_data_observations (
|
||||
id bigint generated always as identity primary key,
|
||||
tenant_id uuid not null,
|
||||
dataset_id text not null,
|
||||
provider_id text not null,
|
||||
cache_key text not null,
|
||||
payload_sha256 char(64) not null,
|
||||
normalizer_version text not null,
|
||||
provider_observed_at timestamptz null,
|
||||
received_at timestamptz not null,
|
||||
ingested_at timestamptz not null,
|
||||
state text not null,
|
||||
correlation_id text not null
|
||||
);
|
||||
create index if not exists ix_external_data_observations_lookup on kbx.external_data_observations(tenant_id,dataset_id,cache_key,ingested_at desc);
|
||||
|
||||
comment on table kbx.external_data_cache is 'External provider normalized read cache. It is not domain truth and does not retain raw provider payload by default.';
|
||||
comment on column kbx.external_data_cache.payload_sha256 is 'Evidence hash for reproducibility without default raw payload retention.';
|
||||
|
||||
comment on column kbx.external_data_cache.request_descriptor is 'Canonical non-secret request inputs only. Credentials, bearer tokens, and provider secrets are forbidden.';
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
-- KBX v26: independent T04 Golden Screen + T05 inventory history projection.
|
||||
create table if not exists erp.item_prices (
|
||||
id uuid primary key,
|
||||
item_id uuid not null references catalog.items(id),
|
||||
effective_date date not null,
|
||||
unit_price numeric(18,4) not null check(unit_price >= 0),
|
||||
remark varchar(500),
|
||||
version bigint not null default 1 check(version > 0),
|
||||
created_at timestamptz not null default now(),
|
||||
created_by varchar(200) not null,
|
||||
updated_at timestamptz,
|
||||
updated_by varchar(200),
|
||||
unique(item_id,effective_date)
|
||||
);
|
||||
create index if not exists ix_erp_item_prices_effective on erp.item_prices(item_id,effective_date desc);
|
||||
|
||||
create table if not exists erp_inventory_ledger_projection (
|
||||
entry_id uuid primary key,
|
||||
item_id uuid not null,
|
||||
occurred_at timestamptz not null,
|
||||
business_type varchar(80) not null,
|
||||
reference_no varchar(120) not null default '',
|
||||
warehouse_name varchar(120) not null,
|
||||
location_code varchar(80),
|
||||
inbound_qty numeric(18,4) not null default 0 check(inbound_qty >= 0),
|
||||
outbound_qty numeric(18,4) not null default 0 check(outbound_qty >= 0),
|
||||
balance_qty numeric(18,4) not null default 0,
|
||||
actor varchar(200) not null default 'system'
|
||||
);
|
||||
create index if not exists ix_erp_inventory_ledger_item_time on erp_inventory_ledger_projection(item_id,occurred_at desc);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
-- KBX v27: normalized item write detail and record lifecycle indexes.
|
||||
create table if not exists catalog.item_details (
|
||||
item_id uuid primary key references catalog.items(id),
|
||||
category_name varchar(120) not null default '',
|
||||
specification varchar(200) not null default '',
|
||||
unit varchar(20) not null default 'EA',
|
||||
barcode varchar(100) not null default '',
|
||||
default_warehouse_id uuid null references inventory.warehouses(id),
|
||||
lot_managed boolean not null default false,
|
||||
expiry_managed boolean not null default false,
|
||||
version bigint not null default 1 check(version > 0),
|
||||
created_at timestamptz not null default now(),
|
||||
created_by varchar(200) not null default 'system',
|
||||
updated_at timestamptz,
|
||||
updated_by varchar(200)
|
||||
);
|
||||
create unique index if not exists ux_catalog_item_details_barcode on catalog.item_details(barcode) where barcode <> '';
|
||||
create index if not exists ix_audit_item_lifecycle on audit.entries(aggregate_id, occurred_at desc) where aggregate_type='Item';
|
||||
create index if not exists ix_audit_order_lifecycle on audit.entries(aggregate_id, occurred_at desc) where aggregate_type='Order';
|
||||
|
||||
-- Backfill existing canonical item identities from the v4 read projection so v27 writes do not strand pre-existing rows.
|
||||
insert into catalog.item_details(item_id,category_name,specification,unit,barcode,default_warehouse_id,lot_managed,expiry_managed,version,created_by)
|
||||
select p.id,p.category_name,p.specification,p.unit,p.barcode,p.default_warehouse_id,p.lot_managed,p.expiry_managed,greatest(p.version,1),'migration-v27'
|
||||
from erp_item_search_projection p
|
||||
join catalog.items i on i.id=p.id
|
||||
on conflict(item_id) do nothing;
|
||||
Reference in New Issue
Block a user