7.8 KiB
Business Rules
Processing order
- Parse the fixed
RES_DETAILXML and require exactly one XML business date. - Create one in-memory source record for every reservation in XML order; no later stage may discard that audit record.
- Require every reservation to contain
RATE_CODEso whitelist membership is knowable. - Classify rows whose trimmed, uppercased
RATE_CODEis not in the whitelist asexcluded_rate_code. - Validate required fields on the remaining rows and classify invalid rows as
validation_failed. - Deduplicate remaining valid rows by
DISP_ROOM_NO + ARRIVAL; retain the first XML occurrence and classify later occurrences asduplicatewith a pointer to the first source sequence. - Preserve the relative XML order of all retained rows and compute static non-negative integer
NIGHTS = DEPARTURE - ARRIVAL. Same-day arrival and departure is a valid zero-night record; a departure before arrival is invalid. - Validate the entire bundled price table, then apply the approved zero-price exception or match and write static daily
REAL PRICE; calculate static dailyTOTAL PRICE = REAL PRICE * NO_OF_ROOMS * NIGHTS. Classify an unmatched row asprice_unmatched. - Assign deterministic channel keys and conditional KB values, then generate the daily workbook. Only legacy
daily-monthlymode also routes the same in-memory rows into a monthly workbook. - Write
result.jsonand the all-source-recordstructured-result.json, then independently validate every requested XLSX output, artifact hash, derived key, outcome reconciliation, and structured payload before reporting success.
Any row-level validation or price error fails the formal invocation. Rows that were valid candidates but could not reach a fully validated formal result become validation_failed with BATCH_NOT_VALIDATED; filtered, duplicate, and price-unmatched conclusions remain unchanged in the failed structured payload.
Rate-code whitelist
GRPA1, GRPA2, GRPA3, GRPA4, GRP1, WHO1, WHO2, WHO3, WHO4, LTLT, LBLT, LBSM, LBMS, LBW1, LBKB, LBLS, WHKR2100B, GL2100B, GL2200KR, GLSPCB.
Multi-value 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.
Price matching
Use the bundled latest references/价格对照.xlsx as a fixed, non-user-editable rule source.
- Normalize company identity on both the price-table side and the XML/output side before price matching or standard-channel routing:
- trim, uppercase, and remove every space and punctuation character, retaining only
A-Zand0-9; - use fixed substring keywords, not edit distance, to recognize
LIANTAI → LIAN TAI,QBD → QBD,RAINBOW → RAINBOW/AI,FENGRUN → FENGRUN,HANATOURorHANA → HANA TOUR,HONGTAI → HONGTAI, andGUANGZHOUGOEASYorGOEASY → GUANGZHOU GO EASY; - if no keyword group matches, use the compact normalized name as an exact identity;
- if more than one keyword group matches, fail rather than guessing.
- trim, uppercase, and remove every space and punctuation character, retaining only
- Match normalized
COMPANY_NAME + RATE_CODE + EFFECTIVE_RATE_AMOUNTto normalized price-tableCOMPANY'S NAME + RATE CODE + Opera展示的价格. - Trim and uppercase rate codes.
- Compare price amounts numerically, so
900equals900.00. - Reject negative Opera display amounts or total prices in the fixed price table.
- Before the three-key lookup, write numeric
REAL PRICE = 0when and only when both conditions hold:- normalized company identity is
RAINBOW/AIorGUANGZHOU GO EASY; and - normalized
RATE_CODEisLBMSorLBSM.
- normalized company identity is
- The zero-price exception ignores
EFFECTIVE_RATE_AMOUNT. It writes dailyREAL PRICE = 0and therefore dailyTOTAL PRICE = 0; the monthly row receives the same two numeric zeros. Other rate codes for those companies still require an exact normalized three-key price match. - Write the matched price-table
总价directly as dailyREAL PRICE, then calculate dailyTOTAL PRICE = REAL PRICE * NO_OF_ROOMS * NIGHTS. - Validate the complete normalized three-field price key before processing. Any duplicate, including same-total duplicates unused by the current XML, fails the entire run.
- Any unmatched retained reservation fails the entire run.
Channel routing
Standard routing uses the same normalized company identity described above. The original XML COMPANY_NAME remains unchanged in output cells.
| Condition | Worksheet |
|---|---|
Normalized identity QBD |
QBD |
Normalized identity LIAN TAI and RATE_CODE in {LBLT, LTLT} |
LIANTAI-FIT |
Normalized identity LIAN TAI and other whitelisted RATE_CODE |
LIANTAI-GROUP |
Normalized identity RAINBOW/AI or GUANGZHOU GO EASY (all rate codes) |
DY-AI-Easy-KB |
Normalized identity FENGRUN |
FENGRUN |
| Any other company | Sanitized actual company name |
For other companies, trim the name, remove : \\ / ? * [ ], and truncate to 31 characters. Reuse the existing sheet for the same actual company. If a different company collides after sanitizing/truncation, append -2, -3, and so on while shortening the base to keep the result at most 31 characters.
Business date and filenames
- Derive the date from the XML arrival/group date, not the runtime clock or upload filename.
- Daily filename and sheet name:
M.D.xlsxandM.D(for example7.20.xlsx). - Month key: XML
ARRIVALyear and month. - Monthly filename:
各渠道情况-YYYY年MM月-更新至M.D.xlsx. 更新至is the maximumARRIVALpresent anywhere in the monthly workbook, so an older backfill cannot move the filename backwards.
Monthly update
These rules apply only to legacy daily-monthly mode. The daily database-ingestion mode never reads or writes a monthly workbook.
- When no monthly base is supplied, start from the bundled blank monthly template.
- A supplied monthly base must already follow the current 19/20-column contract. Legacy 18-column monthly workbooks and the transitional 19-column KB layout are rejected rather than upgraded.
- Keep different natural months in different workbooks.
- Before inserting the current date, delete that
ARRIVALfrom every worksheet. - Insert the newly routed rows, then stably sort each sheet by
ARRIVALascending. - Preserve original daily/XML order within the same date.
- Preserve all other dates and their within-day order.
- Existing sheets retain their formatting. New company sheets copy the standard channel-page header and style.
- In every monthly row, write static
REAL PRICE = daily REAL PRICEand staticTOTAL PRICE = daily TOTAL PRICE. Independently enforceTOTAL PRICE = REAL PRICE * NO_OF_ROOMS * NIGHTSin both reports. - Only
DY-AI-Easy-KBhasKB(100/晚/间); write it as staticNO_OF_ROOMS * 100. Do not multiply KB byNIGHTS, and do not include KB inTOTAL PRICE.
Output types and formatting
- Use the daily and monthly templates independently; never copy columns by position between them.
- Headers are in row 1 and data starts in row 2.
- Store
ARRIVALandDEPARTUREas real Excel dates with display formatDD-MMM-YY. - Store adults, children, room count, nights, rates, real prices, totals, and KB as numbers.
- Store confirmation numbers, room numbers, codes, names, comments, and other identifiers as text.
- Write
NIGHTS,REAL PRICE,TOTAL PRICE, andKB(100/晚/间)as static values, never formulas. - Require
ADULTS >= 0,CHILDREN >= 0,NO_OF_ROOMS > 0,EFFECTIVE_RATE_AMOUNT >= 0, andDEPARTURE >= ARRIVALon every whitelist candidate.DEPARTURE = ARRIVALproducesNIGHTS = 0;DEPARTURE < ARRIVALis invalid. - Preserve complete comment/trace text and leave allowed empty values blank.
- Do not add filters, freezes, colors, or decorations absent from the templates.