fix: use product_shopping_name in all Bring! add paths from low-stock flow
- inventory_use API now returns product_shopping_name in response - showLowStockBringPrompt: uses generic shopping name (e.g. Affettato) as Bring! item name, specific product name + brand as specification field - addLowStockToBring: reads from window._lowStockName instead of arg - Auto-add on depletion JS fallback: same generic-name pattern - Deduplication check now tries both shoppingName and raw name
This commit is contained in:
+4
-1
@@ -1120,7 +1120,7 @@ function useFromInventory(PDO $db): void {
|
|||||||
$totalRemaining = round((float)($stmt->fetchColumn() ?: 0), 6);
|
$totalRemaining = round((float)($stmt->fetchColumn() ?: 0), 6);
|
||||||
|
|
||||||
// Get product info for low-stock prompt
|
// Get product info for low-stock prompt
|
||||||
$stmt = $db->prepare("SELECT name, brand, unit, default_quantity, package_unit FROM products WHERE id = ?");
|
$stmt = $db->prepare("SELECT name, brand, unit, default_quantity, package_unit, shopping_name FROM products WHERE id = ?");
|
||||||
$stmt->execute([$productId]);
|
$stmt->execute([$productId]);
|
||||||
$prodInfo = $stmt->fetch();
|
$prodInfo = $stmt->fetch();
|
||||||
|
|
||||||
@@ -1132,6 +1132,9 @@ function useFromInventory(PDO $db): void {
|
|||||||
$response['product_unit'] = $prodInfo['unit'];
|
$response['product_unit'] = $prodInfo['unit'];
|
||||||
$response['product_default_qty'] = (float)($prodInfo['default_quantity'] ?: 0);
|
$response['product_default_qty'] = (float)($prodInfo['default_quantity'] ?: 0);
|
||||||
$response['product_package_unit'] = $prodInfo['package_unit'] ?: '';
|
$response['product_package_unit'] = $prodInfo['package_unit'] ?: '';
|
||||||
|
// Generic shopping name for Bring! (e.g. "Affettato" for "Mortadella IGP")
|
||||||
|
$shopping = $prodInfo['shopping_name'] ?: computeShoppingName($prodInfo['name'], '', $prodInfo['brand']);
|
||||||
|
$response['product_shopping_name'] = $shopping;
|
||||||
}
|
}
|
||||||
if ($openedId) $response['opened_id'] = $openedId;
|
if ($openedId) $response['opened_id'] = $openedId;
|
||||||
echo json_encode($response);
|
echo json_encode($response);
|
||||||
|
|||||||
+23
-10
@@ -5918,6 +5918,10 @@ function _matchBringToSmart(bringName, smartItems) {
|
|||||||
|
|
||||||
function showLowStockBringPrompt(result, afterCallback) {
|
function showLowStockBringPrompt(result, afterCallback) {
|
||||||
const name = result.product_name || currentProduct?.name || '';
|
const name = result.product_name || currentProduct?.name || '';
|
||||||
|
// Generic shopping name (e.g. "Affettato" for "Mortadella IGP"). Falls back to
|
||||||
|
// the specific name when shopping_name is not set (older API call), so behaviour
|
||||||
|
// is unchanged for legacy callers.
|
||||||
|
const shoppingName = result.product_shopping_name || name;
|
||||||
const unit = result.product_unit || currentProduct?.unit || 'pz';
|
const unit = result.product_unit || currentProduct?.unit || 'pz';
|
||||||
const defaultQty = result.product_default_qty || parseFloat(currentProduct?.default_quantity) || 0;
|
const defaultQty = result.product_default_qty || parseFloat(currentProduct?.default_quantity) || 0;
|
||||||
const totalRemaining = result.total_remaining;
|
const totalRemaining = result.total_remaining;
|
||||||
@@ -5927,12 +5931,13 @@ function showLowStockBringPrompt(result, afterCallback) {
|
|||||||
if (totalRemaining <= 0) {
|
if (totalRemaining <= 0) {
|
||||||
// Backend auto-adds to Bring! when fully depleted. If it failed (Bring not
|
// Backend auto-adds to Bring! when fully depleted. If it failed (Bring not
|
||||||
// configured, or product already on list), silently attempt it from JS.
|
// configured, or product already on list), silently attempt it from JS.
|
||||||
if (!result.added_to_bring && name) {
|
if (!result.added_to_bring && shoppingName) {
|
||||||
// Fire-and-forget — don't block the callback
|
// Fire-and-forget — don't block the callback
|
||||||
|
// Use generic shopping name; specific name goes into specification.
|
||||||
|
const spec = shoppingName !== name ? name + (result.product_brand ? ` · ${result.product_brand}` : '') : '';
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const spec = name;
|
const payload = { items: [{ name: shoppingName, specification: spec }] };
|
||||||
const payload = { items: [{ name, specification: spec }] };
|
|
||||||
if (shoppingListUUID) payload.listUUID = shoppingListUUID;
|
if (shoppingListUUID) payload.listUUID = shoppingListUUID;
|
||||||
const data = await api('bring_add', {}, 'POST', payload);
|
const data = await api('bring_add', {}, 'POST', payload);
|
||||||
if (data.success && data.added > 0) {
|
if (data.success && data.added > 0) {
|
||||||
@@ -5962,7 +5967,7 @@ function showLowStockBringPrompt(result, afterCallback) {
|
|||||||
|
|
||||||
// --- Deduplication check ---
|
// --- Deduplication check ---
|
||||||
// 1. Already on Bring! list (shoppingItems)?
|
// 1. Already on Bring! list (shoppingItems)?
|
||||||
const alreadyOnBring = _findSimilarItem(name, shoppingItems);
|
const alreadyOnBring = _findSimilarItem(shoppingName, shoppingItems) || _findSimilarItem(name, shoppingItems);
|
||||||
if (alreadyOnBring) {
|
if (alreadyOnBring) {
|
||||||
// Already present (same or similar item). Just inform and continue.
|
// Already present (same or similar item). Just inform and continue.
|
||||||
showToast(`🛒 "${escapeHtml(alreadyOnBring.name)}" già nella lista della spesa`, 'info');
|
showToast(`🛒 "${escapeHtml(alreadyOnBring.name)}" già nella lista della spesa`, 'info');
|
||||||
@@ -5971,7 +5976,7 @@ function showLowStockBringPrompt(result, afterCallback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. In smart shopping predictions?
|
// 2. In smart shopping predictions?
|
||||||
const smartMatch = _findSimilarItem(name, smartShoppingItems);
|
const smartMatch = _findSimilarItem(shoppingName, smartShoppingItems) || _findSimilarItem(name, smartShoppingItems);
|
||||||
const smartUrgencyLabel = {
|
const smartUrgencyLabel = {
|
||||||
critical: '🔴 Urgente', high: '🟠 Presto', medium: '🟡 Pianifica', low: '🟢 Previsione'
|
critical: '🔴 Urgente', high: '🟠 Presto', medium: '🟡 Pianifica', low: '🟢 Previsione'
|
||||||
};
|
};
|
||||||
@@ -5983,9 +5988,13 @@ function showLowStockBringPrompt(result, afterCallback) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build specification from product name for Bring
|
// _lowStockName = generic name that goes into Bring! (e.g. "Affettato")
|
||||||
|
// _lowStockSpec = specific product name used as specification (e.g. "Mortadella IGP")
|
||||||
window._lowStockAfterCallback = afterCallback;
|
window._lowStockAfterCallback = afterCallback;
|
||||||
window._lowStockSpec = name;
|
window._lowStockName = shoppingName;
|
||||||
|
window._lowStockSpec = shoppingName !== name
|
||||||
|
? name + (result.product_brand ? ` · ${result.product_brand}` : '')
|
||||||
|
: name;
|
||||||
|
|
||||||
document.getElementById('modal-content').innerHTML = `
|
document.getElementById('modal-content').innerHTML = `
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@@ -5996,7 +6005,7 @@ function showLowStockBringPrompt(result, afterCallback) {
|
|||||||
<p style="margin-bottom:12px"><strong>${escapeHtml(name)}</strong> sta per finire — rimangono solo <strong>${remainLabel}</strong>.</p>
|
<p style="margin-bottom:12px"><strong>${escapeHtml(name)}</strong> sta per finire — rimangono solo <strong>${remainLabel}</strong>.</p>
|
||||||
${smartNote}
|
${smartNote}
|
||||||
<p style="margin-bottom:16px">Vuoi aggiungerlo alla lista della spesa?</p>
|
<p style="margin-bottom:16px">Vuoi aggiungerlo alla lista della spesa?</p>
|
||||||
<button type="button" class="btn btn-large btn-success full-width" onclick="addLowStockToBring('${escapeHtml(name).replace(/'/g, "\\'")}')">
|
<button type="button" class="btn btn-large btn-success full-width" onclick="addLowStockToBring()">
|
||||||
🛒 Sì, aggiungi a Bring!
|
🛒 Sì, aggiungi a Bring!
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-secondary full-width" style="margin-top:8px" onclick="closeLowStockPrompt()">
|
<button type="button" class="btn btn-secondary full-width" style="margin-top:8px" onclick="closeLowStockPrompt()">
|
||||||
@@ -6007,12 +6016,16 @@ function showLowStockBringPrompt(result, afterCallback) {
|
|||||||
document.getElementById('modal-overlay').style.display = 'flex';
|
document.getElementById('modal-overlay').style.display = 'flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addLowStockToBring(productName) {
|
async function addLowStockToBring() {
|
||||||
closeModal();
|
closeModal();
|
||||||
try {
|
try {
|
||||||
|
// Use the generic shopping name (e.g. "Affettato") set by showLowStockBringPrompt.
|
||||||
|
// _lowStockSpec holds the specific product name (e.g. "Mortadella IGP · Marca").
|
||||||
|
const bringName = window._lowStockName || '';
|
||||||
const spec = window._lowStockSpec || '';
|
const spec = window._lowStockSpec || '';
|
||||||
|
window._lowStockName = null;
|
||||||
window._lowStockSpec = null;
|
window._lowStockSpec = null;
|
||||||
const payload = { items: [{ name: productName, specification: spec }] };
|
const payload = { items: [{ name: bringName, specification: spec }] };
|
||||||
if (shoppingListUUID) payload.listUUID = shoppingListUUID;
|
if (shoppingListUUID) payload.listUUID = shoppingListUUID;
|
||||||
const data = await api('bring_add', {}, 'POST', payload);
|
const data = await api('bring_add', {}, 'POST', payload);
|
||||||
if (data.success && data.added > 0) {
|
if (data.success && data.added > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user