186 lines
8.8 KiB
MySQL
186 lines
8.8 KiB
MySQL
|
|
-- Migration 228: Manual subscription and invoice migration centre.
|
||
|
|
-- e-conomic data is deliberately referenced from Invoice Error Finder and never copied back.
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS migration_center_sessions (
|
||
|
|
id SERIAL PRIMARY KEY,
|
||
|
|
name VARCHAR(160) NOT NULL,
|
||
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active'
|
||
|
|
CHECK (status IN ('draft', 'active', 'completed', 'archived')),
|
||
|
|
economic_import_run_id INTEGER REFERENCES invoice_error_finder_import_runs(id) ON DELETE RESTRICT,
|
||
|
|
economic_snapshot_at TIMESTAMP,
|
||
|
|
read_only BOOLEAN NOT NULL DEFAULT FALSE,
|
||
|
|
created_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS migration_center_source_customers (
|
||
|
|
id BIGSERIAL PRIMARY KEY,
|
||
|
|
source_system VARCHAR(30) NOT NULL CHECK (source_system IN ('vtiger', 'simply', 'economic')),
|
||
|
|
source_customer_id VARCHAR(120) NOT NULL,
|
||
|
|
customer_no VARCHAR(80),
|
||
|
|
customer_name VARCHAR(255) NOT NULL,
|
||
|
|
cvr VARCHAR(32),
|
||
|
|
email VARCHAR(255),
|
||
|
|
raw_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
|
|
snapshot_hash CHAR(64) NOT NULL,
|
||
|
|
hub_customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE (source_system, source_customer_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS migration_center_source_subscriptions (
|
||
|
|
id BIGSERIAL PRIMARY KEY,
|
||
|
|
source_system VARCHAR(30) NOT NULL CHECK (source_system IN ('vtiger', 'simply')),
|
||
|
|
source_record_id VARCHAR(120) NOT NULL,
|
||
|
|
source_customer_id VARCHAR(120),
|
||
|
|
customer_no VARCHAR(80),
|
||
|
|
customer_name VARCHAR(255),
|
||
|
|
product_code VARCHAR(100),
|
||
|
|
product_name VARCHAR(500) NOT NULL,
|
||
|
|
amount NUMERIC(14,2) NOT NULL DEFAULT 0,
|
||
|
|
quantity NUMERIC(14,4) NOT NULL DEFAULT 1,
|
||
|
|
billing_frequency VARCHAR(40),
|
||
|
|
start_date DATE,
|
||
|
|
end_date DATE,
|
||
|
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||
|
|
raw_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
|
|
snapshot_hash CHAR(64) NOT NULL,
|
||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE (source_system, source_record_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS migration_center_session_items (
|
||
|
|
id BIGSERIAL PRIMARY KEY,
|
||
|
|
session_id INTEGER NOT NULL REFERENCES migration_center_sessions(id) ON DELETE CASCADE,
|
||
|
|
entity_type VARCHAR(20) NOT NULL CHECK (entity_type IN ('subscription', 'invoice_line')),
|
||
|
|
source_system VARCHAR(30) NOT NULL CHECK (source_system IN ('vtiger', 'simply', 'economic')),
|
||
|
|
source_record_id VARCHAR(180) NOT NULL,
|
||
|
|
source_customer_id VARCHAR(120),
|
||
|
|
customer_no VARCHAR(80),
|
||
|
|
customer_name VARCHAR(255),
|
||
|
|
product_code VARCHAR(100),
|
||
|
|
product_name VARCHAR(500) NOT NULL,
|
||
|
|
amount NUMERIC(14,2) NOT NULL DEFAULT 0,
|
||
|
|
quantity NUMERIC(14,4) NOT NULL DEFAULT 1,
|
||
|
|
billing_frequency VARCHAR(40),
|
||
|
|
period_from DATE,
|
||
|
|
period_to DATE,
|
||
|
|
invoice_no VARCHAR(80),
|
||
|
|
invoice_date DATE,
|
||
|
|
source_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
|
|
source_hash CHAR(64) NOT NULL,
|
||
|
|
previous_source_payload JSONB,
|
||
|
|
match_status VARCHAR(30) NOT NULL DEFAULT 'new'
|
||
|
|
CHECK (match_status IN ('new', 'match_found', 'no_match', 'conflict', 'manual_review', 'source_changed')),
|
||
|
|
approval_status VARCHAR(20) NOT NULL DEFAULT 'pending'
|
||
|
|
CHECK (approval_status IN ('pending', 'approved', 'rejected', 'verified', 'ignored')),
|
||
|
|
hub_status VARCHAR(30) NOT NULL DEFAULT 'not_created'
|
||
|
|
CHECK (hub_status IN ('not_created', 'ready_for_creation', 'created_in_hub', 'linked_to_existing', 'verified')),
|
||
|
|
lock_status VARCHAR(25) NOT NULL DEFAULT 'unlocked'
|
||
|
|
CHECK (lock_status IN ('unlocked', 'locking_pending', 'locked', 'lock_failed')),
|
||
|
|
match_confidence NUMERIC(5,4),
|
||
|
|
match_explanation JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||
|
|
hub_customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
|
||
|
|
hub_sag_id INTEGER REFERENCES sag_sager(id) ON DELETE SET NULL,
|
||
|
|
hub_record_id INTEGER REFERENCES sag_subscriptions(id) ON DELETE SET NULL,
|
||
|
|
suggested_hub_record_id INTEGER REFERENCES sag_subscriptions(id) ON DELETE SET NULL,
|
||
|
|
creation_idempotency_key VARCHAR(120),
|
||
|
|
ignore_reason TEXT,
|
||
|
|
manual_note TEXT,
|
||
|
|
verified_at TIMESTAMP,
|
||
|
|
verified_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
locked_at TIMESTAMP,
|
||
|
|
locked_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE (session_id, entity_type, source_system, source_record_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
ALTER TABLE migration_center_session_items
|
||
|
|
ADD COLUMN IF NOT EXISTS creation_idempotency_key VARCHAR(120);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_mc_items_session_type ON migration_center_session_items(session_id, entity_type);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_mc_items_work_queue ON migration_center_session_items(session_id, lock_status, approval_status);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_mc_items_customer ON migration_center_session_items(session_id, hub_customer_id);
|
||
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_mc_items_creation_idempotency
|
||
|
|
ON migration_center_session_items(session_id, creation_idempotency_key)
|
||
|
|
WHERE creation_idempotency_key IS NOT NULL;
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS migration_center_matches (
|
||
|
|
id BIGSERIAL PRIMARY KEY,
|
||
|
|
session_item_id BIGINT NOT NULL REFERENCES migration_center_session_items(id) ON DELETE CASCADE,
|
||
|
|
matched_entity_type VARCHAR(30) NOT NULL,
|
||
|
|
matched_hub_id INTEGER NOT NULL,
|
||
|
|
confidence NUMERIC(5,4) NOT NULL,
|
||
|
|
rules JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||
|
|
approved BOOLEAN,
|
||
|
|
approved_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
approved_at TIMESTAMP,
|
||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE (session_item_id, matched_entity_type, matched_hub_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS migration_center_lock_operations (
|
||
|
|
id BIGSERIAL PRIMARY KEY,
|
||
|
|
session_item_id BIGINT NOT NULL REFERENCES migration_center_session_items(id) ON DELETE CASCADE,
|
||
|
|
status VARCHAR(25) NOT NULL CHECK (status IN ('pending', 'succeeded', 'failed')),
|
||
|
|
attempt_no INTEGER NOT NULL DEFAULT 1,
|
||
|
|
external_system VARCHAR(30),
|
||
|
|
request_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
|
|
response_payload JSONB,
|
||
|
|
error_message TEXT,
|
||
|
|
requested_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
completed_at TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS migration_center_audit_log (
|
||
|
|
id BIGSERIAL PRIMARY KEY,
|
||
|
|
session_id INTEGER REFERENCES migration_center_sessions(id) ON DELETE SET NULL,
|
||
|
|
session_item_id BIGINT REFERENCES migration_center_session_items(id) ON DELETE SET NULL,
|
||
|
|
entity_type VARCHAR(40) NOT NULL,
|
||
|
|
entity_id VARCHAR(180),
|
||
|
|
action VARCHAR(80) NOT NULL,
|
||
|
|
old_value JSONB,
|
||
|
|
new_value JSONB,
|
||
|
|
source_hash CHAR(64),
|
||
|
|
performed_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
ip_address VARCHAR(45),
|
||
|
|
success BOOLEAN NOT NULL DEFAULT TRUE,
|
||
|
|
error_message TEXT,
|
||
|
|
performed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_mc_audit_session_time ON migration_center_audit_log(session_id, performed_at DESC);
|
||
|
|
|
||
|
|
ALTER TABLE sag_subscriptions
|
||
|
|
ADD COLUMN IF NOT EXISTS migration_locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||
|
|
ADD COLUMN IF NOT EXISTS migration_locked_at TIMESTAMP,
|
||
|
|
ADD COLUMN IF NOT EXISTS migration_locked_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
ADD COLUMN IF NOT EXISTS migration_source_item_id BIGINT;
|
||
|
|
|
||
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_sag_subscription_migration_source
|
||
|
|
ON sag_subscriptions(migration_source_item_id)
|
||
|
|
WHERE migration_source_item_id IS NOT NULL;
|
||
|
|
|
||
|
|
INSERT INTO permissions (code, description, category) VALUES
|
||
|
|
('migration_center.view', 'View migration centre', 'migration_center'),
|
||
|
|
('migration_center.sessions', 'Create and manage migration sessions', 'migration_center'),
|
||
|
|
('migration_center.import', 'Import CRM subscription snapshots', 'migration_center'),
|
||
|
|
('migration_center.create', 'Create customers, cases and subscriptions from migration centre', 'migration_center'),
|
||
|
|
('migration_center.review', 'Link, verify and ignore migration records', 'migration_center'),
|
||
|
|
('migration_center.lock', 'Lock migration records', 'migration_center'),
|
||
|
|
('migration_center.retry', 'Retry failed external locks', 'migration_center'),
|
||
|
|
('migration_center.export', 'Export migration control reports', 'migration_center')
|
||
|
|
ON CONFLICT (code) DO NOTHING;
|
||
|
|
|
||
|
|
INSERT INTO group_permissions (group_id, permission_id)
|
||
|
|
SELECT g.id, p.id
|
||
|
|
FROM groups g
|
||
|
|
JOIN permissions p ON p.category = 'migration_center'
|
||
|
|
WHERE g.name = 'Administrators'
|
||
|
|
ON CONFLICT DO NOTHING;
|