Productos: allowed_order_units + fix sync que pisaba overrides
Introduce un campo nuevo allowed_order_units por producto (array kg/unit) que separa "cómo se vende" de "cómo se puede pedir". Productos sell_unit=unit solo aceptan unit (regla dura aplicada por código y handler). Productos sell_unit=kg defaultean a ambos, editable desde la UI con checkboxes. Fixea bug crítico de wooSnapshot.upsertSnapshotItems que pisaba raw entero en cada sync, perdiendo overrides locales (_sell_unit_override, _categories_override y el nuevo _allowed_order_units). El UPDATE ahora mergea preservando los overrides. Las tools del agente (addToCart, setQuantity, selectCandidate) validan unit contra allowed_order_units y devuelven unit_not_allowed para que el LLM re-pregunte. workingMemory expone allowed_order_units y needs_unit_choice. buildLineItems agrega meta_data unit/sell_unit_product/unit_mode_mismatch y omite subtotal/total cuando hay mismatch para que Woo recalcule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { getDecryptedTenantEcommerceConfig } from "../2-identity/db/repo.js";
|
||||
import { debug as dbg } from "../shared/debug.js";
|
||||
import { getSnapshotPriceByWooId } from "../shared/wooSnapshot.js";
|
||||
import { getSnapshotPriceByWooId, getSnapshotItemsByIds } from "../shared/wooSnapshot.js";
|
||||
import { normalizeUnitToken } from "../shared/sellUnitPolicy.js";
|
||||
import * as ordersRepo from "./ordersRepo.js";
|
||||
|
||||
// --- Simple in-memory lock to serialize work per key ---
|
||||
@@ -129,15 +130,33 @@ function toMoney(value) {
|
||||
async function buildLineItems({ tenantId, basket }) {
|
||||
const items = normalizeBasketItems(basket);
|
||||
const lineItems = [];
|
||||
|
||||
// Traer en bulk el sell_unit del snapshot para cada producto del basket.
|
||||
const productIds = [...new Set(items.map((it) => Number(it.product_id)).filter(Boolean))];
|
||||
const sellUnitByWoo = new Map();
|
||||
if (productIds.length) {
|
||||
const lookup = await getSnapshotItemsByIds({ tenantId, wooProductIds: productIds });
|
||||
for (const p of lookup?.items || []) {
|
||||
sellUnitByWoo.set(Number(p.woo_product_id), p.sell_unit || null);
|
||||
}
|
||||
}
|
||||
|
||||
for (const it of items) {
|
||||
const productId = Number(it.product_id);
|
||||
const unit = String(it.unit).toLowerCase();
|
||||
const qty = Number(it.quantity);
|
||||
if (!productId || !Number.isFinite(qty) || qty <= 0) continue;
|
||||
const pricePerKg = await getWooProductPrice({ tenantId, productId });
|
||||
const productSellUnit = sellUnitByWoo.get(productId);
|
||||
const productSellUnitNorm = normalizeUnitToken(productSellUnit);
|
||||
const requestedUnitNorm = normalizeUnitToken(unit);
|
||||
// Mismatch: cliente pidió "unit" pero el producto se vende por kg (u otro)
|
||||
const mismatch = requestedUnitNorm === "unit" && productSellUnitNorm !== "unit";
|
||||
|
||||
if (unit === "unit") {
|
||||
const total = pricePerKg != null ? toMoney(pricePerKg * qty) : null;
|
||||
// Si hay mismatch (producto kg pedido como unidad) → no enviar subtotal/total,
|
||||
// dejar que Woo calcule con el precio del producto. El comercio resuelve al armar.
|
||||
const total = !mismatch && pricePerKg != null ? toMoney(pricePerKg * qty) : null;
|
||||
lineItems.push({
|
||||
product_id: productId,
|
||||
...(it.variation_id ? { variation_id: it.variation_id } : {}),
|
||||
@@ -145,6 +164,8 @@ async function buildLineItems({ tenantId, basket }) {
|
||||
...(total ? { subtotal: total, total } : {}),
|
||||
meta_data: [
|
||||
{ key: "unit", value: "unit" },
|
||||
{ key: "sell_unit_product", value: productSellUnit || "" },
|
||||
{ key: "unit_mode_mismatch", value: mismatch ? "true" : "false" },
|
||||
],
|
||||
});
|
||||
continue;
|
||||
@@ -165,6 +186,7 @@ async function buildLineItems({ tenantId, basket }) {
|
||||
{ key: "unit", value: "g" },
|
||||
{ key: "weight_g", value: grams },
|
||||
{ key: "unit_price_per_kg", value: pricePerKg },
|
||||
{ key: "sell_unit_product", value: productSellUnit || "" },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user