# AeroFind v1 — Technical Documentation
**Version:** 1.0.3 · **Released:** April 25, 2026 · **Status:** Production Stable

---

## 1. System Overview

AeroFind v1 is a professional Ground Service Management (GSM) platform that automates the full lifecycle of baggage irregularities. It replaces manual agent overhead with algorithmic intelligence, connecting staff workflows, passenger self-service, courier dispatch, and international baggage tracing into a single unified system.

- **Language:** PHP 8.1+  
- **Database:** MySQL 8+ / MariaDB 10.4+ via PDO  
- **Architecture:** PHP monolith — no framework, maximum deployment portability  
- **Standards:** IATA AHM, EC 261/2004, GDPR Privacy-by-Design  

---

## 1.1 UI & Branding Engine (v1.0.3)

Starting with v1.0.2 and refined in v1.0.3, AeroFind implements a high-fidelity **Dynamic Branding Engine** and a **Premium Demo Experience**.

- **Visual Language**: "Premium Aviation" — utilizing glassmorphism, high-contrast typography (Outfit/Inter), and micro-animations.
- **Premium Demo Hub**: A completely redesigned `/demo` environment with unique, dynamic SaaS-themed portals for Staff, Admin, Passenger, Delivery, and Cabin.
- **Dynamic Tokens**: Centralized CSS variables (`--theme-primary`, `--theme-primary-hover`) injected via `dbGetThemeCSS()`.
- **Cross-Portal Parity**: Branding updates propagate instantly to the Guest, Staff, Courier, and Administrative portals.
- **Dark Mode**: System-wide dark mode support with tailored high-contrast surfaces for low-light aviation environments.

---

## 2. File Architecture

| File | Role |
|------|------|
| `index.php` | Passenger-facing SPA portal (PIR form, tracking, cabin L&F) |
| `staffportal.php` | Role-gated staff dashboard (all report management) |
| `admin.php` | Full system configuration panel |
| `api.php` | Central JSON API router — 60+ actions |
| `db.php` | PDO data layer, schema init, all helper functions |
| `db_config.php` | MySQL connection credentials |
| `worldtracer.php` | IATA WorldTracer REST/SOAP API client |
| `flights.php` | AviationStack client + NearestAirportFinder + geocoder |
| `dpd.php` | DPD shipping API client |
| `manage.php` | Token-authenticated passenger self-service portal |
| `track.php` | Public baggage status tracker |
| `lostreports.php` | Cabin Lost & Found management page |
| `cron_cleanup.php` | GDPR auto-purge cron job |
| `cron_dpd.php` | DPD tracking status auto-sync cron |
| `install.php` | First-run setup wizard |
| `seed_demo.php` | Demo data seeder |
| `airports.php` | IATA airport coordinate dataset |
| `privacy.php` / `terms.php` | Legal pages |
| `/reports/` | Static HTML + PDF PIR report store |
| `/uploads/cabin_items/` | Cabin item photos |
| `/reports/labels/` | DPD shipping label PDFs |

---

## 3. Database Schema

### Core Tables

| Table | Purpose |
|-------|---------|
| `users` | Staff accounts (username, password_hash, role) |
| `reports` | PIR records (passenger info, bag tags, address, status, WT ref) |
| `lost_items` | Cabin lost item reports |
| `found_items` | Cabin found item records |
| `bdo_assignments` | Courier/delivery assignments per report |
| `report_rush_legs` | Multi-leg rush flight routing |
| `airlines` | Airline branding and portal config |
| `airline_config` | Per-airline form field requirements |
| `bdo_couriers` | Courier partners (local, long-haul flags, max_range) |
| `bdo_airline_partners` | Airline↔courier partnership links |
| `tasks` | Internal staff tasks |
| `task_messages` | Task discussion thread messages |
| `audit_log` | Immutable event log (file_ref, user, action, details, timestamp) |
| `settings` | Key-value configuration store |
| `staff_emails` | Staff email list for notifications |
| `zip_geocache` | OSM Nominatim geocode result cache (zip+country → lat/lon) |

---

## 4. Key Functions (db.php)

