Files
wyndham-ARR/arr-opera-daily-ingest/references/business-rules.md
2026-07-29 16:38:05 +08:00

4.9 KiB

Business Rules

Fixed processing order

  1. Validate the invocation and parse one fixed RES_DETAIL XML.
  2. Require exactly one XML business date and keep one audit record for every G_RESERVATION in XML order.
  3. Require RATE_CODE so whitelist membership is knowable.
  4. Classify a trimmed, uppercased rate code outside the whitelist as excluded_rate_code.
  5. Validate every whitelist candidate; classify invalid rows as validation_failed.
  6. Deduplicate valid candidates by DISP_ROOM_NO + ARRIVAL; retain the first XML occurrence and classify later occurrences as duplicate pointing to the first source sequence.
  7. Compute integer NIGHTS = DEPARTURE - ARRIVAL. Zero is legal; negative is invalid.
  8. Validate the complete fixed price table, then apply the approved zero-price exception or exact three-key match. Classify unmatched candidates as price_unmatched.
  9. Assign channel_key and conditional kb_amount as row facts.
  10. Generate the 19-column daily XLSX, result.json, and path-free structured-result.json from the same in-memory records.
  11. Independently replay the XML, rules, prices, routing, workbook, artifacts, outcomes, and structured records before reporting success.

Any row-level validation or price error fails the formal invocation. On failure, already excluded, duplicate, or price-unmatched outcomes remain explicit. Other candidates that did not reach a validated result become validation_failed with BATCH_NOT_VALIDATED.

Rate-code whitelist

GRPA1, GRPA2, GRPA3, GRPA4, GRP1, WHO1, WHO2, WHO3, WHO4, LTLT, LBLT, LBSM, LBMS, LBW1, LBKB, LBLS, WHKR2100B, GL2100B, GL2200KR, GLSPCB.

Reservation text

For reservation-level RES_COMMENT and TRACE_TEXT, take the first non-empty value in XML order. Leave blank when every candidate is empty. Never concatenate values or expand one reservation into multiple rows.

Company normalization

Normalize company identity for price matching and channel facts:

  1. trim, uppercase, and keep only A-Z and 0-9;
  2. use fixed substring keywords:
    • LIANTAILIAN TAI
    • QBDQBD
    • RAINBOWRAINBOW/AI
    • FENGRUNFENGRUN
    • HANATOUR or HANAHANA TOUR
    • HONGTAIHONGTAI
    • GUANGZHOUGOEASY or GOEASYGUANGZHOU GO EASY
  3. when no keyword matches, use the compact normalized name;
  4. when more than one keyword group matches, fail instead of guessing.

This is deterministic keyword matching, not edit distance.

Pricing

Use bundled 价格对照.xlsx as a fixed, non-user-editable rule source.

  • Normalize RATE_CODE with upper(trim(value)).
  • Compare amounts numerically, so 900 equals 900.00.
  • Reject negative reference amounts/totals, invalid rows, ambiguous companies, and every duplicate normalized COMPANY + RATE_CODE + Opera amount key.
  • Before the normal lookup, set numeric REAL PRICE = 0 only when:
    • company identity is RAINBOW/AI or GUANGZHOU GO EASY; and
    • rate code is LBMS or LBSM.
  • The exception ignores EFFECTIVE_RATE_AMOUNT.
  • Every other retained row must exactly match normalized COMPANY_NAME + RATE_CODE + EFFECTIVE_RATE_AMOUNT.
  • Write reference 总价 as static REAL PRICE.
  • Write static TOTAL PRICE = REAL PRICE * NO_OF_ROOMS * NIGHTS.
  • An unmatched candidate fails the entire invocation.

Channel facts

Keep original XML COMPANY_NAME; assign only the derived channel_key.

Condition channel_key
QBD QBD
LIAN TAI and rate in {LBLT, LTLT} LIANTAI-FIT
LIAN TAI and another whitelisted rate LIANTAI-GROUP
RAINBOW/AI or GUANGZHOU GO EASY, any rate DY-AI-Easy-KB
FENGRUN FENGRUN
any other company sanitized actual company name

For other companies, trim, remove : \ / ? * [ ], and truncate to 31 characters. Resolve different-company collisions with -2, -3, and so on. Set kb_amount = NO_OF_ROOMS * 100 only for DY-AI-Easy-KB; do not multiply by nights and do not add it to TOTAL PRICE.

Business date, XLSX, and types

  • Derive the business date only from XML group dates, never runtime time or filename.
  • Daily filename and sheet name: M.D.xlsx and M.D.
  • Preserve retained XML order.
  • Use the bundled daily template; do not add monthly or channel worksheets.
  • Headers occupy row 1; data begins row 2.
  • Write dates as real Excel dates with DD-MMM-YY.
  • Write counts and prices as static numbers, never formulas.
  • Write identifiers and descriptive fields as text.
  • Require ADULTS >= 0, CHILDREN >= 0, NO_OF_ROOMS > 0, EFFECTIVE_RATE_AMOUNT >= 0, and DEPARTURE >= ARRIVAL.
  • DEPARTURE = ARRIVAL yields NIGHTS = 0 and TOTAL PRICE = 0.
  • Preserve allowed blank fields and complete selected comment/trace text.

Prohibited behavior

Do not access OSS, embed credentials, generate/update a monthly workbook, query/write a database, infer Group Code from BLOCK_CODE, or derive structured facts by reopening the generated XLSX.