audit and sync
This commit is contained in:
@@ -276,3 +276,159 @@ export async function refreshProductByWooId({ tenantId, wooId, parentId = null }
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Sincronización completa con WooCommerce
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Obtiene todos los productos de WooCommerce usando paginación
|
||||
*/
|
||||
export async function fetchAllWooProducts({ tenantId }) {
|
||||
const client = await getWooClient({ tenantId });
|
||||
const allProducts = [];
|
||||
let page = 1;
|
||||
const perPage = 100;
|
||||
|
||||
console.log("[wooSnapshot] fetchAllWooProducts starting...");
|
||||
|
||||
while (true) {
|
||||
const url = `${client.base}/products?per_page=${perPage}&page=${page}&status=any`;
|
||||
const data = await fetchWoo({
|
||||
url,
|
||||
method: "GET",
|
||||
timeout: client.timeout * 2, // Más tiempo para listados grandes
|
||||
headers: client.authHeader
|
||||
});
|
||||
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
allProducts.push(...data);
|
||||
console.log(`[wooSnapshot] fetchAllWooProducts page ${page}: ${data.length} products (total: ${allProducts.length})`);
|
||||
|
||||
if (data.length < perPage) {
|
||||
break; // Última página
|
||||
}
|
||||
|
||||
page++;
|
||||
}
|
||||
|
||||
console.log(`[wooSnapshot] fetchAllWooProducts completed: ${allProducts.length} products`);
|
||||
return allProducts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sincroniza todos los productos desde WooCommerce (reemplaza snapshot local)
|
||||
*/
|
||||
export async function syncFromWoo({ tenantId }) {
|
||||
console.log("[wooSnapshot] syncFromWoo starting...");
|
||||
const t0 = Date.now();
|
||||
|
||||
// 1. Obtener todos los productos de Woo
|
||||
const wooProducts = await fetchAllWooProducts({ tenantId });
|
||||
|
||||
if (wooProducts.length === 0) {
|
||||
console.log("[wooSnapshot] syncFromWoo: no products found in Woo");
|
||||
return { ok: true, synced: 0, ms: Date.now() - t0 };
|
||||
}
|
||||
|
||||
// 2. Crear run para tracking
|
||||
const runId = await insertSnapshotRun({ tenantId, source: "sync_from_woo", total: wooProducts.length });
|
||||
|
||||
// 3. Normalizar e insertar productos
|
||||
const normalized = wooProducts.map(normalizeWooProduct);
|
||||
await upsertSnapshotItems({ tenantId, items: normalized, runId });
|
||||
|
||||
// 4. Eliminar productos que ya no existen en Woo
|
||||
await deleteMissingItems({ tenantId, runId });
|
||||
|
||||
const ms = Date.now() - t0;
|
||||
console.log(`[wooSnapshot] syncFromWoo completed: ${wooProducts.length} products in ${ms}ms`);
|
||||
|
||||
return { ok: true, synced: wooProducts.length, ms };
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushea cambios de un producto a WooCommerce
|
||||
*/
|
||||
export async function pushProductToWoo({ tenantId, wooProductId, categories, sellUnit }) {
|
||||
const client = await getWooClient({ tenantId });
|
||||
const url = `${client.base}/products/${encodeURIComponent(wooProductId)}`;
|
||||
|
||||
// Construir payload de actualización
|
||||
const updatePayload = {};
|
||||
|
||||
// Actualizar categorías si se proporcionaron
|
||||
if (categories && Array.isArray(categories) && categories.length > 0) {
|
||||
// Primero obtener las categorías existentes en Woo para mapear nombres a IDs
|
||||
const existingCats = await fetchWooCategoriesByNames({ tenantId, names: categories });
|
||||
if (existingCats.length > 0) {
|
||||
updatePayload.categories = existingCats.map(c => ({ id: c.id }));
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar meta de unidad de venta
|
||||
if (sellUnit) {
|
||||
updatePayload.meta_data = [
|
||||
{ key: "_sell_unit_override", value: sellUnit }
|
||||
];
|
||||
}
|
||||
|
||||
// Solo hacer request si hay algo que actualizar
|
||||
if (Object.keys(updatePayload).length === 0) {
|
||||
return { ok: true, woo_product_id: wooProductId, updated: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await fetchWoo({
|
||||
url,
|
||||
method: "PUT",
|
||||
body: updatePayload,
|
||||
timeout: client.timeout,
|
||||
headers: client.authHeader,
|
||||
});
|
||||
|
||||
if (dbg.wooHttp) console.log("[wooSnapshot] pushProductToWoo", { wooProductId, updated: true });
|
||||
|
||||
return { ok: true, woo_product_id: wooProductId, updated: true, data };
|
||||
} catch (err) {
|
||||
console.error("[wooSnapshot] pushProductToWoo error:", err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene categorías de WooCommerce por nombres
|
||||
*/
|
||||
async function fetchWooCategoriesByNames({ tenantId, names }) {
|
||||
if (!names || names.length === 0) return [];
|
||||
|
||||
const client = await getWooClient({ tenantId });
|
||||
const allCategories = [];
|
||||
let page = 1;
|
||||
|
||||
// Obtener todas las categorías de Woo (usualmente son pocas)
|
||||
while (true) {
|
||||
const url = `${client.base}/products/categories?per_page=100&page=${page}`;
|
||||
const data = await fetchWoo({
|
||||
url,
|
||||
method: "GET",
|
||||
timeout: client.timeout,
|
||||
headers: client.authHeader,
|
||||
});
|
||||
|
||||
if (!Array.isArray(data) || data.length === 0) break;
|
||||
allCategories.push(...data);
|
||||
if (data.length < 100) break;
|
||||
page++;
|
||||
}
|
||||
|
||||
// Filtrar las que coinciden con los nombres buscados
|
||||
const normalizedNames = names.map(n => String(n).toLowerCase().trim());
|
||||
return allCategories.filter(c =>
|
||||
normalizedNames.includes(String(c.name).toLowerCase().trim()) ||
|
||||
normalizedNames.includes(String(c.slug).toLowerCase().trim())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user