productos, equivalencias, cross-sell y cantidades

This commit is contained in:
Lucas Tettamanti
2026-01-18 18:28:28 -03:00
parent 8cc4744c49
commit c7c56ddbfc
32 changed files with 4083 additions and 2073 deletions

View File

@@ -17,13 +17,13 @@ export const makeListAliases = (tenantIdOrFn) => async (req, res) => {
export const makeCreateAlias = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const { alias, woo_product_id, boost, category_hint, metadata } = req.body || {};
const { alias, woo_product_id, boost, category_hint, metadata, product_mappings } = req.body || {};
if (!alias || !woo_product_id) {
return res.status(400).json({ ok: false, error: "alias_and_woo_product_id_required" });
}
const result = await handleCreateAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata });
const result = await handleCreateAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata, product_mappings });
res.json({ ok: true, item: result });
} catch (err) {
console.error(err);
@@ -38,13 +38,13 @@ export const makeUpdateAlias = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const alias = req.params.alias;
const { woo_product_id, boost, category_hint, metadata } = req.body || {};
const { woo_product_id, boost, category_hint, metadata, product_mappings } = req.body || {};
if (!woo_product_id) {
return res.status(400).json({ ok: false, error: "woo_product_id_required" });
}
const result = await handleUpdateAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata });
const result = await handleUpdateAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata, product_mappings });
if (!result) {
return res.status(404).json({ ok: false, error: "alias_not_found" });
}

View File

@@ -0,0 +1,51 @@
import {
handleListProductQtyRules,
handleGetProductQtyRules,
handleSaveProductQtyRules
} from "../handlers/quantities.js";
export const makeListProductQtyRules = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const result = await handleListProductQtyRules({ tenantId });
res.json(result);
} catch (err) {
console.error(err);
res.status(500).json({ ok: false, error: "internal_error" });
}
};
export const makeGetProductQtyRules = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const wooProductId = parseInt(req.params.wooProductId, 10);
if (!wooProductId) {
return res.status(400).json({ ok: false, error: "woo_product_id_required" });
}
const result = await handleGetProductQtyRules({ tenantId, wooProductId });
res.json(result);
} catch (err) {
console.error(err);
res.status(500).json({ ok: false, error: "internal_error" });
}
};
export const makeSaveProductQtyRules = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const wooProductId = parseInt(req.params.wooProductId, 10);
const { rules } = req.body || {};
if (!wooProductId) {
return res.status(400).json({ ok: false, error: "woo_product_id_required" });
}
const result = await handleSaveProductQtyRules({ tenantId, wooProductId, rules: rules || [] });
res.json({ ok: true, ...result });
} catch (err) {
console.error(err);
res.status(500).json({ ok: false, error: "internal_error" });
}
};

View File

