changes with app products
This commit is contained in:
@@ -78,7 +78,37 @@ async function tenantsWithOrphans() {
|
|||||||
const groups = (await get('/products/getallproducts', { tenantid: tenant.tenantid })) ?? [];
|
const groups = (await get('/products/getallproducts', { tenantid: tenant.tenantid })) ?? [];
|
||||||
const products = (Array.isArray(groups) ? groups : []).flatMap((g) => g?.products ?? []);
|
const products = (Array.isArray(groups) ? groups : []).flatMap((g) => g?.products ?? []);
|
||||||
const orphans = products.filter((p) => !p.categoryid);
|
const orphans = products.filter((p) => !p.categoryid);
|
||||||
if (orphans.length > 0) out.push({ tenant, orphans });
|
if (orphans.length === 0) continue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where each orphan already sits.
|
||||||
|
*
|
||||||
|
* `getallproducts` is tenant-wide and carries NO locationid — the first
|
||||||
|
* version of this read `p.locationid` off it, got undefined, sent 0, and
|
||||||
|
* every repair came back "missing required field(s): locationid". Nothing
|
||||||
|
* was written, which is the one good thing about that failure.
|
||||||
|
*
|
||||||
|
* The outlet matters beyond passing validation. Import writes a
|
||||||
|
* productlocations row, so naming an outlet the product is NOT on would put
|
||||||
|
* it on that shelf — silently extending the product's reach as a side
|
||||||
|
* effect of a repair. Only an outlet where it is already stocked is safe:
|
||||||
|
* there the upsert lands on the existing row.
|
||||||
|
*/
|
||||||
|
const locations = (await get('/tenants/gettenantlocations', { tenantid: tenant.tenantid })) ?? [];
|
||||||
|
const placement = new Map();
|
||||||
|
for (const loc of Array.isArray(locations) ? locations : []) {
|
||||||
|
const rows = (await get('/products/getlocationproducts', {
|
||||||
|
tenantid: tenant.tenantid,
|
||||||
|
locationid: loc.locationid,
|
||||||
|
pageno: 1,
|
||||||
|
pagesize: 500,
|
||||||
|
})) ?? [];
|
||||||
|
for (const row of Array.isArray(rows) ? rows : []) {
|
||||||
|
if (!placement.has(row.productid)) placement.set(row.productid, { loc, row });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push({ tenant, orphans, placement });
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -114,7 +144,7 @@ let repaired = 0;
|
|||||||
let unchanged = 0;
|
let unchanged = 0;
|
||||||
let refused = 0;
|
let refused = 0;
|
||||||
|
|
||||||
for (const { tenant, orphans } of work) {
|
for (const { tenant, orphans, placement } of work) {
|
||||||
const categoryid = await categoryFor(tenant.tenantid);
|
const categoryid = await categoryFor(tenant.tenantid);
|
||||||
console.log(`${tenant.tenantid} ${tenant.tenantname} — ${orphans.length} to repair`);
|
console.log(`${tenant.tenantid} ${tenant.tenantname} — ${orphans.length} to repair`);
|
||||||
|
|
||||||
@@ -136,25 +166,60 @@ for (const { tenant, orphans } of work) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const at = placement.get(p.productid);
|
||||||
|
if (!at) {
|
||||||
|
console.log(` ${label} — SKIPPED: not stocked at any outlet, so there is no safe row to repair through`);
|
||||||
|
refused++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A product with no price must not be made visible.
|
||||||
|
*
|
||||||
|
* Repairing the category is what puts a product in front of shoppers, and
|
||||||
|
* the app has no price floor — `GetProducts` filters on category and outlet
|
||||||
|
* and nothing else. Fixing a product priced at 0 would not "restore" it; it
|
||||||
|
* would publish a free one. Idhayam Sesame Oil 500ml (7083) is in exactly
|
||||||
|
* this state, priced nowhere, and it wants a price before it wants a
|
||||||
|
* category.
|
||||||
|
*/
|
||||||
|
const price = Number(p.retailprice ?? 0);
|
||||||
|
if (!(price > 0)) {
|
||||||
|
console.log(` ${label} — SKIPPED: no price set. Repairing this would list it at ₹0. Price it first.`);
|
||||||
|
refused++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!apply) {
|
if (!apply) {
|
||||||
console.log(` ${label} → categoryid ${categoryid}`);
|
console.log(
|
||||||
|
` ${label} → categoryid ${categoryid} (via ${at.loc.locationname ?? at.loc.locationid}, price ₹${price} unchanged)`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await post('/products/importcatalogueproduct', [
|
const result = await post('/products/importcatalogueproduct', [
|
||||||
{
|
{
|
||||||
tenantid: tenant.tenantid,
|
tenantid: tenant.tenantid,
|
||||||
// Any outlet the product already sits at; the upsert leaves the row
|
// An outlet the product ALREADY sits at, so the upsert lands on the
|
||||||
// alone and the category is written on `products`, which is tenant-wide.
|
// existing row instead of putting it on a new shelf. The category
|
||||||
locationid: p.locationid ?? p.applocationid ?? 0,
|
// itself is written to `products`, which is tenant-wide, so one call
|
||||||
|
// fixes the product everywhere.
|
||||||
|
locationid: at.loc.locationid,
|
||||||
brand: p.productbrand,
|
brand: p.productbrand,
|
||||||
catalogueid: p.catalogueid,
|
catalogueid: p.catalogueid,
|
||||||
categoryid,
|
categoryid,
|
||||||
subcategoryid: p.subcategoryid ?? 0,
|
subcategoryid: p.subcategoryid ?? 0,
|
||||||
|
// Zero, so CreateProductLocation writes no stock ledger entry — it only
|
||||||
|
// records stock when quantity > 0. These products already have their
|
||||||
|
// stock and a repair must not add to it.
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
stocktype: 'in',
|
stocktype: 'in',
|
||||||
status: p.productstatus || 'Active',
|
status: at.row.productstatus || p.productstatus || 'Active',
|
||||||
retailprice: p.retailprice ?? 0,
|
// The product's OWN current values, re-sent unchanged. The same call
|
||||||
|
// updates pricing, and the location upsert sets productlocations.price
|
||||||
|
// from retailprice — verified equal for every product being repaired
|
||||||
|
// here, so this round-trips rather than overwriting an outlet price.
|
||||||
|
retailprice: price,
|
||||||
productcost: p.productcost ?? 0,
|
productcost: p.productcost ?? 0,
|
||||||
taxpercent: p.taxpercent ?? 0,
|
taxpercent: p.taxpercent ?? 0,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user