Refactor supplier analysis and product handling
- Updated `SupplierAnalysisResult` to include a `product` field and modified related tests. - Refactored `addRowsSheet` to accommodate changes in the product structure. - Enhanced UPC file analysis to utilize a new `toSupplierInputRecord` function for cleaner record creation. - Introduced new types for supplier input records and product observations. - Updated frontend components to handle new product details and analysis history. - Improved database writing functions to streamline run completion and error handling. - Added new API endpoints for product details and adjusted routing in the frontend.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import path from "node:path";
|
||||
import { requireAsin } from "../asin.ts";
|
||||
import { fetchKeepaDataBatch, lookupKeepaUpcs } from "../integrations/keepa.ts";
|
||||
import {
|
||||
fetchSellabilityBatch,
|
||||
@@ -11,6 +12,8 @@ import {
|
||||
} from "./upc-file-reader.ts";
|
||||
import {
|
||||
appendSupplierResultsToRun,
|
||||
completeRunInDb,
|
||||
failRunInDb,
|
||||
refreshRunCountsInDb,
|
||||
startRunInDb,
|
||||
type RunCounts,
|
||||
@@ -239,8 +242,8 @@ async function lookupUpcsWithChunking(
|
||||
chunkDetails.set(
|
||||
upc,
|
||||
fallbackDetail && fallbackDetail.status !== "request_failed"
|
||||
? fallbackDetail
|
||||
: spDetail!,
|
||||
? { ...fallbackDetail, provider: "keepa" }
|
||||
: { ...spDetail!, provider: "sp_api" },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -266,7 +269,7 @@ function toProductRecord(
|
||||
const keepaCategory = detail.keepaData?.categoryTree?.[0];
|
||||
|
||||
return {
|
||||
asin: detail.asin ?? row.upc,
|
||||
asin: requireAsin(detail.asin),
|
||||
name: row.name ?? detail.asin ?? row.upc,
|
||||
unitCost: row.unitCost ?? 0,
|
||||
brand: row.brand,
|
||||
@@ -274,6 +277,15 @@ function toProductRecord(
|
||||
};
|
||||
}
|
||||
|
||||
function toSupplierInputRecord(row: UpcInputRow) {
|
||||
return {
|
||||
name: row.name ?? row.upc,
|
||||
unitCost: row.unitCost ?? 0,
|
||||
brand: row.brand,
|
||||
category: row.category,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchFeesForProducts(
|
||||
products: ProductRecord[],
|
||||
keepaResults: Map<string, NonNullable<SupplierAnalysisResult["keepa"]>>,
|
||||
@@ -359,7 +371,7 @@ export async function runUpcFileAnalysis(
|
||||
let processedRows = 0;
|
||||
let matchedRows = 0;
|
||||
|
||||
const runId = await startRunInDb(options.inputFile, outputFile);
|
||||
const runId = await startRunInDb(options.inputFile, outputFile, undefined, "supplier_upc");
|
||||
|
||||
try {
|
||||
const readerSummary = await processUpcFileInBatches(
|
||||
@@ -382,12 +394,19 @@ export async function runUpcFileAnalysis(
|
||||
product: ProductRecord;
|
||||
}> = [];
|
||||
for (const row of rows) {
|
||||
const detail = detailMap.get(row.upc);
|
||||
if (!detail) {
|
||||
unresolvedByStatus.request_failed += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const detail =
|
||||
detailMap.get(row.upc) ??
|
||||
({
|
||||
requestedUpc: row.upc,
|
||||
normalizedUpc: row.upc,
|
||||
status: "request_failed",
|
||||
asin: null,
|
||||
candidateAsins: [],
|
||||
keepaData: null,
|
||||
provider: "sp_api",
|
||||
reason: "UPC lookup returned no result",
|
||||
} satisfies UpcLookupDetail);
|
||||
if (!detailMap.has(row.upc)) detailMap.set(row.upc, detail);
|
||||
unresolvedByStatus[detail.status] += 1;
|
||||
|
||||
if (detail.status === "found" && detail.asin) {
|
||||
@@ -407,30 +426,15 @@ export async function runUpcFileAnalysis(
|
||||
|
||||
const batchResults: SupplierAnalysisResult[] = [];
|
||||
for (const row of rows) {
|
||||
const detail = detailMap.get(row.upc);
|
||||
if (!detail || detail.status === "found") continue;
|
||||
const detail = detailMap.get(row.upc)!;
|
||||
if (detail.status === "found") continue;
|
||||
|
||||
batchResults.push({
|
||||
upc: row.upc,
|
||||
rowNumber: row.rowNumber,
|
||||
record: {
|
||||
asin: detail?.asin ?? row.upc,
|
||||
name: row.name ?? row.upc,
|
||||
unitCost: row.unitCost ?? 0,
|
||||
brand: row.brand,
|
||||
category: row.category,
|
||||
},
|
||||
lookup:
|
||||
detail ??
|
||||
({
|
||||
requestedUpc: row.upc,
|
||||
normalizedUpc: row.upc,
|
||||
status: "request_failed",
|
||||
asin: null,
|
||||
candidateAsins: [],
|
||||
keepaData: null,
|
||||
reason: "UPC lookup returned no result",
|
||||
} satisfies UpcLookupDetail),
|
||||
record: toSupplierInputRecord(row),
|
||||
product: null,
|
||||
lookup: detail,
|
||||
keepa: null,
|
||||
spApi: null,
|
||||
score: skippedScore(detail?.reason ?? "UPC unresolved"),
|
||||
@@ -465,7 +469,8 @@ export async function runUpcFileAnalysis(
|
||||
batchResults.push({
|
||||
upc: entry.detail.normalizedUpc,
|
||||
rowNumber: entry.row.rowNumber,
|
||||
record: entry.product,
|
||||
record: toSupplierInputRecord(entry.row),
|
||||
product: entry.product,
|
||||
lookup: entry.detail,
|
||||
keepa,
|
||||
spApi,
|
||||
@@ -488,6 +493,7 @@ export async function runUpcFileAnalysis(
|
||||
|
||||
const exportSummary = summarizeSupplierResults(allResults, unresolvedByStatus);
|
||||
await writeSupplierWorkbook(outputFile, allResults, exportSummary);
|
||||
await completeRunInDb(runId);
|
||||
|
||||
if (allResults.length > 0) {
|
||||
const ranked = allResults
|
||||
@@ -530,6 +536,9 @@ export async function runUpcFileAnalysis(
|
||||
skippedInvalidUpc: readerSummary.skippedInvalidUpc,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
await failRunInDb(runId, error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (manageResources) {
|
||||
await disconnectCache();
|
||||
|
||||
Reference in New Issue
Block a user