Files
production-order-tiny/src/sync.js
Cauê Faleiros cd6b4c93f0 first commit
2026-07-02 12:54:31 -03:00

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
};