| Function | Description |
|----------|-------------|
| `getDB()` | Returns singleton PDO connection (MySQL). Falls back gracefully in migration mode. |
| `initSchema($db)` | Creates all tables `IF NOT EXISTS`. Safe on re-run. All ALTER TABLE calls wrapped in try/catch. |
| `migrateExistingData($db)` | Backfills legacy data for new columns. |
| `dbGetSetting($key, $default)` | Read from `settings` KV store. |
| `dbSetSetting($key, $value)` | Write to `settings` KV store. |
| `dbGetAirlines()` | Returns all airlines with config joined. |
| `dbGetConfig()` | Returns airline config map. |
| `dbGetBDOCouriers()` | Returns all courier partners. |
| `dbGetAirlinePartners($airlineId)` | Returns couriers linked to a specific airline. |
| `dbAuditLog($ref, $user, $action, $details)` | Inserts immutable audit record. |
| `dbRebuildReportHTML($reportId)` | Regenerates static HTML report file from current DB data. Called after every data-changing operation. |
| `dbGetFaviconLink()` | Returns `<link>` tag for configured favicon. |
| `generateRefCode($prefix)` | Generates unique reference code (e.g. `LR-ABC123`). |
| `handleCabinPhotoUpload($field)` | Validates and saves cabin item photo to `/uploads/cabin_items/`. |
| `geocodeZip($zip, $country)` | Nominatim lookup with `zip_geocache` caching. Returns `['lat', 'lon']` or null. |
| `haversineDistance($lat1, $lon1, $lat2, $lon2)` | Returns km distance between two coordinates. |
| `getSmartAddress($report)` | Returns best delivery address (temp if valid/current, else permanent). |
91: | `dbGetThemePrimary()` | Returns configured primary hex color (default: `#e11d48`). |
92: | `dbGetThemePrimaryHover()` | Returns configured hover hex color (default: `#be123c`). |
93: | `dbGetThemeCSS()` | Injects dynamic `<style>` block with theme-aware CSS variables. |

---

## 5. API Reference (api.php)

All requests: `POST api.php` with JSON body `{"action": "...", ...}`  
All responses: `{"success": true|false, ...}`

### PIR / Report Actions
| Action | Key Params | Description |
|--------|-----------|-------------|
| `submitReport` | full form data | Create PIR, generate HTML, send email |
| `getReports` | — | All PIRs with audit logs |
| `getReport` | `id` | Single PIR by ID |
| `updatePassengerDetails` | `id`, any field | Staff inline edit, triggers HTML rebuild |
| `updatePassengerSelf` | `id`, `token`, fields | Token-auth passenger self-update |
| `updateBaggageStatus` | `id`, `status`, courier fields | Status update + BDO + email + HTML rebuild |
| `deleteReport` | `id` | Delete report + files (admin/supervisor) |
| `closeCase` | `id` | Mark CLOSED (supervisor+) |
| `trackBagReport` | `ref` | Public: find by tag/WT ref/ID |
| `generatePDF` | `id` | DomPDF generation |
| `getDashboardStats` | — | Count totals by status |

### WorldTracer Actions
| Action | Key Params | Description |
|--------|-----------|-------------|
| `submitToWorldTracer` | `report_id` | Create PIR + auto-sync BDO in WT |
| `autoSyncWT` | `report_id`, `tag`, `surname` | Query WT, update ref, patch HTML, email |
| `updateWTReference` | `report_id`, `wt_ref` | Manual WT ref save + email |
| `syncWTBDO` | `id`, `courier_name`, `courier_link` | Push BDO to WT |
| `syncWTReference` | `report_id`, `bag_tag` | Tag-only WT lookup |
| `testWorldTracerConnection` | — | Connectivity ping |
| `getWorldTracerConfig` | — | Masked key + enabled state |
| `setWorldTracerKey` | `api_key`, `base_url`, `enabled` | Save WT credentials |

### DPD Actions
| Action | Key Params | Description |
|--------|-----------|-------------|
| `createDPDLabel` | `id` | Create shipment, save label PDF, update status |
| `trackDPD` | `report_id` | Poll DPD, sync status back |

### BDO / Logistics Actions
| Action | Key Params | Description |
|--------|-----------|-------------|
| `getBDOSuggestion` | `report_id` | Decision engine: tier, mode, reason, distance |
| `getSuggestedFlights` | `id`/`city`/`zip` | AviationStack flight schedules |
| `getRushLegs` | `id` | Multi-leg rush routing |
| `getNearestAirport` | `id`/`city`/`zip` | Geocode → IATA code |
| `getZipDistance` | `zip1`, `zip2`, `country1`, `country2` | Haversine km distance |
| `deleteBDOAssignment` | `id` | Remove BDO, void WT BDO, reset status |
| `searchFlights` | `city`, `zip`, `country` | AviationStack search from home station |

### Cabin L&F Actions
| Action | Key Params | Description |
|--------|-----------|-------------|
| `addLostItem` | flight, seat, pax info, category, description | Passenger lost item report |
| `addFoundItem` | flight, found_by, category, description, storage | Staff found item record |
| `getLostItems` | — | All lost items + audit logs |
| `getFoundItems` | — | All found items + audit logs |
| `matchItems` | `lost_id`, `found_id` | Pair records, notify passenger |
| `unmatchItems` | `lost_id`, `found_id` | Unlink pair, send apology email |
| `updateLostItemStatus` | `id`, `status` | Update lost item status |
| `trackLostItem` | `reference_code` | Public tracking by ref code |
| `notifyPassenger` | `lost_id`, `message` | Manual staff email to passenger |
| `deleteLostItem` | `id` | Delete lost item |
| `deleteFoundItem` | `id` | Delete found item |

