Files
wyndham-ARR/opera-daily-channel-report/references/structured-output.md
2026-07-29 16:38:05 +08:00

5.7 KiB
Raw Blame History

Structured Finance Output Contract

Purpose and compatibility

Each invocation has two JSON contracts with different consumers:

  • In legacy daily-monthly mode, result.json and structured-result.json use version 1.0.
  • In database-ingestion daily mode, both contracts use version 2.0, require processing_mode: "daily", and require the monthly output/artifact to be null.

Do not add database fields to result.json or derive database rows by reopening generated workbooks. The processor creates every requested workbook and every structured record from the same in-memory classified records. The structured file intentionally does not hash itself; it hashes result.json and the other requested artifacts, avoiding an impossible self-hash recursion.

Batch contract

The payload records:

  • business_date, processor_version, rule_set_sha256, and result_schema_version;
  • the four database counters and a five-outcome reconciliation;
  • deterministic channel/worksheet counts;
  • absolute local artifact paths, SHA-256 values, sizes, MIME types, and database-compatible file kinds;
  • every XML source reservation in original order;
  • structured errors on failed runs.

rule_set_sha256 is a deterministic manifest digest of the processor, independent validator, Skill contracts, fixed price table, and both workbook templates. processor_version changes when processor behavior or its payload contract changes.

activation_eligible: true means only that the file pipeline and independent validations passed. It is not proof that a database transaction committed, and it never switches finance.current_daily_versions by itself.

Per-source-record contract

Every /G_RESERVATION produces exactly one record with a continuous one-based source_sequence and source_location = reservation[N].

Direct Opera XML has no source Excel coordinate:

  • source_worksheet = null
  • source_row_no = null

The deterministic channel destination is channel_key, even when daily mode does not create a monthly workbook. source_worksheet/source_row_no are reserved for an explicitly approved workbook-backfill adapter and must not be fabricated for direct XML.

The only outcomes are:

  • retained
  • excluded_rate_code
  • duplicate
  • validation_failed
  • price_unmatched

Duplicate records point to the earlier retained candidate through duplicate_of_source_sequence. decision_codes preserve deterministic reasons such as whitelist exclusion, duplicate selection, exact price matching, the zero-price exception, or a validation error.

Derived keys are:

normalized_rate_code = upper(trim(rate_code))
group_code_key       = upper(trim(res_comment))
company_key          = deterministic company keyword normalization

An empty trimmed RES_COMMENT produces JSON null for group_code_key. Never substitute BLOCK_CODE, confirmation number, or another value. Non-empty Group Codes start with booking_source_match_status = not_checked; empty values use missing_group_code.

The database adapter performs the only approved booking-source lookup:

structured record group_code_key
  = booking.booking_source_rows.group_code_key

booking_type_of_room_raw
  = booking.booking_source_rows.hotel_raw

This is an exact normalized lookup, not fuzzy matching. room_category_label remains the Opera/XML actual room category and must never be overwritten with hotel_raw.

Price and worksheet mapping

  • XML/daily EFFECTIVE_RATE_AMOUNT becomes structured effective_rate_amount and monthly RATE_AMOUNT.
  • REAL PRICE becomes real_price.
  • TOTAL PRICE becomes total_price and always equals real_price * no_of_rooms * nights for retained rows.
  • nights may be zero when arrival and departure are the same date; the corresponding total_price is numeric zero. Negative nights are invalid.
  • KB100/晚/间) becomes kb_amount; it is present only for DY-AI-Easy-KB and equals no_of_rooms * 100.
  • The actual monthly worksheet becomes channel_key; direct XML still keeps source_worksheet = null.
  • pricing_method is price_reference_exact or zero_price_exception for retained rows.

Failure and version-switch boundary

Success requires XML parsing, filtering, deduplication, pricing, deterministic channel assignment, every requested workbook write, independent output validation, and structured completeness validation. Daily v2 validates only the daily workbook and requires no monthly artifact; legacy v1 additionally requires and validates the monthly workbook. A successful payload has no validation_failed or price_unmatched rows and satisfies:

source_rows = removed_by_rate_code + removed_as_duplicates + output_rows

On failure, activation_eligible is false, output_rows is zero, daily/monthly artifacts are null, and the exception/result artifacts remain available. Valid candidates that never reached a fully validated formal result are marked validation_failed with BATCH_NOT_VALIDATED; already excluded, duplicate, or price-unmatched conclusions remain explicit.

The future database adapter must:

  1. confirm current_database() = 'booking_test' before any write;
  2. re-hash every file and validate this payload;
  3. allocate a new immutable version for every accepted same-day rerun;
  4. write file objects, the daily version, all records, channel metrics, and exact Group Code lookup results in one transaction;
  5. activate the new daily version and supersede the prior one only at the end of that successful transaction;
  6. roll back fully on any error, leaving the previous current version unchanged.

This Skill does not execute database DDL or DML. Its Finance payload targets the current booking_test contract after migrations 002-004, while the file processor itself remains runnable without a live database connection.