From e52fb1d7d19ce28ed48306d59d5222bc5efd4e27 Mon Sep 17 00:00:00 2001 From: Luigi Negri Date: Fri, 30 Jan 2026 11:08:58 +0000 Subject: [PATCH] Update server.js --- server.js | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/server.js b/server.js index 7646e8e..556a3ad 100644 --- a/server.js +++ b/server.js @@ -81,5 +81,174 @@ app.delete('/api/cocktails/:id', (req, res) => { }); }); +// ========================= +// INVENTARIO BOTTIGLIE +// ========================= + +// Get all inventory items +app.get('/api/inventory', (req, res) => { + const sql = ` + SELECT ii.*, ing.name AS ingredient_name + FROM inventory_items ii + JOIN ingredients ing ON ing.id = ii.ingredient_id + WHERE ii.is_active = 1 + ORDER BY ing.name, ii.brand + `; + db.all(sql, [], (err, rows) => { + if (err) return res.status(500).json({ error: err.message }); + res.json(rows); + }); +}); + +// Get single inventory item +app.get('/api/inventory/:id', (req, res) => { + const { id } = req.params; + const sql = ` + SELECT ii.*, ing.name AS ingredient_name + FROM inventory_items ii + JOIN ingredients ing ON ing.id = ii.ingredient_id + WHERE ii.id = ? + `; + db.get(sql, [id], (err, row) => { + if (err) return res.status(500).json({ error: err.message }); + if (!row) return res.status(404).json({ error: 'Not found' }); + res.json(row); + }); +}); + +// Create inventory item +app.post('/api/inventory', (req, res) => { + const { + ingredient_id, + brand, + label_name, + barcode, + bottle_size_ml, + bottle_type, + origin_country, + region, + style, + abv, + current_level_fraction, + current_level_ml, + condition, + location, + storage_notes, + opened_at, + purchase_price, + purchase_currency, + purchase_date, + supplier, + par_level_bottles, + reorder_point_bottles, + reorder_qty_bottles, + status, + is_collectible, + personal_rating, + tasting_notes + } = req.body; + + const sql = ` + INSERT INTO inventory_items ( + ingredient_id, brand, label_name, barcode, + bottle_size_ml, bottle_type, origin_country, region, style, abv, + current_level_fraction, current_level_ml, condition, location, storage_notes, opened_at, + purchase_price, purchase_currency, purchase_date, supplier, + par_level_bottles, reorder_point_bottles, reorder_qty_bottles, status, + is_collectible, personal_rating, tasting_notes, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) + `; + + const params = [ + ingredient_id, brand, label_name, barcode, + bottle_size_ml, bottle_type, origin_country, region, style, abv, + current_level_fraction, current_level_ml, condition, location, storage_notes, opened_at, + purchase_price, purchase_currency, purchase_date, supplier, + par_level_bottles, reorder_point_bottles, reorder_qty_bottles, status, + is_collectible, personal_rating, tasting_notes + ]; + + db.run(sql, params, function (err) { + if (err) return res.status(500).json({ error: err.message }); + res.status(201).json({ id: this.lastID }); + }); +}); + +// Update inventory item +app.put('/api/inventory/:id', (req, res) => { + const { id } = req.params; + const body = req.body; + + const fields = [ + 'ingredient_id','brand','label_name','barcode', + 'bottle_size_ml','bottle_type','origin_country','region','style','abv', + 'current_level_fraction','current_level_ml','condition','location','storage_notes','opened_at', + 'purchase_price','purchase_currency','purchase_date','supplier', + 'par_level_bottles','reorder_point_bottles','reorder_qty_bottles','status', + 'is_collectible','personal_rating','tasting_notes' + ]; + + const setClauses = []; + const params = []; + + fields.forEach(f => { + if (Object.prototype.hasOwnProperty.call(body, f)) { + setClauses.push(`${f} = ?`); + params.push(body[f]); + } + }); + + if (setClauses.length === 0) { + return res.status(400).json({ error: 'No fields to update' }); + } + + const sql = ` + UPDATE inventory_items + SET ${setClauses.join(', ')}, updated_at = datetime('now') + WHERE id = ? + `; + params.push(id); + + db.run(sql, params, function (err) { + if (err) return res.status(500).json({ error: err.message }); + if (this.changes === 0) return res.status(404).json({ error: 'Not found' }); + res.json({ success: true }); + }); +}); + +// Soft delete inventory item +app.delete('/api/inventory/:id', (req, res) => { + const { id } = req.params; + const sql = ` + UPDATE inventory_items + SET is_active = 0, updated_at = datetime('now') + WHERE id = ? + `; + db.run(sql, [id], function (err) { + if (err) return res.status(500).json({ error: err.message }); + if (this.changes === 0) return res.status(404).json({ error: 'Not found' }); + res.json({ success: true }); + }); +}); + +// Cocktail completamente fattibili con l'inventario attuale +app.get('/api/cocktails/available', (req, res) => { + const sql = ` + SELECT c.* + FROM cocktails c + JOIN cocktail_ingredients ci ON ci.cocktail_id = c.id + LEFT JOIN inventory_items ii + ON ii.ingredient_id = ci.ingredient_id + AND ii.status IN ('in_stock', 'low') + AND ii.is_active = 1 + GROUP BY c.id + HAVING COUNT(DISTINCT ci.ingredient_id) = COUNT(DISTINCT ii.ingredient_id) + `; + db.all(sql, [], (err, rows) => { + if (err) return res.status(500).json({ error: err.message }); + res.json(rows); + }); +}); + const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`Server su http://localhost:${PORT}`));