@@ -37,14 +37,18 @@ export const makeGetRecommendation = (tenantIdOrFn) => async (req, res) => {
export const makeCreateRecommendation = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const { rule_key, trigger, queries, boosts, ask_slots, active, priority } = req.body || {};
const {
rule_key, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, items
} = req.body || {};
if (!rule_key) {
return res.status(400).json({ ok: false, error: "rule_key_required" });
}
const result = await handleCreateRecommendation({
tenantId, rule_key, trigger, queries, boosts, ask_slots, active, priority
tenantId, rule_key, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, items
});
res.json({ ok: true, item: result });
} catch (err) {
@@ -60,10 +64,14 @@ export const makeUpdateRecommendation = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const id = req.params.id;
const { trigger, queries, boosts, ask_slots, active, priority } = req.body || {};
const {
trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, items
} = req.body || {};
const result = await handleUpdateRecommendation({
tenantId, id, trigger, queries, boosts, ask_slots, active, priority
tenantId, id, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, items
});
if (!result) {
return res.status(404).json({ ok: false, error: "recommendation_not_found" });

View File

@@ -16,17 +16,19 @@ export async function listProducts({ tenantId, q = "", limit = 2000, offset = 0
select
woo_id as woo_product_id,
name,
slug as sku,
coalesce(raw->>'SKU', raw->>'sku', slug) as sku,
slug,
price_current as price,
stock_status,
stock_qty,
categories,
attributes_normalized,
updated_at as refreshed_at,
raw as payload,
raw->>'_sell_unit_override' as sell_unit
coalesce(raw->>'_sell_unit_override', 'kg') as sell_unit
from woo_products_snapshot
where tenant_id = $1
and (name ilike $2 or coalesce(slug,'') ilike $2)
and (name ilike $2 or coalesce(slug,'') ilike $2 or coalesce(raw->>'SKU', raw->>'sku', '') ilike $2)
order by name asc
limit $3 offset $4
`;
@@ -36,14 +38,16 @@ export async function listProducts({ tenantId, q = "", limit = 2000, offset = 0
select
woo_id as woo_product_id,
name,
slug as sku,
coalesce(raw->>'SKU', raw->>'sku', slug) as sku,
slug,
price_current as price,
stock_status,
stock_qty,
categories,
attributes_normalized,
updated_at as refreshed_at,
raw as payload,
raw->>'_sell_unit_override' as sell_unit
coalesce(raw->>'_sell_unit_override', 'kg') as sell_unit
from woo_products_snapshot
where tenant_id = $1
order by name asc
@@ -61,14 +65,16 @@ export async function getProductByWooId({ tenantId, wooProductId }) {
select
woo_id as woo_product_id,
name,
slug as sku,
coalesce(raw->>'sku', slug) as sku,
slug,
price_current as price,
stock_status,
stock_qty,
categories,
attributes_normalized,
updated_at as refreshed_at,
raw as payload,
raw->>'_sell_unit_override' as sell_unit
coalesce(raw->>'_sell_unit_override', 'kg') as sell_unit
from woo_products_snapshot
where tenant_id = $1 and woo_id = $2
limit 1
@@ -101,29 +107,37 @@ export async function bulkUpdateProductSellUnit({ tenantId, wooProductIds, sell_
}
export async function updateProduct({ tenantId, wooProductId, sell_unit, categories }) {
// Build the JSONB update dynamically
// Build the update - combine all raw updates into one
let updates = [];
let params = [tenantId, wooProductId];
let paramIdx = 3;
// Build the raw column update by chaining jsonb_set calls
let rawExpr = "coalesce(raw, '{}'::jsonb)";
if (sell_unit) {
updates.push(`raw = jsonb_set(coalesce(raw, '{}'::jsonb), '{_sell_unit_override}', $${paramIdx}::jsonb)`);
rawExpr = `jsonb_set(${rawExpr}, '{_sell_unit_override}', $${paramIdx}::jsonb)`;
params.push(JSON.stringify(sell_unit));
paramIdx++;
}
if (categories) {
// Also update the categories column if it exists
// Update categories column
updates.push(`categories = $${paramIdx}::jsonb`);
params.push(JSON.stringify(categories.map(name => ({ name }))));
paramIdx++;
// Also store in raw for persistence
updates.push(`raw = jsonb_set(coalesce(raw, '{}'::jsonb), '{_categories_override}', $${paramIdx}::jsonb)`);
// Chain the categories override into raw
rawExpr = `jsonb_set(${rawExpr}, '{_categories_override}', $${paramIdx}::jsonb)`;
params.push(JSON.stringify(categories));
paramIdx++;
}
// Only add raw update if we modified it
if (sell_unit || categories) {
updates.push(`raw = ${rawExpr}`);
}
if (!updates.length) return null;
const sql = `
@@ -185,6 +199,31 @@ export async function listAliases({ tenantId, q = "", woo_product_id = null, lim
}
const { rows } = await pool.query(sql, params);
// Cargar mappings para cada alias
if (rows.length > 0) {
const mappingsSql = `
select alias, woo_product_id, score
from alias_product_mappings
where tenant_id = $1 and alias = any($2::text[])
order by alias, score desc
`;
const aliases = rows.map(r => r.alias);
const { rows: mappings } = await pool.query(mappingsSql, [tenantId, aliases]);
// Agrupar mappings por alias
const mappingsByAlias = {};
for (const m of mappings) {
if (!mappingsByAlias[m.alias]) mappingsByAlias[m.alias] = [];
mappingsByAlias[m.alias].push({ woo_product_id: m.woo_product_id, score: m.score });
}
// Agregar mappings a cada alias
for (const row of rows) {
row.product_mappings = mappingsByAlias[row.alias] || [];
}
}
return rows;
}
@@ -236,9 +275,62 @@ export async function updateAlias({ tenantId, alias, woo_product_id, boost = 0,
export async function deleteAlias({ tenantId, alias }) {
const sql = `delete from product_aliases where tenant_id = $1 and alias = $2 returning alias`;
const { rows } = await pool.query(sql, [tenantId, alias.toLowerCase().trim()]);
// También eliminar mappings asociados
await pool.query(`delete from alias_product_mappings where tenant_id = $1 and alias = $2`, [tenantId, alias.toLowerCase().trim()]);
return rows.length > 0;
}
// ─────────────────────────────────────────────────────────────
// Alias Product Mappings (multi-producto)
// ─────────────────────────────────────────────────────────────
export async function listAliasMappings({ tenantId, alias }) {
const sql = `
select alias, woo_product_id, score, created_at
from alias_product_mappings
where tenant_id = $1 and alias = $2
order by score desc
`;
const { rows } = await pool.query(sql, [tenantId, alias.toLowerCase().trim()]);
return rows;
}
export async function upsertAliasMapping({ tenantId, alias, woo_product_id, score = 1.0 }) {
const sql = `
insert into alias_product_mappings (tenant_id, alias, woo_product_id, score)
values ($1, $2, $3, $4)
on conflict (tenant_id, alias, woo_product_id)
do update set score = $4
returning alias, woo_product_id, score, created_at
`;
const { rows } = await pool.query(sql, [tenantId, alias.toLowerCase().trim(), woo_product_id, score]);
return rows[0];
}
export async function deleteAliasMapping({ tenantId, alias, woo_product_id }) {
const sql = `delete from alias_product_mappings where tenant_id = $1 and alias = $2 and woo_product_id = $3 returning alias`;
const { rows } = await pool.query(sql, [tenantId, alias.toLowerCase().trim(), woo_product_id]);
return rows.length > 0;
}
export async function setAliasMappings({ tenantId, alias, mappings }) {
const normalizedAlias = alias.toLowerCase().trim();
// Eliminar mappings existentes
await pool.query(`delete from alias_product_mappings where tenant_id = $1 and alias = $2`, [tenantId, normalizedAlias]);
// Insertar nuevos mappings
if (mappings && mappings.length > 0) {
const insertSql = `
insert into alias_product_mappings (tenant_id, alias, woo_product_id, score)
values ($1, $2, $3, $4)
`;
for (const mapping of mappings) {
await pool.query(insertSql, [tenantId, normalizedAlias, mapping.woo_product_id, mapping.score ?? 1.0]);
}
}
}
// ─────────────────────────────────────────────────────────────
// Recommendations
// ─────────────────────────────────────────────────────────────
@@ -252,7 +344,7 @@ export async function listRecommendations({ tenantId, q = "", limit = 200 }) {
const like = `%${query}%`;
sql = `
select id, rule_key, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, created_at, updated_at
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, created_at, updated_at
from product_reco_rules
where tenant_id = $1 and rule_key ilike $2
order by priority desc, rule_key asc
@@ -262,7 +354,7 @@ export async function listRecommendations({ tenantId, q = "", limit = 200 }) {
} else {
sql = `
select id, rule_key, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, created_at, updated_at
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, created_at, updated_at
from product_reco_rules
where tenant_id = $1
order by priority desc, rule_key asc
@@ -278,13 +370,24 @@ export async function listRecommendations({ tenantId, q = "", limit = 200 }) {
export async function getRecommendationById({ tenantId, id }) {
const sql = `
select id, rule_key, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, created_at, updated_at
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, created_at, updated_at
from product_reco_rules
where tenant_id = $1 and id = $2
limit 1
`;
const { rows } = await pool.query(sql, [tenantId, id]);
return rows[0] || null;
if (!rows[0]) return null;
// Cargar items asociados
const itemsSql = `
select id, woo_product_id, audience_type, qty_per_person, unit, reason, display_order
from reco_rule_items
where rule_id = $1
order by display_order asc
`;
const { rows: items } = await pool.query(itemsSql, [id]);
return { ...rows[0], items };
}
export async function insertRecommendation({
@@ -298,11 +401,14 @@ export async function insertRecommendation({
priority = 100,
trigger_product_ids = [],
recommended_product_ids = [],
rule_type = "crosssell",
trigger_event = null,
items = [],
}) {
const sql = `
insert into product_reco_rules (tenant_id, rule_key, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids)
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
returning id, rule_key, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids, created_at, updated_at
insert into product_reco_rules (tenant_id, rule_key, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids, rule_type, trigger_event)
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
returning id, rule_key, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids, rule_type, trigger_event, created_at, updated_at
`;
const { rows } = await pool.query(sql, [
@@ -316,9 +422,18 @@ export async function insertRecommendation({
priority || 100,
trigger_product_ids || [],
recommended_product_ids || [],
rule_type || "crosssell",
trigger_event || null,
]);
return rows[0];
const rule = rows[0];
// Insertar items si hay
if (items && items.length > 0) {
await upsertRecoRuleItems({ ruleId: rule.id, items });
}
return rule;
}
export async function updateRecommendation({
@@ -332,6 +447,9 @@ export async function updateRecommendation({
priority,
trigger_product_ids,
recommended_product_ids,
rule_type,
trigger_event,
items,
}) {
const sql = `
update product_reco_rules
@@ -344,9 +462,11 @@ export async function updateRecommendation({
priority = $8,
trigger_product_ids = $9,
recommended_product_ids = $10,
rule_type = $11,
trigger_event = $12,
updated_at = now()
where tenant_id = $1 and id = $2
returning id, rule_key, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids, created_at, updated_at
returning id, rule_key, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids, rule_type, trigger_event, created_at, updated_at
`;
const { rows } = await pool.query(sql, [
@@ -360,13 +480,147 @@ export async function updateRecommendation({
priority || 100,
trigger_product_ids || [],
recommended_product_ids || [],
rule_type || "crosssell",
trigger_event || null,
]);
// Actualizar items si se proporcionan
if (items !== undefined) {
await upsertRecoRuleItems({ ruleId: id, items: items || [] });
}
return rows[0] || null;
}
export async function upsertRecoRuleItems({ ruleId, items }) {
// Eliminar items existentes
await pool.query(`delete from reco_rule_items where rule_id = $1`, [ruleId]);
// Insertar nuevos items
if (items && items.length > 0) {
const insertSql = `
insert into reco_rule_items (rule_id, woo_product_id, audience_type, qty_per_person, unit, reason, display_order)
values ($1, $2, $3, $4, $5, $6, $7)
`;
for (let i = 0; i < items.length; i++) {
const item = items[i];
await pool.query(insertSql, [
ruleId,
item.woo_product_id,
item.audience_type ?? "adult",
item.qty_per_person ?? null,
item.unit ?? null,
item.reason ?? null,
item.display_order ?? i,
]);
}
}
}
export async function deleteRecommendation({ tenantId, id }) {
// Los items se eliminan en cascada
const sql = `delete from product_reco_rules where tenant_id = $1 and id = $2 returning id`;
const { rows } = await pool.query(sql, [tenantId, id]);
return rows.length > 0;
}
// ─────────────────────────────────────────────────────────────
// Product Quantity Rules (cantidades por producto/evento/persona)
// ─────────────────────────────────────────────────────────────
/**
* Obtener todas las reglas de cantidad agrupadas por producto
*/
export async function listProductQtyRules({ tenantId }) {
const sql = `
select id, woo_product_id, event_type, person_type, qty_per_person, unit, updated_at
from product_qty_rules
where tenant_id = $1
order by woo_product_id, event_type, person_type
`;
const { rows } = await pool.query(sql, [tenantId]);
return rows;
}
/**
* Obtener reglas de un producto específico
*/
export async function getProductQtyRules({ tenantId, wooProductId }) {
const sql = `
select id, woo_product_id, event_type, person_type, qty_per_person, unit, updated_at
from product_qty_rules
where tenant_id = $1 and woo_product_id = $2
order by event_type, person_type
`;
const { rows } = await pool.query(sql, [tenantId, wooProductId]);
return rows;
}
/**
* Upsert una regla de cantidad (crear o actualizar)
*/
export async function upsertProductQtyRule({ tenantId, wooProductId, eventType, personType, qtyPerPerson, unit }) {
const sql = `
insert into product_qty_rules (tenant_id, woo_product_id, event_type, person_type, qty_per_person, unit)
values ($1, $2, $3, $4, $5, $6)
on conflict (tenant_id, woo_product_id, event_type, person_type)
do update set
qty_per_person = $5,
unit = $6,
updated_at = now()
returning id, woo_product_id, event_type, person_type, qty_per_person, unit, updated_at
`;
const { rows } = await pool.query(sql, [tenantId, wooProductId, eventType, personType, qtyPerPerson, unit || 'kg']);
return rows[0];
}
/**
* Eliminar una regla de cantidad específica
*/
export async function deleteProductQtyRule({ tenantId, id }) {
const sql = `delete from product_qty_rules where tenant_id = $1 and id = $2 returning id`;
const { rows } = await pool.query(sql, [tenantId, id]);
return rows.length > 0;
}
/**
* Guardar todas las reglas de un producto (reemplaza las existentes)
*/
export async function saveProductQtyRules({ tenantId, wooProductId, rules }) {
// Eliminar reglas existentes del producto
await pool.query(`delete from product_qty_rules where tenant_id = $1 and woo_product_id = $2`, [tenantId, wooProductId]);
// Insertar nuevas reglas
if (rules && rules.length > 0) {
const insertSql = `
insert into product_qty_rules (tenant_id, woo_product_id, event_type, person_type, qty_per_person, unit)
values ($1, $2, $3, $4, $5, $6)
`;
for (const rule of rules) {
if (rule.qty_per_person != null && rule.qty_per_person > 0) {
await pool.query(insertSql, [
tenantId,
wooProductId,
rule.event_type,
rule.person_type,
rule.qty_per_person,
rule.unit || 'kg'
]);
}
}
}
}
/**
* Contar reglas por producto (para mostrar badges)
*/
export async function countQtyRulesByProduct({ tenantId }) {
const sql = `
select woo_product_id, count(*) as rule_count
from product_qty_rules
where tenant_id = $1
group by woo_product_id
`;
const { rows } = await pool.query(sql, [tenantId]);
return rows;
}

View File

@@ -1,19 +1,64 @@
import { listAliases, insertAlias, updateAlias, deleteAlias } from "../db/repo.js";
import {
listAliases,
insertAlias,
updateAlias,
deleteAlias,
listAliasMappings,
setAliasMappings,
} from "../db/repo.js";
export async function handleListAliases({ tenantId, q = "", woo_product_id = null, limit = 200 }) {
const items = await listAliases({ tenantId, q, woo_product_id, limit });
return { items };
}
export async function handleCreateAlias({ tenantId, alias, woo_product_id, boost = 0, category_hint = null, metadata = {} }) {
return insertAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata });
export async function handleCreateAlias({
tenantId,
alias,
woo_product_id,
boost = 0,
category_hint = null,
metadata = {},
product_mappings = [],
}) {
const result = await insertAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata });
// Si hay mappings, guardarlos
if (product_mappings && product_mappings.length > 0) {
await setAliasMappings({ tenantId, alias, mappings: product_mappings });
} else if (woo_product_id) {
// Si solo hay un producto, crear mapping por defecto
await setAliasMappings({ tenantId, alias, mappings: [{ woo_product_id, score: boost || 1.0 }] });
}
return result;
}
export async function handleUpdateAlias({ tenantId, alias, woo_product_id, boost = 0, category_hint = null, metadata = {} }) {
return updateAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata });
export async function handleUpdateAlias({
tenantId,
alias,
woo_product_id,
boost = 0,
category_hint = null,
metadata = {},
product_mappings,
}) {
const result = await updateAlias({ tenantId, alias, woo_product_id, boost, category_hint, metadata });
// Si hay mappings, actualizarlos
if (product_mappings !== undefined) {
await setAliasMappings({ tenantId, alias, mappings: product_mappings || [] });
}
return result;
}
export async function handleDeleteAlias({ tenantId, alias }) {
const deleted = await deleteAlias({ tenantId, alias });
return { deleted };
}
export async function handleGetAliasMappings({ tenantId, alias }) {
const mappings = await listAliasMappings({ tenantId, alias });
return { mappings };
}

View File

@@ -0,0 +1,23 @@
import {
listProductQtyRules,
getProductQtyRules,
saveProductQtyRules,
countQtyRulesByProduct,
} from "../db/repo.js";
export async function handleListProductQtyRules({ tenantId }) {
const rules = await listProductQtyRules({ tenantId });
const counts = await countQtyRulesByProduct({ tenantId });
return { rules, counts };
}
export async function handleGetProductQtyRules({ tenantId, wooProductId }) {
const rules = await getProductQtyRules({ tenantId, wooProductId });
return { rules };
}
export async function handleSaveProductQtyRules({ tenantId, wooProductId, rules }) {
await saveProductQtyRules({ tenantId, wooProductId, rules });
const updated = await getProductQtyRules({ tenantId, wooProductId });
return { rules: updated };
}

View File

@@ -26,8 +26,14 @@ export async function handleCreateRecommendation({
priority = 100,
trigger_product_ids = [],
recommended_product_ids = [],
rule_type = "crosssell",
trigger_event = null,
items = [],
}) {
return insertRecommendation({ tenantId, rule_key, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids });
return insertRecommendation({
tenantId, rule_key, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, items
});
}
export async function handleUpdateRecommendation({
@@ -41,8 +47,14 @@ export async function handleUpdateRecommendation({
priority,
trigger_product_ids,
recommended_product_ids,
rule_type,
trigger_event,
items,
}) {
return updateRecommendation({ tenantId, id, trigger, queries, boosts, ask_slots, active, priority, trigger_product_ids, recommended_product_ids });
return updateRecommendation({
tenantId, id, trigger, queries, boosts, ask_slots, active, priority,
trigger_product_ids, recommended_product_ids, rule_type, trigger_event, items
});
}
export async function handleDeleteRecommendation({ tenantId, id }) {