240 lines
8.6 KiB
PL/PgSQL
240 lines
8.6 KiB
PL/PgSQL
-- 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;
|