Actualiser api/index.php
CI / PHP Syntax Check (push) Has been cancelled
CI / JavaScript Lint (push) Has been cancelled
CI / Docker Build Test (push) Has been cancelled
CI / Validate Translation Files (push) Has been cancelled
CI / Auto-merge develop → main (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
Security Scan (Trivy) / Trivy — Docker image scan (push) Has been cancelled
Security Scan (Trivy) / Trivy — Filesystem scan (push) Has been cancelled
CI / PHP Syntax Check (push) Has been cancelled
CI / JavaScript Lint (push) Has been cancelled
CI / Docker Build Test (push) Has been cancelled
CI / Validate Translation Files (push) Has been cancelled
CI / Auto-merge develop → main (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
Security Scan (Trivy) / Trivy — Docker image scan (push) Has been cancelled
Security Scan (Trivy) / Trivy — Filesystem scan (push) Has been cancelled
This commit is contained in:
+130
-8
@@ -21,6 +21,28 @@ function isValidLocation(PDO $db, string $location): bool {
|
|||||||
return isset($cache[$location]);
|
return isset($cache[$location]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidSubcategory(PDO $db, string $category, string $subcategory): bool {
|
||||||
|
static $cache = [];
|
||||||
|
if (!isset($cache[$category])) {
|
||||||
|
$stmt = $db->prepare("SELECT key FROM subcategories WHERE category = ?");
|
||||||
|
$stmt->execute([$category]);
|
||||||
|
$cache[$category] = array_flip($stmt->fetchAll(PDO::FETCH_COLUMN));
|
||||||
|
}
|
||||||
|
return isset($cache[$category][$subcategory]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRequiredSubcategoryCategories(PDO $db): array {
|
||||||
|
static $cache = null;
|
||||||
|
if ($cache === null) {
|
||||||
|
$stmt = $db->prepare("SELECT value FROM app_settings WHERE key = 'subcategory_required_categories'");
|
||||||
|
$stmt->execute();
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
$decoded = $row ? json_decode($row['value'], true) : null;
|
||||||
|
$cache = is_array($decoded) ? $decoded : ['bevande'];
|
||||||
|
}
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
|
||||||
const RECIPE_PANTRY_MIN_MATCH_SCORE = 80;
|
const RECIPE_PANTRY_MIN_MATCH_SCORE = 80;
|
||||||
const RECENTLY_EXHAUSTED_DAYS = 30;
|
const RECENTLY_EXHAUSTED_DAYS = 30;
|
||||||
/** How long to suppress auto-re-add after user bought an item (ms, synced with client blocklist). */
|
/** How long to suppress auto-re-add after user bought an item (ms, synced with client blocklist). */
|
||||||
@@ -942,6 +964,18 @@ try {
|
|||||||
case 'locations_update':
|
case 'locations_update':
|
||||||
locationsUpdate($db);
|
locationsUpdate($db);
|
||||||
break;
|
break;
|
||||||
|
case 'subcategories_list':
|
||||||
|
subcategoriesList($db);
|
||||||
|
break;
|
||||||
|
case 'subcategories_add':
|
||||||
|
subcategoriesAdd($db);
|
||||||
|
break;
|
||||||
|
case 'subcategories_remove':
|
||||||
|
subcategoriesRemove($db);
|
||||||
|
break;
|
||||||
|
case 'subcategories_update':
|
||||||
|
subcategoriesUpdate($db);
|
||||||
|
break;
|
||||||
case 'recipes_list':
|
case 'recipes_list':
|
||||||
recipesList($db);
|
recipesList($db);
|
||||||
break;
|
break;
|
||||||
@@ -2703,19 +2737,18 @@ function saveProduct(PDO $db): void {
|
|||||||
? $input['shopping_name']
|
? $input['shopping_name']
|
||||||
: computeShoppingName($input['name'], $input['category'] ?? '', $input['brand'] ?? '');
|
: computeShoppingName($input['name'], $input['category'] ?? '', $input['brand'] ?? '');
|
||||||
|
|
||||||
// Sous-catégorie obligatoire uniquement pour les boissons
|
// Sous-catégorie : validée et rendue obligatoire dynamiquement selon la config en base
|
||||||
$category = $input['category'] ?? '';
|
$category = $input['category'] ?? '';
|
||||||
$subcategory = trim($input['subcategory'] ?? '');
|
$subcategory = trim($input['subcategory'] ?? '');
|
||||||
$validSubcategories = ['vin', 'biere', 'spiritueux', 'soda', 'jus', 'eau', 'autre'];
|
if ($subcategory !== '' && !isValidSubcategory($db, $category, $subcategory)) {
|
||||||
if ($category === 'bevande') {
|
$subcategory = ''; // invalide pour cette catégorie -> ignorée plutôt que de planter
|
||||||
if ($subcategory === '' || !in_array($subcategory, $validSubcategories, true)) {
|
}
|
||||||
|
if (in_array($category, getRequiredSubcategoryCategories($db), true) && $subcategory === '') {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
echo json_encode(['error' => 'subcategory_required', 'message' => 'Sous-catégorie requise pour les boissons']);
|
echo json_encode(['error' => 'subcategory_required', 'message' => 'Sous-catégorie requise pour cette catégorie']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
$subcategory = $subcategory !== '' ? $subcategory : null;
|
||||||
$subcategory = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$barcode = normalizeProductBarcode($input['barcode'] ?? null);
|
$barcode = normalizeProductBarcode($input['barcode'] ?? null);
|
||||||
|
|
||||||
@@ -12082,6 +12115,95 @@ function locationsUpdate(PDO $db): void {
|
|||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function subcategoriesList(PDO $db): void {
|
||||||
|
$rows = $db->query("SELECT id, category, key, label, sort_order FROM subcategories ORDER BY category ASC, sort_order ASC, id ASC")->fetchAll();
|
||||||
|
echo json_encode(['success' => true, 'subcategories' => $rows]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function subcategoriesAdd(PDO $db): void {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||||
|
$category = trim($input['category'] ?? '');
|
||||||
|
$label = trim($input['label'] ?? '');
|
||||||
|
|
||||||
|
if ($category === '' || $label === '') {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'category and label required']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = mb_strtolower(trim($label));
|
||||||
|
$key = preg_replace('/[^a-z0-9]+/u', '_', $key);
|
||||||
|
$key = trim($key, '_');
|
||||||
|
if ($key === '') {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'invalid label']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare("SELECT id FROM subcategories WHERE category = ? AND key = ?");
|
||||||
|
$stmt->execute([$category, $key]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'subcategory already exists for this category']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare("SELECT COALESCE(MAX(sort_order), 0) FROM subcategories WHERE category = ?");
|
||||||
|
$stmt->execute([$category]);
|
||||||
|
$maxOrder = (int)$stmt->fetchColumn();
|
||||||
|
|
||||||
|
$stmt = $db->prepare("INSERT INTO subcategories (category, key, label, sort_order) VALUES (?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$category, $key, $label, $maxOrder + 1]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'id' => (int)$db->lastInsertId(), 'key' => $key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function subcategoriesRemove(PDO $db): void {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||||
|
$id = (int)($input['id'] ?? 0);
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'id required']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare("SELECT category, key FROM subcategories WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if (!$row) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'subcategory not found']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare("SELECT COUNT(*) FROM products WHERE category = ? AND subcategory = ?");
|
||||||
|
$stmt->execute([$row['category'], $row['key']]);
|
||||||
|
if ((int)$stmt->fetchColumn() > 0) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'subcategory still used by products']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->prepare("DELETE FROM subcategories WHERE id = ?")->execute([$id]);
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function subcategoriesUpdate(PDO $db): void {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||||
|
$id = (int)($input['id'] ?? 0);
|
||||||
|
$label = trim($input['label'] ?? '');
|
||||||
|
|
||||||
|
if ($id <= 0 || $label === '') {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'id and label required']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare("SELECT id FROM subcategories WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'subcategory not found']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->prepare("UPDATE subcategories SET label = ? WHERE id = ?")->execute([$label, $id]);
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
// ===== SHARED APP DATA FUNCTIONS =====
|
// ===== SHARED APP DATA FUNCTIONS =====
|
||||||
|
|
||||||
function appSettingsGet(PDO $db): void {
|
function appSettingsGet(PDO $db): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user