164 lines
4.9 KiB
JavaScript
164 lines
4.9 KiB
JavaScript
const logger = require('./logger');
|
|
const { publicConfig, resolveSyncDateRange } = require('./config');
|
|
const { mapProductionOrder } = require('./mapper');
|
|
const { TinyClient, findArrayPaths, getTopLevelShape } = require('./tinyClient');
|
|
const { ProductionOrderRepository } = require('./db');
|
|
|
|
const safeRawPreview = (raw, maxKeys = 25) => {
|
|
if (!raw || typeof raw !== 'object') return raw;
|
|
const keys = Object.keys(raw).slice(0, maxKeys);
|
|
return keys.reduce((preview, key) => {
|
|
preview[key] = raw[key];
|
|
return preview;
|
|
}, {});
|
|
};
|
|
|
|
const orderWritePreview = (order, markers) => ({
|
|
production_orders: {
|
|
tiny_id: order.tinyId,
|
|
number: order.number,
|
|
status: order.status,
|
|
order_reference: order.orderReference,
|
|
issue_date: order.issueDate,
|
|
expected_date: order.expectedDate,
|
|
product_sku: order.productSku,
|
|
product_description: order.productDescription,
|
|
quantity: order.quantity,
|
|
unit: order.unit,
|
|
integration_status: order.integrationStatus,
|
|
tiny_payload_keys: order.tinyPayload && typeof order.tinyPayload === 'object' ? Object.keys(order.tinyPayload) : []
|
|
},
|
|
production_order_markers: markers
|
|
});
|
|
|
|
const runSync = async (config) => {
|
|
const syncDateRange = resolveSyncDateRange(config);
|
|
logger.info('Start config', {
|
|
...publicConfig(config),
|
|
resolvedDateRange: syncDateRange
|
|
});
|
|
|
|
const tinyClient = new TinyClient(config);
|
|
const repository = config.dryRun ? null : new ProductionOrderRepository(config.databaseUrl);
|
|
|
|
const stats = {
|
|
pagesFetched: 0,
|
|
ordersFound: 0,
|
|
rowsMapped: 0,
|
|
rowsWouldUpsert: 0,
|
|
rowsUpserted: 0,
|
|
skippedInvalidRows: 0,
|
|
detailErrors: 0
|
|
};
|
|
|
|
if (repository) await repository.connect();
|
|
|
|
try {
|
|
let page = 1;
|
|
let totalPages = 1;
|
|
|
|
do {
|
|
const pageResult = await tinyClient.listProductionOrders(page, {
|
|
startDate: syncDateRange.startDate,
|
|
endDate: syncDateRange.endDate
|
|
});
|
|
|
|
stats.pagesFetched += 1;
|
|
totalPages = pageResult.totalPages;
|
|
logger.info('Tiny production orders page fetched', {
|
|
page,
|
|
totalPages,
|
|
dateRange: {
|
|
startDate: syncDateRange.startDate,
|
|
endDate: syncDateRange.endDate,
|
|
mode: syncDateRange.mode,
|
|
source: syncDateRange.source
|
|
},
|
|
endpoint: config.tinyListEndpoint,
|
|
ordersFound: pageResult.orders.length
|
|
});
|
|
|
|
if (config.dryRun) {
|
|
logger.info('DRY_RUN Tiny payload shape', {
|
|
page,
|
|
endpoint: config.tinyListEndpoint,
|
|
shape: getTopLevelShape(pageResult.raw),
|
|
arrayPaths: findArrayPaths(pageResult.raw)
|
|
});
|
|
logger.info('DRY_RUN extracted order preview', {
|
|
page,
|
|
extractedOrders: pageResult.orders.length,
|
|
firstOrderRawPreview: safeRawPreview(pageResult.orders[0] || null)
|
|
});
|
|
}
|
|
|
|
for (const summaryRecord of pageResult.orders) {
|
|
stats.ordersFound += 1;
|
|
let record = summaryRecord;
|
|
|
|
if (config.tinyFetchDetails) {
|
|
const mappedSummary = mapProductionOrder(summaryRecord);
|
|
const tinyId = mappedSummary.order?.tinyId;
|
|
if (tinyId) {
|
|
try {
|
|
record = await tinyClient.getProductionOrderDetail(tinyId);
|
|
} catch (error) {
|
|
stats.detailErrors += 1;
|
|
logger.warn('Tiny detail fetch failed, using list summary payload', {
|
|
tinyId,
|
|
error: error.message
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
const mapped = mapProductionOrder(record);
|
|
if (!mapped.order) {
|
|
stats.skippedInvalidRows += 1;
|
|
logger.warn('Skipped invalid Tiny production order row', {
|
|
reason: mapped.reason,
|
|
rawPreview: safeRawPreview(mapped.raw || record)
|
|
});
|
|
continue;
|
|
}
|
|
stats.rowsMapped += 1;
|
|
|
|
if (config.dryRun) {
|
|
stats.rowsWouldUpsert += 1;
|
|
logger.info('DRY_RUN would upsert production order', {
|
|
tinyId: mapped.order.tinyId,
|
|
number: mapped.order.number,
|
|
status: mapped.order.status,
|
|
markers: mapped.markers.length,
|
|
writePreview: orderWritePreview(mapped.order, mapped.markers),
|
|
rawPreview: safeRawPreview(mapped.raw)
|
|
});
|
|
continue;
|
|
}
|
|
|
|
await repository.upsertOrder(mapped.order, mapped.markers);
|
|
stats.rowsUpserted += 1;
|
|
}
|
|
|
|
logger.info('Page sync complete', {
|
|
page,
|
|
rowsMappedSoFar: stats.rowsMapped,
|
|
rowsWouldUpsertSoFar: stats.rowsWouldUpsert,
|
|
rowsUpsertedSoFar: stats.rowsUpserted,
|
|
skippedInvalidRowsSoFar: stats.skippedInvalidRows
|
|
});
|
|
|
|
page += 1;
|
|
} while (page <= totalPages);
|
|
|
|
logger.info('End summary', stats);
|
|
return stats;
|
|
} finally {
|
|
if (repository) await repository.close();
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
runSync
|
|
};
|