MEGAINFRA-WEBAPP/src/lib/pb-types.ts

228 lines
5.8 KiB
TypeScript

/**
* Typed shapes matching the 12 MEGA INFRASTRUCTURE collections on
* `https://pb.mega.infra.apphub.co.za`. Keep in sync with the live
* schema — re-run `scripts/pb-bootstrap-schema.py` after changes.
*/
import type { RecordModel } from "./pb";
export interface SiteRecord extends RecordModel {
collectionName: "sites";
project_code: string;
name: string;
duration: string;
contract_value: number;
work_hours: number;
hours_per_week: number;
wages_hours_per_day: number;
/** ISO date (YYYY-MM-DD) the project started/starts. Optional. */
job_start_date?: string;
expand?: Record<string, never>;
}
export type WorkerType = "permanent" | "casual";
export interface EmployeeRecord extends RecordModel {
collectionName: "employees";
project_code: string;
site: string; // relation id
name: string;
employee_no: string;
position: string;
rate_per_hour: number;
hours_per_day_standard: number;
worker_type: WorkerType;
expand?: { site?: SiteRecord };
}
export type UnitType = "fuel" | "plant";
export interface VehicleRecord extends RecordModel {
collectionName: "vehicles";
project_code: string;
site: string;
description: string;
standard_work_hours: string | number;
fuel_limits: string;
qr_code_id: string;
cost_per_hour: number;
unit_type: UnitType;
expand?: { site?: SiteRecord };
}
export interface PlantRecord extends VehicleRecord {
// Deprecated alias — Plant has been merged into Vehicles.
}
export interface FixedCostRecord extends RecordModel {
collectionName: "fixed_costs";
project_code: string;
site: string;
date_logged: string;
description: string;
cost: number;
notes: string;
is_recurring?: boolean;
expand?: { site?: SiteRecord };
}
export type BatchType =
| "timesheets"
| "fuel_logs"
| "purchase_orders"
| "delivery_notes"
| "plant_hours";
export type BatchStatus =
| "pending"
| "processing"
| "processed"
| "failed"
| "dead";
export interface DataImportBatchRecord extends RecordModel {
collectionName: "data_import_batches";
batch_type: BatchType;
content_type?: string;
original_file: string;
date_uploaded: string;
uploaded_by: string;
status?: BatchStatus; // nullable for legacy T/F rows
attempts?: number;
last_error?: string;
last_attempt_at?: string;
next_retry_at?: string;
processed_at?: string;
processed_record_id?: string;
}
export interface PurchaseOrderRecord extends RecordModel {
collectionName: "purchase_orders";
purchase_order_number: string;
project_code: string;
site: string;
supplier: string;
required_by_date: string;
ordered_by: string;
items_count: number;
total_value: number;
purchase_order_file: string;
raw_ocr_data: unknown;
is_verified: boolean;
expand?: { site?: SiteRecord };
}
export interface PurchaseOrderItemRecord extends RecordModel {
collectionName: "purchase_order_items";
purchase_order: string;
material_code: string;
cost_code: string;
description: string;
qty: number;
qty_outstanding: number;
unit: string;
rate: number;
disc_percent: number;
vat_percent: number;
amount: number;
expand?: { purchase_order?: PurchaseOrderRecord };
}
export interface TimesheetRecord extends RecordModel {
collectionName: "timesheets";
employee: string;
batch_reference: string;
date_logged: string;
hours_worked: number;
expand?: { employee?: EmployeeRecord; batch_reference?: DataImportBatchRecord };
}
export interface FuelLogRecord extends RecordModel {
collectionName: "fuel_logs";
project_code: string;
site: string;
vehicle: string;
batch_reference: string;
date_logged: string;
litres: number;
odo_reading: number;
/**
* "Latest Consumption" value as reported by the pump sheet (typically
* litres/100km or litres/hour). Persisted verbatim from the source
* Excel/CSV rather than recomputed, so the audit trail reflects what
* the fuel team actually filed. Optional — the Aug 2026 sample has
* ~9% empty cells, which we preserve as `undefined` rather than
* fabricating a value. Added in the 2026-08-24 migration.
*/
latest_consumption?: number;
expand?: {
site?: SiteRecord;
vehicle?: VehicleRecord;
batch_reference?: DataImportBatchRecord;
};
}
export interface PlantHoursLogRecord extends RecordModel {
collectionName: "plant_hours_log";
date_logged: string;
vehicle_qr: string;
vehicle: string;
site: string;
project_code: string;
start_date: string;
end_date: string;
start_time?: string;
end_time?: string;
start_hours: number;
end_hours: number;
hours: number;
notes?: string;
hour_meter_budget: number;
cost_per_hour: number;
value: number;
over_budget: boolean;
hours_mismatch: boolean;
vehicle_unit_type_at_import: UnitType;
batch_reference?: string;
expand?: {
vehicle?: VehicleRecord;
site?: SiteRecord;
batch_reference?: DataImportBatchRecord;
};
}
export interface DeliveryNoteRecord extends RecordModel {
collectionName: "delivery_notes";
delivery_note_number: string;
project_code: string;
site: string;
purchase_order: string;
date_received: string;
received_by: string;
slip_image: string;
raw_ocr_data: unknown;
is_verified: boolean;
processing_status?: DeliveryNoteProcessingStatus;
ocr_confidence?: number;
ocr_completed_at?: string;
received_by_name?: string;
expand?: { site?: SiteRecord; purchase_order?: PurchaseOrderRecord };
}
export type DeliveryNoteProcessingStatus =
| "pending"
| "ocr-complete"
| "auto-verified"
| "needs-review"
| "manual-verified"
| "failed";
export interface ReceivedItemRecord extends RecordModel {
collectionName: "received_items";
delivery_note: string;
purchase_order_item: string;
qty_received: number;
condition_notes: string;
expand?: {
delivery_note?: DeliveryNoteRecord;
purchase_order_item?: PurchaseOrderItemRecord;
};
}