# Field Contracts

## XML structure

Root:

`/RES_DETAIL`

Reservations:

`/RES_DETAIL/LIST_G_GROUP_BY1/G_GROUP_BY1/LIST_G_RESERVATION/G_RESERVATION`

| XML source | Output field |
|---|---|
| `ADULTS` | `ADULTS` |
| `BLOCK_CODE` | `BLOCK_CODE` |
| `CF_CHILDREN` | `CHILDREN` |
| `COMPANY_NAME` | `COMPANY_NAME` |
| `CONFIRMATION_NO` | `CONFIRMATION_NO` |
| `DISP_ROOM_NO` | `DISP_ROOM_NO` |
| `EFFECTIVE_RATE_AMOUNT` | `EFFECTIVE_RATE_AMOUNT` |
| `FULL_NAME` | `FULL_NAME` |
| first non-empty `LIST_G_COMMENT_RESV_NAME_ID/.../RES_COMMENT` | `RES_COMMENT` |
| first non-empty `LIST_G_DEPT_ID/.../TRACE_TEXT` | `TRACE_TEXT` |
| `NO_OF_ROOMS` | `NO_OF_ROOMS` |
| `PRODUCTS` | `PRODUCTS` |
| `RATE_CODE` | `RATE_CODE` |
| `ROOM_CATEGORY_LABEL` | `ROOM_CATEGORY_LABEL` |
| `TRUNC_BEGIN` | `ARRIVAL` |
| `TRUNC_END` | `DEPARTURE` |
| computed | `NIGHTS` |
| price rule | `REAL PRICE` |
| computed | `TOTAL PRICE` |

Group date candidates are `GROUPBY1_SORT_COL` (`YYYYMMDD`) and `GROUPBY1_COL` (`DD-MM-YY`). Both must agree when both exist. Every whitelist candidate `ARRIVAL` must equal the single group business date.

## Daily XLSX: exactly 19 columns

1. `BLOCK_CODE`
2. `ADULTS`
3. `CHILDREN`
4. `COMPANY_NAME`
5. `CONFIRMATION_NO`
6. `DISP_ROOM_NO`
7. `EFFECTIVE_RATE_AMOUNT`
8. `FULL_NAME`
9. `RES_COMMENT`
10. `TRACE_TEXT`
11. `NO_OF_ROOMS`
12. `PRODUCTS`
13. `RATE_CODE`
14. `ROOM_CATEGORY_LABEL`
15. `ARRIVAL`
16. `DEPARTURE`
17. `NIGHTS`
18. `REAL PRICE`
19. `TOTAL PRICE`

`TOTAL PRICE = REAL PRICE * NO_OF_ROOMS * NIGHTS`. All derived values are static numbers.

## Direct XML lineage

- `source_sequence`: one-based XML reservation order.
- `source_location`: `reservation[N]`.
- `source_worksheet`: null.
- `source_row_no`: null.
- `channel_key`: deterministic downstream channel fact; it is not an XML source worksheet.

## Derived keys

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

When trimmed `RES_COMMENT` is empty, `group_code_key` is null and `booking_source_match_status` is `missing_group_code`. Never substitute `BLOCK_CODE`, confirmation number, or another value.

## Required whitelist-candidate values

`RATE_CODE`, `COMPANY_NAME`, `CONFIRMATION_NO`, `DISP_ROOM_NO`, `EFFECTIVE_RATE_AMOUNT`, `FULL_NAME`, `ADULTS`, `CF_CHILDREN`, `NO_OF_ROOMS`, `ARRIVAL`, and `DEPARTURE` must be present and valid.

`BLOCK_CODE`, `PRODUCTS`, `ROOM_CATEGORY_LABEL`, `RES_COMMENT`, and `TRACE_TEXT` may be blank.

The complete record field list and conditional nullability rules are authoritative in [structured-result.schema.json](structured-result.schema.json).
