/** @return list */ function phone_inventory_status_options(): array { return ['available', 'reserved', 'sold']; } function phone_normalize_inventory_status(string $raw): string { $raw = strtolower(trim($raw)); return in_array($raw, phone_inventory_status_options(), true) ? $raw : 'available'; } function phone_inventory_status(array $phone): string { $status = strtolower(trim((string)($phone['inventory_status'] ?? '')); if (in_array($status, phone_inventory_status_options(), true)) { return $status; } return 'available'; } function phone_inventory_status_label(string $status): string { return match (phone_normalize_inventory_status($status)) { 'available' => 'Available', 'reserved' => 'Reserved', 'sold' => 'Sold', default => 'Available', }; } function phone_is_sold(array $phone): bool { return phone_inventory_status($phone) === 'sold'; } /** Listed on the public shop (not manually marked sold). Legacy stock-0/hidden still excluded. */ function phone_listed_on_shop(array $phone): bool { if (phone_is_sold($phone)) { return false; } return ($phone['status'] ?? '') === 'active' && (int)($phone['stock'] ?? 0) > 0; } function phone_normalize_new_battery(string $raw): string { $raw = strtolower(trim($raw)); return in_array($raw, ['yes', 'no'], true) ? $raw : ''; } function phone_new_battery_label(array $phone): string { return match (phone_normalize_new_battery((string)($phone['new_battery'] ?? ''))) { 'yes' => 'Yes', 'no' => 'No', default => '', }; } function hold_active_for_phone(string $phoneId): ?array { $phoneId = trim($phoneId); if ($phoneId === '') { return null; } foreach (json_db_read('holds.json')['holds'] ?? [] as $hold) { if ((string)($hold['phone_id'] ?? '') !== $phoneId) { continue; } $status = (string)($hold['status'] ?? ''); if ($status === 'pending' || $status === 'confirmed') { return $hold; } } return null; } function phone_reservation_blocks_reserve(array $phone): bool { return hold_active_for_phone((string)($phone['id'] ?? '')) !== null; } function phone_update_by_id(string $phoneId, callable $mutator): bool { $phoneId = trim($phoneId); if ($phoneId === '') { return false; } $ok = false; json_db_transaction('phones.json', static function (array $data) use ($phoneId, $mutator, &$ok): array { foreach ($data['phones'] as &$phone) { if ((string)($phone['id'] ?? '') !== $phoneId) { continue; } $phone = $mutator($phone); $phone['updated_at'] = date('c'); $ok = true; break; } unset($phone); return $data; }); return $ok; } function phone_sync_reserved_on_confirm(string $phoneId, string $holdId): void { if ($phoneId === '' || $holdId === '') { return; } phone_update_by_id($phoneId, static function (array $phone): array { $phone['inventory_status'] = 'reserved'; $phone['reserved_hold_id'] = $holdId; return $phone; }); } function phone_sync_available_on_hold_end(string $phoneId, string $holdId): void { if ($phoneId === '') { return; } phone_update_by_id($phoneId, static function (array $phone) use ($holdId): array { if (phone_inventory_status($phone) === 'sold') { return $phone; } $linked = (string)($phone['reserved_hold_id'] ?? ''); if ($linked !== '' && $linked !== $holdId) { return $phone; } $phone['inventory_status'] = 'available'; $phone['reserved_hold_id'] = ''; return $phone; }); } function phone_mark_sold(string $phoneId): bool { return phone_update_by_id($phoneId, static function (array $phone): array { $phone['inventory_status'] = 'sold'; $phone['reserved_hold_id'] = ''; $phone['sold_at'] = date('Y-m-d H:i:s'); $phone['stock'] = 0; $phone['stock_held'] = 0; $phone['status'] = phone_status_from_stock(0); return $phone; }); } function phone_mark_available(string $phoneId): bool { return phone_update_by_id($phoneId, static function (array $phone): array { $phone['inventory_status'] = 'available'; $phone['reserved_hold_id'] = ''; $phone['sold_at'] = ''; if ((int)($phone['stock'] ?? 0) <= 0) { $phone['stock'] = 1; } $phone['status'] = phone_status_from_stock((int)$phone['stock']); return $phone; }); } function phone_admin_hold_summary(string $phoneId): ?array { $hold = hold_active_for_phone($phoneId); if (!$hold) { return null; } $status = (string)($hold['status'] ?? ''); $customer = $hold['customer'] ?? []; return [ 'id' => (string)($hold['id'] ?? ''), 'memo' => (string)($hold['memo'] ?? ''), 'status' => $status, 'status_label' => hold_status_label($status), 'customer' => (string)($customer['name'] ?? ''), 'admin_url' => url('/admin/holds?status=' . rawurlencode($status === 'confirmed' ? 'confirmed' : 'pending')), ]; }