### Data Management Actions
| Action | Key Params | Description |
|--------|-----------|-------------|
| `purgeOldData` | `target_date`, purge type flags | GDPR data purge (7-day safety minimum) |
| `getAirlines` | — | All airlines |
| `addAirline` / `updateAirline` / `deleteAirline` | airline fields | Manage airlines |
| `getBDOCouriers` | — | All couriers |
| `addCourier` / `updateCourier` / `deleteCourier` | courier fields | Manage couriers |
| `getSettings` / `saveSettings` | key-value pairs | System configuration |

---

## 6. Flight Intelligence Module (flights.php)

### `FlightAPI` Class
| Method | Description |
|--------|-------------|
| `isConfigured()` | Returns true if AviationStack key is set |
| `getSchedules($origin, $dest)` | Tier-1: paid schedules API. Falls back to Tier-2: free flights API. Returns standardized flight array. |
| `getConnectingFlights($origin, $dest)` | Probes major hub airports for indirect routes |

### `NearestAirportFinder` Class
| Method | Description |
|--------|-------------|
| `resolvePassengerAirport($city, $zip, $country)` | 4-step resolution: direct IATA match → DB lookup → ZIP geocode → city geocode |
| `findNearest($lat, $lon)` | Haversine search against `airport_coords` local table |

---

## 7. WorldTracer Module (worldtracer.php)

### `WorldTracerAPI` Class
| Method | Description |
|--------|-------------|
| `isConfigured()` | Returns true if API key + base URL are set |
| `getConfigStatus()` | Returns config summary for admin display |
| `setApiKey($key)` | Persist API key to settings |
| `setBaseUrl($url)` | Persist base URL to settings |
| `testConnection()` | Ping WT API endpoint |
| `createAndSubmitPIR($reportId)` | Build PIR payload from DB, submit to WT |
| `createBDO($reportId, $courier, $parcelRef)` | Submit BDO/RUSH entry to WT |
| `voidBDO($reportId)` | Void existing BDO in WT |
| `queryBaggage($bagTag)` | Look up by bag tag only |
| `searchByTagAndSurname($tag, $surname)` | Look up by tag + surname (more precise) |
| `injectWTReference($reportId, $ref)` | Patch WT reference directly into static HTML file |

---

## 8. DPD Module (dpd.php)

### `DPD_API` Class
| Method | Description |
|--------|-------------|
| `createShipment($orderData)` | Create DPD shipment, returns `parcel_number` + `label_base64` |
| `getTrackingStatus($parcelNumber)` | Poll DPD tracking, returns normalized `aerofind_status` |

---

## 9. Email Notification Events

All email sending is controlled by per-event toggles in Admin → Settings:

| Setting Key | Event |
|-------------|-------|
| `email_lost_pax` | Lost item report submitted |
| `email_match_pax` | Lost item matched to found item |
| `email_umatch_pax` | Match reversed (apology email) |
| `email_notify_pax` | Manual staff notification |
| `email_status_pax` | Baggage delivery status update |
| `email_wt_pax` | WorldTracer reference assigned |
| `email_pod_pax` | Proof of Delivery confirmed (Passenger) |
| `email_pod_staff` | Proof of Delivery confirmed (Staff) |

---

## 10. GDPR Compliance

- **Data Retention:** Configurable in Admin. Defaults: anonymize after 30 days, delete after 90.
- **cron_cleanup.php:** Runs automatically. Anonymizes PIIs, deletes expired records.
- **Manual Purge:** `purgeOldData` API with selective flags (PIR, L&F, logs, tasks, messages). Safety: target date must be ≥7 days in past. Full wipe mode (`purge_all`) available for admin only.
- **Consent:** Privacy checkbox on PIR form and cabin L&F form. Links to `privacy.php`.
- **File Cleanup:** HTML/PDF report files and cabin photos deleted on record purge.

---

## 11. Deployment

1. Upload files to PHP 8.1+ web root
2. Configure `db_config.php` with MySQL credentials
3. Visit `install.php` for guided setup
4. Configure company settings in Admin panel
5. Optionally seed demo data via `seed_demo.php`
6. Set up cron jobs:
   - `php /path/to/cron_cleanup.php` — daily
   - `php /path/to/cron_dpd.php` — every 30 minutes

### Required PHP Extensions
- `pdo`, `pdo_mysql`, `curl`, `gd`, `mbstring`, `json`

---

## 12. Key Design Decisions

- **No framework:** Maximum portability. Runs on any cPanel/shared hosting with PHP 8+.
- **Static HTML reports:** Generated once and served as files — zero DB query on public view.
- **Singleton DB:** `getDB()` returns one PDO connection per request lifecycle.
- **`dbRebuildReportHTML`:** Called after every data mutation to keep files in sync. Critical for PDF accuracy.
- **Smart address:** `getSmartAddress()` prefers temp address while validity date is future. Falls back to permanent address automatically.
- **Prepared statements:** Every SQL query uses PDO prepared statements. No string interpolation in SQL.

---

*AeroFind v1 — Built for the ground. Designed for the passenger.*
