Files
canteen-asset-tracker/docs/FIELD_MAP.md
T

226 lines
9.2 KiB
Markdown

# Field Map — Canteen Asset Tracker
Every column in `assets.db`, how each pipeline touches it.
## Source Legend
| Source | Pipeline | File(s) | How to Run |
|--------|----------|---------|------------|
| **MSFS** | ADB pull → Dynamics 365 extraction | `pull_msfs_data.py``sync_assets_to_canteen.py` | Button on extraction viewer `:8910` **Manual** |
| **CANT** | Cantaloupe staged import | `cantaloupe_sync.py` in admin server | Dashboard upload **Manual** |
| **SEED** | Seed Excel import | `parser.py``db_writer.py``sync.py` | CLI `--mode=seed` or `--mode=update` **Manual** |
| **APP** | Main app (technician check-in, photo, GPS) | `server.py` | Used by techs in field |
| **ADMIN** | Admin dashboard edits | `admin_server.py` | Dashboard Cleanup/Mass Edit |
## Policy Legend
- **always** — pipeline overwrites this field every run
- **if_empty** — only filled when currently NULL/empty
- **never** — pipeline never touches this column
- **if_numeric** — only overwritten if value is bare number (Cantaloupe default ID)
---
## 🔵 MSFS Sync — Field Policy
| Column | Policy | Dynamics 365 Source |
|--------|--------|---------------------|
| `serial_number` | always | `hsl_serialnumber` |
| `connect_id` | always | `hsl_connectid` |
| `equipment_id` | always | `hsl_equipmentid` |
| `canteen_connect_guid` | always | `hsl_canteenconnectguid` |
| `barcode` | always | `hsl_barcode` |
| `manufacturer` | always | `hsl_manufacturertext` |
| `model` | always | `hsl_modeltext` or `hsl_modelname` |
| `route_name` | always | `hsl_routeidname` |
| `install_date` | always | `hsl_opendate` |
| `device` | always | `msdyn_deviceid` |
| `name` | if_numeric | `msdyn_name` or `hsl_name` |
| `company` | if_empty | Account `name` |
| `customer_name` | if_empty | Account `name` |
| `place` | if_empty | Account `address1_city` |
| `address` | if_empty | Account `address1_line1` + `line2` |
| `make` | **never** | _(uses `manufacturer` → separate column)_ |
| `building_name` | **never** | — |
| `floor` | **never** | — |
| `room` | **never** | — |
| `building_number` | **never** | — |
| `trailer_number` | **never** | — |
| `location_area` | **never** | — |
| `latitude` | **never** | _(mapped but only if NULL in code)_ |
| `longitude` | **never** | _(mapped but only if NULL in code)_ |
| `photo_path` | **never** | — |
| `category` | **never** | — |
| `status` | **never** | — |
| `description` | **never** | — |
> All other columns (sales data, alerts, disney flags, etc.) are **never** touched by MSFS sync.
---
## 🟡 Cantaloupe Sync — Field Policy
| Column | Policy | Notes |
|--------|--------|-------|
| `machine_id` | never (match key) | — |
| `serial_number` | always | — |
| `name` | always | — |
| `description` | always | — |
| `category` | always | Validated against categories table |
| `status` | always | Validated against statuses |
| `make` | always | — |
| `model` | always | — |
| `address` | always | — |
| `building_name` | always | — |
| `building_number` | always | — |
| `floor` | always | — |
| `room` | always | — |
| `trailer_number` | always | — |
| `walking_directions` | always | — |
| `map_link` | always | — |
| `parking_location` | always | — |
| `photo_path` | always | — |
| `latitude` | always | — |
| `longitude` | always | — |
| `geofence_radius_meters` | always | — |
| `dex_report_date` | always | — |
| `deployed` | always | — |
| `pulled_date` | always | — |
| `customer_id` | always | Via customer name lookup/creation |
| `location_id` | always | Via location name lookup/creation |
| `is_disney` | always | Derived from customer name prefix `D-` |
> **No preserve logic.** Any non-empty field in the import file overwrites the existing value.
---
## 🟢 Seed Import — Field Policy (db_writer.py)
### Seed Mode (`--mode=seed`)
**DESTROYS AND RE-INSERTS** — `DELETE FROM assets` first, then inserts all rows fresh.
### Update Mode (`--mode=update`)
Upsert by `machine_id`. **MSFS is primary data source** — seed fills gaps only.
**ALL fields** are treated as ONLY_IF_EMPTY for existing records:
| Column | Policy | Excel Source / Generation |
|--------|--------|--------------------------|
| `serial_number` | if_empty | Serial Number column + OCR corrections |
| `name` | if_empty | Generated: `{make} @ {place}` |
| `make` | if_empty | Parsed from Type column |
| `model` | if_empty | Parsed from Type column |
| `class` | if_empty | Parsed from Type column |
| `is_glass_front` | if_empty | Parsed from Type column |
| `company` | if_empty | Parsed from Customer column |
| `place` | if_empty | Parsed from Place column |
| `building_name` | if_empty | Parsed from Place column |
| `suite` | if_empty | Parsed from Place column |
| `room` | if_empty | Parsed from Place column |
| `trailer` | if_empty | Parsed from Place column |
| `floor` | if_empty | Parsed from Place column |
| `zone` | if_empty | Parsed from Place column |
| `zone_type` | if_empty | Parsed from Place column |
| `address` | if_empty | Address column |
| `location_area` | if_empty | City column |
| `latitude` | if_empty | Excel (existing GPS preserved) |
| `longitude` | if_empty | Excel (existing GPS preserved) |
| `photo_path` | if_empty | Excel (existing photo preserved) |
| `install_date` | if_empty | Added Date (existing date preserved) |
| `deployed` | if_empty | Deployed column (existing preserved) |
| `pulled_date` | if_empty | Pulled Date (existing preserved) |
| `dex_report_date` | if_empty | Last Dex Report Time |
| `disney_park` | if_empty | Parsed from Customer column |
| `disney_area` | if_empty | Derived from disney_park |
| `remote_pricing_status` | if_empty | Status column |
| `alerts` | if_empty | Alerts column + Coil/Product Alerts |
| `telemetry_provider` | if_empty | Parsed from Device column |
| `card_reader_brand` | if_empty | Parsed from Device column |
| `priority` | if_empty | Computed from yearly_sales |
| `yearly_sales` | if_empty | Yearly Sales column |
| `monthly_sales` | if_empty | Monthly Sales column |
| `weekly_sales` | if_empty | Weekly Sales column |
| `days_since_restock` | if_empty | Days Since Restock column |
| `prepick_group` | if_empty | Prepick Group column |
| `has_cashless` | if_empty | Has Cashless column |
> **New assets** (not found in DB by machine_id) are inserted with full seed data.
> **Existing assets**: only empty/null fields are filled. Any field already populated by MSFS, cleanup, or field use is preserved.
---
## 🟠 Seed Import — Sync (`sync.py` → main app DB)
After `db_writer.py`, this script pushes the seed DB into the main app's schema.
**Now follows ONLY_IF_EMPTY policy** for existing assets — same as db_writer update mode.
| Column | Policy |
|--------|--------|
| `machine_id` | never (match key) |
| `serial_number` | if_empty |
| `name` | if_empty |
| `description` | if_empty |
| `category` | if_empty |
| `status` | if_empty |
| `make` | if_empty |
| `model` | if_empty |
| `address` | if_empty |
| `building_name` | if_empty |
| `building_number` | if_empty |
| `floor` | if_empty |
| `room` | if_empty |
| `trailer_number` | if_empty |
| `walking_directions` | if_empty |
| `map_link` | if_empty |
| `parking_location` | if_empty |
| `photo_path` | if_empty |
| `latitude` | if_empty |
| `longitude` | if_empty |
| `geofence_radius_meters` | if_empty |
| `disney_park` | if_empty |
| `is_disney` | if_empty |
| `dex_report_date` | if_empty |
| `install_date` | if_empty |
| `deployed` | if_empty |
| `pulled_date` | if_empty |
| `customer_id` | if_empty (via upsert customers table) |
| `location_id` | if_empty (via upsert locations table) |
> **Also creates/updates** `customers` and `locations` tables.
---
## ⚪ Admin Tools — Policy
### Batch Update (Cleanup page)
- `make`, `model`, `name`, `company`, `place`, `building_name`, `building_number`, `floor`, `room`, `location_area`, `address`, `category`, `status`, `customer_name`
- **Always overwrites** with user-provided value.
### Clear GPS
- `latitude`, `longitude` — set to NULL
### Mass Edit (existing tool)
- Same allowed fields as Batch Update.
---
## ⚫ Main App (Tech Field) — What Touches What
| Action | Fields Affected |
|--------|----------------|
| Barcode scan check-in | `updated_at` |
| GPS check-in | `latitude`, `longitude`, `updated_at` |
| Photo upload | `photo_path`, `updated_at` |
| OCR scan (name search) | (read-only) |
| Register new asset | All columns with defaults |
---
## Pipeline Activity Status
| Pipeline | Currently Scheduled? | Frequency | Risk to Cleanup |
|----------|--------------------|-----------|-----------------|
| **MSFS Sync** | 🔴 **Manual only** | On-demand via extraction viewer | ✅ GPS never touched. Preserves name/company/place/building/floor. Only `model` overwritten. |
| **Cantaloupe Sync** | 🔴 **No cron** | Manual upload on admin dashboard | ⚠️ Preserves GPS now (fixed — none from Cantaloupe). Still overwrites other fields. |
| **Seed Import** `--mode=update` | 🔴 **Manual CLI only** | `python3 main.py -m update` | ✅ **GPS never touched** (check-in only). Fills gaps only for other fields. |
| **Seed Import** `--mode=seed` | 🔴 **Manual CLI only** | `python3 main.py -m seed` | 🔥 **NUCLEAR**`DELETE FROM assets`. Full re-insert. GPS set to NULL. |