feat: prepare ARR for controlled public deployment
This commit is contained in:
239
database/002_finance_monthly_backfill_support.sql
Normal file
239
database/002_finance_monthly_backfill_support.sql
Normal file
@@ -0,0 +1,239 @@
|
||||
-- Finance monthly-result backfill and booking-source enrichment support v1.0
|
||||
-- PostgreSQL 15+
|
||||
-- Target database: booking_test (connect to that database before running).
|
||||
-- This migration is additive; do not edit applied migration 001 in place.
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE finance.daily_versions
|
||||
ADD COLUMN ingestion_mode text NOT NULL DEFAULT 'opera_xml';
|
||||
|
||||
ALTER TABLE finance.daily_versions
|
||||
ADD CONSTRAINT daily_versions_ingestion_mode_valid CHECK (
|
||||
ingestion_mode IN ('opera_xml', 'monthly_backfill')
|
||||
);
|
||||
|
||||
ALTER TABLE finance.daily_versions
|
||||
DROP CONSTRAINT daily_version_active_shape;
|
||||
|
||||
ALTER TABLE finance.daily_versions
|
||||
ADD CONSTRAINT daily_version_active_shape CHECK (
|
||||
version_status <> 'active'
|
||||
OR (
|
||||
business_date IS NOT NULL
|
||||
AND validated_at IS NOT NULL
|
||||
AND activated_at IS NOT NULL
|
||||
AND failure_code IS NULL
|
||||
AND (
|
||||
(
|
||||
ingestion_mode = 'opera_xml'
|
||||
AND daily_report_file_id IS NOT NULL
|
||||
AND result_json_file_id IS NOT NULL
|
||||
)
|
||||
OR ingestion_mode = 'monthly_backfill'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX daily_versions_source_date_mode_unique
|
||||
ON finance.daily_versions (
|
||||
source_file_id,
|
||||
business_date,
|
||||
ingestion_mode
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN finance.daily_versions.ingestion_mode IS
|
||||
'opera_xml for normal deterministic processing; monthly_backfill only for an explicitly approved processed-workbook backfill.';
|
||||
|
||||
ALTER TABLE finance.daily_records
|
||||
ADD COLUMN source_worksheet text,
|
||||
ADD COLUMN source_row_no integer,
|
||||
ADD COLUMN booking_source_row_id bigint,
|
||||
ADD COLUMN booking_source_match_status text NOT NULL DEFAULT 'not_checked';
|
||||
|
||||
ALTER TABLE finance.daily_records
|
||||
ADD CONSTRAINT daily_records_source_worksheet_valid CHECK (
|
||||
source_worksheet IS NULL OR btrim(source_worksheet) <> ''
|
||||
),
|
||||
ADD CONSTRAINT daily_records_source_row_no_valid CHECK (
|
||||
source_row_no IS NULL OR source_row_no > 1
|
||||
),
|
||||
ADD CONSTRAINT daily_records_source_coordinate_shape CHECK (
|
||||
(source_worksheet IS NULL AND source_row_no IS NULL)
|
||||
OR (source_worksheet IS NOT NULL AND source_row_no IS NOT NULL)
|
||||
),
|
||||
ADD CONSTRAINT daily_records_booking_source_status_valid CHECK (
|
||||
booking_source_match_status IN (
|
||||
'matched',
|
||||
'unmatched',
|
||||
'missing_group_code',
|
||||
'conflict',
|
||||
'not_checked'
|
||||
)
|
||||
),
|
||||
ADD CONSTRAINT daily_records_booking_source_group_fk
|
||||
FOREIGN KEY (booking_source_row_id, group_code_key)
|
||||
REFERENCES booking.booking_source_rows(id, group_code_key),
|
||||
ADD CONSTRAINT daily_records_booking_source_match_shape CHECK (
|
||||
(
|
||||
booking_source_match_status = 'matched'
|
||||
AND group_code_key IS NOT NULL
|
||||
AND booking_source_row_id IS NOT NULL
|
||||
)
|
||||
OR (
|
||||
booking_source_match_status IN ('unmatched', 'conflict')
|
||||
AND group_code_key IS NOT NULL
|
||||
AND booking_source_row_id IS NULL
|
||||
)
|
||||
OR (
|
||||
booking_source_match_status = 'missing_group_code'
|
||||
AND group_code_key IS NULL
|
||||
AND booking_source_row_id IS NULL
|
||||
)
|
||||
OR (
|
||||
booking_source_match_status = 'not_checked'
|
||||
AND booking_source_row_id IS NULL
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX daily_records_source_coordinate_unique
|
||||
ON finance.daily_records (
|
||||
daily_version_id,
|
||||
source_worksheet,
|
||||
source_row_no
|
||||
)
|
||||
WHERE source_worksheet IS NOT NULL;
|
||||
|
||||
CREATE INDEX daily_records_booking_source_row_idx
|
||||
ON finance.daily_records (booking_source_row_id)
|
||||
WHERE booking_source_row_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX daily_records_booking_source_status_idx
|
||||
ON finance.daily_records (booking_source_match_status);
|
||||
|
||||
COMMENT ON COLUMN finance.daily_records.source_worksheet IS
|
||||
'Exact source worksheet for processed-workbook backfills; null for direct XML rows.';
|
||||
|
||||
COMMENT ON COLUMN finance.daily_records.source_row_no IS
|
||||
'One-based source worksheet row number; null for direct XML rows.';
|
||||
|
||||
COMMENT ON COLUMN finance.daily_records.booking_source_row_id IS
|
||||
'Accepted booking attachment source row selected by exact normalized RES_COMMENT Group Code.';
|
||||
|
||||
COMMENT ON COLUMN finance.daily_records.booking_source_match_status IS
|
||||
'Source-layer booking lookup status, separate from Agent parse-version matching.';
|
||||
|
||||
ALTER TABLE finance.report_versions
|
||||
ADD COLUMN as_of_date date NOT NULL,
|
||||
ADD COLUMN processor_version text NOT NULL,
|
||||
ADD COLUMN rule_set_sha256 character(64),
|
||||
ADD COLUMN result_schema_version text NOT NULL DEFAULT '1.0';
|
||||
|
||||
ALTER TABLE finance.report_versions
|
||||
ADD CONSTRAINT report_versions_as_of_date_valid CHECK (
|
||||
as_of_date BETWEEN period_start AND period_end
|
||||
),
|
||||
ADD CONSTRAINT report_versions_processor_version_valid CHECK (
|
||||
btrim(processor_version) <> ''
|
||||
),
|
||||
ADD CONSTRAINT report_versions_rule_set_sha256_valid CHECK (
|
||||
rule_set_sha256 IS NULL
|
||||
OR rule_set_sha256 ~ '^[0-9a-f]{64}$'
|
||||
),
|
||||
ADD CONSTRAINT report_versions_result_schema_version_valid CHECK (
|
||||
btrim(result_schema_version) <> ''
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN finance.report_versions.as_of_date IS
|
||||
'Latest business date included in this report artifact, distinct from its natural reporting period end.';
|
||||
|
||||
COMMENT ON COLUMN finance.report_versions.processor_version IS
|
||||
'Deterministic report generator or approved backfill adapter version.';
|
||||
|
||||
CREATE TABLE finance.report_booking_sources (
|
||||
report_version_id bigint NOT NULL
|
||||
REFERENCES finance.report_versions(id),
|
||||
group_code_key text NOT NULL CHECK (
|
||||
group_code_key = upper(btrim(group_code_key))
|
||||
AND group_code_key <> ''
|
||||
),
|
||||
booking_source_row_id bigint NOT NULL,
|
||||
PRIMARY KEY (report_version_id, group_code_key),
|
||||
CONSTRAINT report_booking_source_row_fk
|
||||
FOREIGN KEY (booking_source_row_id, group_code_key)
|
||||
REFERENCES booking.booking_source_rows(id, group_code_key)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE finance.report_booking_sources IS
|
||||
'Freezes booking attachment source rows used by a report before Agent parse versions exist or are required.';
|
||||
|
||||
CREATE OR REPLACE VIEW finance.v_company_report_source AS
|
||||
SELECT
|
||||
current_version.business_date,
|
||||
records.id AS daily_record_id,
|
||||
records.channel_key,
|
||||
records.company_key,
|
||||
records.company_name,
|
||||
records.block_code,
|
||||
records.group_code_key,
|
||||
records.res_comment,
|
||||
booking_source.hotel_raw AS type_of_room,
|
||||
records.no_of_rooms AS actual_room_quantity,
|
||||
records.arrival,
|
||||
records.departure,
|
||||
records.nights,
|
||||
records.real_price,
|
||||
records.total_price,
|
||||
records.booking_parse_version_id,
|
||||
records.booking_match_status
|
||||
FROM finance.current_daily_versions AS current_version
|
||||
JOIN finance.daily_records AS records
|
||||
ON records.daily_version_id = current_version.daily_version_id
|
||||
LEFT JOIN booking.booking_source_rows AS booking_source
|
||||
ON booking_source.id = records.booking_source_row_id
|
||||
WHERE records.outcome = 'retained';
|
||||
|
||||
CREATE VIEW finance.v_channel_booking_enriched AS
|
||||
SELECT
|
||||
current_version.business_date,
|
||||
records.daily_version_id,
|
||||
records.id AS daily_record_id,
|
||||
records.source_worksheet,
|
||||
records.source_row_no,
|
||||
records.channel_key,
|
||||
records.company_key,
|
||||
records.company_name,
|
||||
records.rate_code,
|
||||
records.normalized_rate_code,
|
||||
records.block_code,
|
||||
records.group_code_key,
|
||||
records.res_comment,
|
||||
records.room_category_label AS opera_room_category_label,
|
||||
booking_source.hotel_raw AS booking_type_of_room_raw,
|
||||
records.booking_source_row_id,
|
||||
records.booking_source_match_status,
|
||||
records.adults,
|
||||
records.children,
|
||||
records.no_of_rooms,
|
||||
records.arrival,
|
||||
records.departure,
|
||||
records.nights,
|
||||
records.effective_rate_amount,
|
||||
records.real_price,
|
||||
records.total_price,
|
||||
records.kb_amount,
|
||||
records.pricing_method
|
||||
FROM finance.current_daily_versions AS current_version
|
||||
JOIN finance.daily_records AS records
|
||||
ON records.daily_version_id = current_version.daily_version_id
|
||||
LEFT JOIN booking.booking_source_rows AS booking_source
|
||||
ON booking_source.id = records.booking_source_row_id
|
||||
WHERE records.outcome = 'retained';
|
||||
|
||||
COMMENT ON VIEW finance.v_company_report_source IS
|
||||
'Privacy-minimized company-report facts; type_of_room is the matched booking attachment source text, never Opera ROOM_CATEGORY_LABEL.';
|
||||
|
||||
COMMENT ON VIEW finance.v_channel_booking_enriched IS
|
||||
'Privacy-minimized active channel facts with Opera room category and booking original type_of_room kept as separate fields.';
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user