t('inventory.title'), 'products' => InventoryService::products(), 'batches' => InventoryService::batches(), 'movements' => InventoryService::movements(), 'alerts' => InventoryService::alerts(), 'references' => [ 'medication' => $db ->query('SELECT id,name FROM ref_medications ORDER BY name COLLATE NOCASE') ->fetchAll(), 'dewormer' => $db->query('SELECT id,name FROM ref_dewormers ORDER BY name COLLATE NOCASE')->fetchAll(), 'vaccine' => $db->query('SELECT id,name FROM ref_vaccines ORDER BY name COLLATE NOCASE')->fetchAll(), ], 'suppliers' => $db ->query( "SELECT DISTINCT dc.id,dc.name FROM directory_contacts dc JOIN directory_contact_roles r ON r.contact_id=dc.id WHERE dc.deleted_at IS NULL AND dc.kind='organization' AND r.role IN ('cabinet','fourriere') ORDER BY dc.name COLLATE NOCASE", ) ->fetchAll(), 'animals' => $db ->query( 'SELECT id,name FROM animals WHERE deleted_at IS NULL AND archived_at IS NULL ORDER BY name COLLATE NOCASE', ) ->fetchAll(), 'status' => (string) ($_GET['status'] ?? ''), 'error' => (string) ($_GET['error'] ?? ''), ]); } public static function product(): void { self::admin(); self::run(fn() => InventoryService::saveProduct($_POST), 'product'); } public static function receive(): void { self::admin(); self::run(fn() => InventoryService::receive($_POST), 'received'); } public static function move(): void { self::admin(); self::run(fn() => InventoryService::move($_POST), 'moved'); } private static function run(callable $fn, string $status): void { try { $fn(); header('Location: /settings/inventory?status=' . $status); } catch (RuntimeException $e) { header('Location: /settings/inventory?error=' . rawurlencode($e->getMessage())); } catch (Throwable $e) { header('Location: /settings/inventory?error=' . rawurlencode(SecurityService::publicError($e))); } exit(); } }