import { activeAmazonShops, allRows, dateValue, forward, hash, log, regionByMarketplace, shopPointer, sleep, upsertMany, writeAudit, } from './sp-api-collector-lib.mjs'; const shops = await activeAmazonShops(); const listings = await allRows('Listing', 'asin,shop'); const capturedAt = dateValue(new Date()); for (const shop of shops) { const asins = [...new Set(listings .filter(item => item.shop?.objectId === shop.objectId && item.asin) .map(item => String(item.asin).trim().toUpperCase()))]; let records = 0; let emptyResponses = 0; let failures = 0; const startedAt = new Date(); for (const asin of asins) { const endpoints = [ ['item_browse_node', `/customerFeedback/2024-06-01/items/${asin}/browseNode?marketplaceId=${shop.marketplaceId}`], ['item_review_topics', `/customerFeedback/2024-06-01/items/${asin}/reviews/topics?marketplaceId=${shop.marketplaceId}&sortBy=MENTIONS&sortOrder=DESC`], ['item_review_trends', `/customerFeedback/2024-06-01/items/${asin}/reviews/trends?marketplaceId=${shop.marketplaceId}`], ]; for (const [feedbackType, endpoint] of endpoints) { try { const result = await forward(shop.objectId, endpoint, 'GET', undefined, 2); if (!result || Object.keys(result).length === 0) { emptyResponses++; continue; } const browseNodeId = String(result.browseNodeId || result.browseNode?.id || ''); await upsertMany('AmazonCustomerFeedback', [{ recordKey: `${shop.objectId}:${asin}:${feedbackType}`, marketplaceId: shop.marketplaceId, feedbackType, asin, browseNodeId, capturedAt, rawData: result, shop: shopPointer(shop.objectId), }]); records++; } catch (error) { failures++; log('feedback_failed', { shop: shop.name, asin, feedbackType, error: error.message }); } await sleep(500); } } await writeAudit({ dataType: 'customer_feedback', endpoint: '/customerFeedback/2024-06-01', status: failures ? 'partial' : 'completed', shop, region: regionByMarketplace[shop.marketplaceId] || 'UNKNOWN', marketplaceId: shop.marketplaceId, records, pages: asins.length * 3, error: failures ? `${failures} calls failed` : '', startedAt, details: { asins: asins.length, emptyResponses, failures }, }); log('feedback_completed', { shop: shop.name, asins: asins.length, records, emptyResponses, failures }); } for (const shop of shops) { const startedAt = new Date(); const statuses = ['WORKING', 'SHIPPED', 'RECEIVING', 'CLOSED', 'CANCELLED', 'DELETED', 'ERROR', 'IN_TRANSIT', 'DELIVERED', 'CHECKED_IN']; const after = new Date(Date.now() - 729 * 24 * 60 * 60 * 1000).toISOString(); const before = new Date().toISOString(); let token = ''; let pages = 0; let records = 0; const seenTokens = new Set(); try { do { pages++; const path = token ? `/fba/inbound/v0/shipments?QueryType=NEXT_TOKEN&MarketplaceId=${shop.marketplaceId}&NextToken=${encodeURIComponent(token)}` : `/fba/inbound/v0/shipments?QueryType=DATE_RANGE&MarketplaceId=${shop.marketplaceId}` + `&ShipmentStatusList=${statuses.join(',')}` + `&LastUpdatedAfter=${encodeURIComponent(after)}&LastUpdatedBefore=${encodeURIComponent(before)}`; const result = await forward(shop.objectId, path, 'GET', undefined, 2); const payload = result.payload || result; const shipments = payload.ShipmentData || payload.shipments || []; const rows = shipments.map(item => ({ recordKey: `${regionByMarketplace[shop.marketplaceId] || 'UNKNOWN'}:${item.ShipmentId || hash(item)}`, region: regionByMarketplace[shop.marketplaceId] || 'UNKNOWN', marketplaceId: shop.marketplaceId, shipmentId: item.ShipmentId || '', shipmentName: item.ShipmentName || '', shipmentStatus: item.ShipmentStatus || '', destinationFulfillmentCenterId: item.DestinationFulfillmentCenterId || '', labelPrepType: item.LabelPrepType || '', capturedAt, rawData: item, shop: shopPointer(shop.objectId), })); await upsertMany('AmazonInboundShipment', rows); records += rows.length; token = String(payload.NextToken || '').trim(); if (token) { if (seenTokens.has(token)) throw new Error('Repeated inbound NextToken'); seenTokens.add(token); await sleep(1200); } } while (token); await writeAudit({ dataType: 'inbound_shipments', endpoint: '/fba/inbound/v0/shipments', status: 'completed', shop, region: regionByMarketplace[shop.marketplaceId] || 'UNKNOWN', marketplaceId: shop.marketplaceId, records, pages, startedAt, details: { after, before }, }); log('inbound_completed', { shop: shop.name, records, pages }); } catch (error) { await writeAudit({ dataType: 'inbound_shipments', endpoint: '/fba/inbound/v0/shipments', status: records ? 'partial' : 'failed', shop, region: regionByMarketplace[shop.marketplaceId] || 'UNKNOWN', marketplaceId: shop.marketplaceId, records, pages, error: error.message, startedAt, }); log('inbound_failed', { shop: shop.name, records, pages, error: error.message }); } }