-- 100kmph MariaDB schema (Drift tablet schema version 6, snake_case). -- Apply as an admin user. The app user has no DDL and no DELETE. -- Synced rows are soft-deleted via deleted_at. Do not hard-delete them. -- No foreign keys: the tablet may push children before parents. CREATE TABLE IF NOT EXISTS staff_members ( id CHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, pin_hash VARCHAR(255) NOT NULL, role VARCHAR(64) NOT NULL, active TINYINT(1) NOT NULL DEFAULT 1, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_staff_members_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS shop_settings ( id CHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, address VARCHAR(4000) NOT NULL DEFAULT '', phone VARCHAR(64) NOT NULL DEFAULT '', gstin VARCHAR(64) NOT NULL DEFAULT '', tax_percent DOUBLE NOT NULL DEFAULT 18, sms_bill_template VARCHAR(4000) NOT NULL, sms_status_template VARCHAR(4000) NOT NULL, -- Last-write-wins. v1 does not allocate invoice numbers on the server. Two tablets incrementing this will clash. next_invoice_number INT NOT NULL DEFAULT 1 COMMENT 'Last-write-wins. v1 does not allocate invoice numbers on the server. Two tablets incrementing this will clash.', expected_open VARCHAR(16) NOT NULL DEFAULT '09:00', expected_close VARCHAR(16) NOT NULL DEFAULT '18:00', late_grace_minutes INT NOT NULL DEFAULT 120, public_base_url VARCHAR(512) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_shop_settings_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS work_days ( id CHAR(36) NOT NULL, day_key VARCHAR(32) NOT NULL, started_at DATETIME(3) NOT NULL, ended_at DATETIME(3) NULL, started_by_staff_id CHAR(36) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_work_days_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS attendance_records ( id CHAR(36) NOT NULL, work_day_id CHAR(36) NOT NULL, staff_id CHAR(36) NOT NULL, check_in_at DATETIME(3) NULL, check_out_at DATETIME(3) NULL, notes VARCHAR(4000) NOT NULL DEFAULT '', updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_attendance_records_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS short_leaves ( id CHAR(36) NOT NULL, work_day_id CHAR(36) NOT NULL, staff_id CHAR(36) NOT NULL, from_at DATETIME(3) NOT NULL, to_at DATETIME(3) NOT NULL, minutes INT NOT NULL, reason VARCHAR(4000) NOT NULL DEFAULT '', updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_short_leaves_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS overtime_logs ( id CHAR(36) NOT NULL, work_day_id CHAR(36) NOT NULL, staff_id CHAR(36) NOT NULL, from_at DATETIME(3) NULL, to_at DATETIME(3) NULL, minutes INT NOT NULL, reason VARCHAR(4000) NOT NULL DEFAULT '', updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_overtime_logs_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS customers ( id CHAR(36) NOT NULL, phone VARCHAR(64) NOT NULL, name VARCHAR(255) NOT NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_customers_phone (phone), KEY idx_customers_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS vehicles ( id CHAR(36) NOT NULL, customer_id CHAR(36) NOT NULL, registration VARCHAR(64) NOT NULL, vin VARCHAR(64) NOT NULL DEFAULT '', make VARCHAR(255) NOT NULL DEFAULT '', model_name VARCHAR(255) NOT NULL DEFAULT '', `year` INT NULL, engine_capacity INT NULL, bike_model_id CHAR(36) NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_vehicles_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS job_cards ( id CHAR(36) NOT NULL, vehicle_id CHAR(36) NOT NULL, customer_id CHAR(36) NOT NULL, brought_by_customer_id CHAR(36) NULL, complaint VARCHAR(4000) NOT NULL, stage VARCHAR(64) NOT NULL, engineer_notes VARCHAR(4000) NOT NULL DEFAULT '', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_job_cards_customer (customer_id), KEY idx_job_cards_vehicle (vehicle_id), KEY idx_job_cards_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS odo_readings ( id CHAR(36) NOT NULL, read_at DATETIME(3) NOT NULL, vehicle_id CHAR(36) NOT NULL, reading_km INT NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_odo_readings_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS job_card_events ( id CHAR(36) NOT NULL, job_card_id CHAR(36) NOT NULL, stage VARCHAR(64) NOT NULL, note VARCHAR(4000) NOT NULL DEFAULT '', `at` DATETIME(3) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_job_card_events_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS part_types ( id CHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_part_types_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS bike_models ( id CHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, brand VARCHAR(255) NOT NULL DEFAULT '', updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_bike_models_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS bike_groups ( id CHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, part_type_id CHAR(36) NULL, code VARCHAR(64) NOT NULL DEFAULT '', notes VARCHAR(4000) NOT NULL DEFAULT '', updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_bike_groups_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS bike_group_members ( id CHAR(36) NOT NULL, group_id CHAR(36) NOT NULL, bike_model_id CHAR(36) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_bike_group_members_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS catalog_parts ( id CHAR(36) NOT NULL, part_type_id CHAR(36) NOT NULL, bike_model_id CHAR(36) NULL, group_id CHAR(36) NULL, is_universal TINYINT(1) NOT NULL DEFAULT 0, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_catalog_parts_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS catalog_part_models ( id CHAR(36) NOT NULL, catalog_part_id CHAR(36) NOT NULL, bike_model_id CHAR(36) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_catalog_part_models_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS part_variants ( id CHAR(36) NOT NULL, catalog_part_id CHAR(36) NOT NULL, grade VARCHAR(64) NOT NULL, brand VARCHAR(255) NOT NULL DEFAULT '', sku VARCHAR(128) NOT NULL DEFAULT '', cost DOUBLE NOT NULL DEFAULT 0, sale_price DOUBLE NOT NULL, qty_on_hand DOUBLE NOT NULL DEFAULT 0, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_part_variants_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS job_line_items ( id CHAR(36) NOT NULL, job_card_id CHAR(36) NOT NULL, kind VARCHAR(64) NOT NULL, variant_id CHAR(36) NULL, description VARCHAR(4000) NOT NULL, qty DOUBLE NOT NULL, unit_price DOUBLE NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_job_line_items_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS invoices ( id CHAR(36) NOT NULL, number VARCHAR(64) NOT NULL, public_token CHAR(36) NOT NULL, job_card_id CHAR(36) NULL, customer_id CHAR(36) NULL, kind VARCHAR(64) NOT NULL, subtotal DOUBLE NOT NULL, tax_percent DOUBLE NOT NULL, tax_amount DOUBLE NOT NULL, total DOUBLE NOT NULL, snapshot_json LONGTEXT NOT NULL, publish_status VARCHAR(64) NOT NULL, created_at DATETIME(3) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY uq_invoices_public_token (public_token), KEY idx_invoices_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS invoice_lines ( id CHAR(36) NOT NULL, invoice_id CHAR(36) NOT NULL, description VARCHAR(4000) NOT NULL, qty DOUBLE NOT NULL, unit_price DOUBLE NOT NULL, line_total DOUBLE NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_invoice_lines_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS payments ( id CHAR(36) NOT NULL, invoice_id CHAR(36) NOT NULL, method VARCHAR(64) NOT NULL, amount DOUBLE NOT NULL, `at` DATETIME(3) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_payments_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS demand_logs ( id CHAR(36) NOT NULL, part_type_id CHAR(36) NOT NULL, bike_model_id CHAR(36) NOT NULL, variant_id CHAR(36) NULL, qty DOUBLE NOT NULL, reason VARCHAR(4000) NOT NULL DEFAULT '', created_at DATETIME(3) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_demand_logs_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS sms_outbox ( id CHAR(36) NOT NULL, to_phone VARCHAR(64) NOT NULL, body VARCHAR(4000) NOT NULL, status VARCHAR(64) NOT NULL, error VARCHAR(4000) NOT NULL DEFAULT '', invoice_id CHAR(36) NULL, job_card_id CHAR(36) NULL, created_at DATETIME(3) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_sms_outbox_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS publish_outbox ( id CHAR(36) NOT NULL, invoice_id CHAR(36) NOT NULL, public_token CHAR(36) NOT NULL, payload_json LONGTEXT NOT NULL, status VARCHAR(64) NOT NULL, error VARCHAR(4000) NOT NULL DEFAULT '', created_at DATETIME(3) NOT NULL, updated_at DATETIME(3) NOT NULL, deleted_at DATETIME(3) NULL, is_dirty TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY idx_publish_outbox_sync (updated_at, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Legacy unauthenticated bill publish. Not a synced table. -- The posted token is the public token, never invoices.id. CREATE TABLE IF NOT EXISTS bill_snapshots ( token CHAR(36) NOT NULL, payload_json LONGTEXT NOT NULL, updated_at DATETIME(3) NOT NULL, PRIMARY KEY (token) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;