Document Details
Purpose
This catalog defines the business rules that shape validation, transformation, controls, and exception handling throughout the migration workflow.
View or Download
Document Preview
Business Rules
Evidence Labels
Rules marked Observed are enforced or documented in code, tests, or README. Rules marked Inferred describe likely business intent behind implementation behavior.
| ID | Rule | Enforcement point | Exceptions or notes | Evidence quality |
|---|---|---|---|---|
| BR-001 | Source Odin report must be an .xlsx workbook. |
inspect_inputs in src/odin_lunchtab/managed.py |
Other extensions are rejected. | Observed |
| BR-002 | Lunchtab users export must be a .csv file. |
inspect_inputs in src/odin_lunchtab/managed.py |
Other extensions are rejected. | Observed |
| BR-003 | Odin rows require account type, ID number, deposits, spendings, balance, and student name. | extract_odin_report in src/odin_lunchtab/workflow.py |
Missing fields become malformed exceptions. | Observed |
| BR-004 | Odin student names must use Surname, FirstName format. |
split_student_name in src/odin_lunchtab/workflow.py |
Invalid names become malformed exceptions. | Observed |
| BR-005 | Monetary values must parse to finite decimals. | _money_text, _decimal, and _decimal_or_none in workflow modules |
Invalid values block or are excluded depending on workflow stage. | Observed |
| BR-006 | Lunchtab users export must include FirstName, PreferredName, Surname, LoginBarcode, DefaultFamilyCode, and DefaultFamilyBalanceAmount. |
REQUIRED_LUNCHTAB_HEADERS in src/odin_lunchtab/managed.py |
Profile-specific target fields can add required headers. | Observed |
| BR-007 | OdinBalanceAmount is inserted immediately after DefaultFamilyBalanceAmount in the transfer CSV. |
run_workflow in src/odin_lunchtab/workflow.py |
The original Lunchtab rows are preserved with the added balance column. | Observed |
| BR-008 | A duplicate Odin account identity is not auto-matched. | match_balances_detailed in src/odin_lunchtab/workflow.py |
Reason is duplicate Odin ID Number. |
Observed |
| BR-009 | A crosswalk match must resolve to exactly one Lunchtab target and pass name validation. | match_balances_detailed in src/odin_lunchtab/workflow.py |
Stale, duplicate, or name-failed crosswalks become exceptions. | Observed |
| BR-010 | Identifier matches must pass surname and first/preferred-name validation. | names_compatible in src/odin_lunchtab/workflow.py |
Surnames normalize exactly; first names can match by compatible prefix. | Observed |
| BR-011 | Matching profiles can target LoginBarcode, ExternalId, or EmailUsername only. | APPROVED_TARGET_FIELDS in src/odin_lunchtab/profiles.py |
Unsupported targets are rejected. | Observed |
| BR-012 | Email username matching uses the complete local part before a single @, and optional domains must be exact and valid. |
parse_email_address, valid_email_domain, and _email_candidate_allowed |
Malformed or disallowed emails do not produce candidates. | Observed |
| BR-013 | A matching profile cannot contain duplicate enabled rule names or duplicate crosswalk source identities. | MatchingProfile.validate in src/odin_lunchtab/profiles.py |
Validation raises an error. | Observed |
| BR-014 | The Legacy Default profile is read-only and cannot be modified or deleted. | save_profile and delete_profile in src/odin_lunchtab/profiles.py |
It preserves exact ID-to-LoginBarcode matching plus name fallback. | Observed |
| BR-015 | Multiple matching rules resolving to different Lunchtab users are quarantined. | match_balances_detailed in src/odin_lunchtab/workflow.py |
Reason is matching rules resolved to different Lunchtab users. |
Observed |
| BR-016 | Multiple Odin records matching one Lunchtab user are quarantined. | match_balances_detailed in src/odin_lunchtab/workflow.py |
Prevents multiple balance writes to one user row. | Observed |
| BR-017 | no Lunchtab match records are excluded from the focused manual-review exception file. |
run_workflow in src/odin_lunchtab/workflow.py |
They remain in the full exceptions report. | Observed |
| BR-018 | Reconciliation audit control must satisfy valid source dollars equals matched dollars plus valid exception dollars. | write_reconciliation_audit_control in src/odin_lunchtab/audit_control.py |
A failure raises an error in run_workflow. |
Observed |
| BR-019 | Managed runs publish only after output files and manifest are created. | run_managed_workflow, run_initial_balances_workflow, and run_manual_reconciliation_workflow |
Staging folders are removed on failure. | Observed |
| BR-020 | Manual reconciliation edited transfer must have the same headers and row count as the original transfer. | audit_manual_reconciliation in src/odin_lunchtab/manual_reconciliation.py |
Structural mismatch is rejected. | Observed |
| BR-021 | Filling a previously blank OdinBalanceAmount is treated as a manual resolution. |
audit_manual_reconciliation |
Severity is REVIEW. |
Observed |
| BR-022 | Invalid manual balance amounts block downstream use. | audit_manual_reconciliation |
Summary status becomes BLOCKED. |
Observed |
| BR-023 | Changing automated balances, family codes, identity fields, or other fields requires review. | audit_manual_reconciliation |
Summary status becomes REVIEW unless invalid amounts block. |
Observed |
| BR-024 | InitialBalances export must contain exactly FamilyName,FamilyCode,Amount in order. |
inspect_initial_balances_inputs in src/odin_lunchtab/initial_balances.py |
Noncanonical headers are rejected. | Observed |
| BR-025 | InitialBalances transfer aggregates populated Odin balances by case-sensitive DefaultFamilyCode. |
_analyze_initial_balances in src/odin_lunchtab/initial_balances.py |
Multiple source rows can update one family. | Observed |
| BR-026 | Blank target InitialBalances amounts are treated as zero. | _decimal(..., blank_as_zero=True) |
Only when parsing target amount. | Observed |
| BR-027 | InitialBalances import file is omitted when blocking exceptions exist. | process_initial_balances in src/odin_lunchtab/initial_balances.py |
Audit and exception artifacts are still produced. | Observed |
| BR-028 | InitialBalances control totals must pass before publication. | process_initial_balances and write_initial_balances_audit_control |
Failure raises an integrity error. | Observed |
| BR-029 | CSV reads support UTF-8, Windows-1252, and BOM-marked UTF-16, and reject binary-looking or corrupted text. | _decode_csv_bytes and _validate_csv_text in src/odin_lunchtab/workflow.py |
Generated CSVs use UTF-8 with BOM. | Observed |
| BR-030 | Source files are not modified by the application. | README and workflow behavior | Outputs are written to separate result folders. | Observed |
| BR-031 | Logs and manifests should avoid student-level row data and absolute source paths. | README and manifest structures in managed.py and initial_balances.py |
File hashes and names are stored for provenance. | Observed |
| BR-032 | Staff should use generated imports only after reviewing summaries and resolving blocking exceptions. | README and run summary next-action text | Operational approval outside the app remains a human activity. | Inferred |