mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 11:05:24 +02:00
feat(bc): sync pricing units2
This commit is contained in:
+2
-1
@@ -15,7 +15,8 @@ BC_ITEMS_WRITE_METHOD="PATCH"
|
|||||||
BC_ITEMS_WRITE_URL_TEMPLATE="{{itemsUrl}}('{{itemNo}}')"
|
BC_ITEMS_WRITE_URL_TEMPLATE="{{itemsUrl}}('{{itemNo}}')"
|
||||||
BC_ITEMS_WRITE_BODY_TEMPLATE=''
|
BC_ITEMS_WRITE_BODY_TEMPLATE=''
|
||||||
BC_UOM_WRITE_METHOD="PATCH"
|
BC_UOM_WRITE_METHOD="PATCH"
|
||||||
BC_UOM_WRITE_URL_TEMPLATE="{{itemUnitsOfMeasureUrl}}('{{itemNo}}')"
|
BC_UOM_CODE="OUTER"
|
||||||
|
BC_UOM_WRITE_URL_TEMPLATE="{{itemUnitsOfMeasureUrl}}(itemNo='{{itemNo}}',code='{{itemUnitsCode}}')"
|
||||||
BC_UOM_WRITE_BODY_TEMPLATE=''
|
BC_UOM_WRITE_BODY_TEMPLATE=''
|
||||||
|
|
||||||
# APP_URL: The URL where this applet is hosted.
|
# APP_URL: The URL where this applet is hosted.
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ View your app in AI Studio: https://ai.studio/apps/bac9908e-4996-4e4c-8ec7-3add5
|
|||||||
`BC_WRITE_METHOD`, `BC_WRITE_URL_TEMPLATE`, `BC_WRITE_BODY_TEMPLATE`
|
`BC_WRITE_METHOD`, `BC_WRITE_URL_TEMPLATE`, `BC_WRITE_BODY_TEMPLATE`
|
||||||
5. For the safer BC sync preview/apply flow, you can also set:
|
5. For the safer BC sync preview/apply flow, you can also set:
|
||||||
`BC_ITEMS_WRITE_METHOD`, `BC_ITEMS_WRITE_URL_TEMPLATE`, `BC_ITEMS_WRITE_BODY_TEMPLATE`,
|
`BC_ITEMS_WRITE_METHOD`, `BC_ITEMS_WRITE_URL_TEMPLATE`, `BC_ITEMS_WRITE_BODY_TEMPLATE`,
|
||||||
`BC_UOM_WRITE_METHOD`, `BC_UOM_WRITE_URL_TEMPLATE`, `BC_UOM_WRITE_BODY_TEMPLATE`
|
`BC_UOM_CODE`, `BC_UOM_WRITE_METHOD`, `BC_UOM_WRITE_URL_TEMPLATE`, `BC_UOM_WRITE_BODY_TEMPLATE`
|
||||||
6. Run the app:
|
6. Run the app:
|
||||||
`npm run dev`
|
`npm run dev`
|
||||||
|
|||||||
+6
-1
@@ -16,8 +16,9 @@ export function getBcConfig(env = process.env) {
|
|||||||
itemsWriteMethod: String(env.BC_ITEMS_WRITE_METHOD || legacyWriteMethod).toUpperCase(),
|
itemsWriteMethod: String(env.BC_ITEMS_WRITE_METHOD || legacyWriteMethod).toUpperCase(),
|
||||||
itemsWriteUrlTemplate: env.BC_ITEMS_WRITE_URL_TEMPLATE || legacyWriteUrlTemplate,
|
itemsWriteUrlTemplate: env.BC_ITEMS_WRITE_URL_TEMPLATE || legacyWriteUrlTemplate,
|
||||||
itemsWriteBodyTemplate: env.BC_ITEMS_WRITE_BODY_TEMPLATE || null,
|
itemsWriteBodyTemplate: env.BC_ITEMS_WRITE_BODY_TEMPLATE || null,
|
||||||
|
itemUnitsCode: env.BC_UOM_CODE || 'OUTER',
|
||||||
itemUnitsWriteMethod: env.BC_UOM_WRITE_METHOD ? String(env.BC_UOM_WRITE_METHOD).toUpperCase() : 'PATCH',
|
itemUnitsWriteMethod: env.BC_UOM_WRITE_METHOD ? String(env.BC_UOM_WRITE_METHOD).toUpperCase() : 'PATCH',
|
||||||
itemUnitsWriteUrlTemplate: env.BC_UOM_WRITE_URL_TEMPLATE || `{{itemUnitsOfMeasureUrl}}('{{itemNo}}')`,
|
itemUnitsWriteUrlTemplate: env.BC_UOM_WRITE_URL_TEMPLATE || `{{itemUnitsOfMeasureUrl}}(itemNo='{{itemNo}}',code='{{itemUnitsCode}}')`,
|
||||||
itemUnitsWriteBodyTemplate: env.BC_UOM_WRITE_BODY_TEMPLATE || null,
|
itemUnitsWriteBodyTemplate: env.BC_UOM_WRITE_BODY_TEMPLATE || null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -30,6 +31,10 @@ export function getItemsUrl(config) {
|
|||||||
return `https://api.businesscentral.dynamics.com/v2.0/${config.tenantId}/production/api/craze/integrations/v1.0/companies(${config.companyId})/items`;
|
return `https://api.businesscentral.dynamics.com/v2.0/${config.tenantId}/production/api/craze/integrations/v1.0/companies(${config.companyId})/items`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getItemUnitsOfMeasureUrl(config) {
|
||||||
|
return `https://api.businesscentral.dynamics.com/v2.0/${config.tenantId}/production/api/craze/integrations/v1.0/companies(${config.companyId})/itemUnitsOfMeasure2`;
|
||||||
|
}
|
||||||
|
|
||||||
let tokenCache = { token: null, expiresAt: 0 };
|
let tokenCache = { token: null, expiresAt: 0 };
|
||||||
|
|
||||||
export async function getBCToken(config) {
|
export async function getBCToken(config) {
|
||||||
|
|||||||
+36
-34
@@ -1,9 +1,5 @@
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { getBcConfig, getBCToken, getItemsUrl } from './bc-runtime.js';
|
import { getBcConfig, getBCToken, getItemsUrl, getItemUnitsOfMeasureUrl } from './bc-runtime.js';
|
||||||
|
|
||||||
function getItemUnitsOfMeasureUrl(config) {
|
|
||||||
return `https://api.businesscentral.dynamics.com/v2.0/${config.tenantId}/production/api/craze/integrations/v1.0/companies(${config.companyId})/itemUnitsOfMeasure`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findHeaderIndex(headers, patterns) {
|
function findHeaderIndex(headers, patterns) {
|
||||||
const normalized = headers.map(h => String(h || '').toLowerCase());
|
const normalized = headers.map(h => String(h || '').toLowerCase());
|
||||||
@@ -89,13 +85,11 @@ export function buildBusinessCentralMappingPreview(headers, row) {
|
|||||||
|
|
||||||
const itemUnitsOfMeasurePayload = {
|
const itemUnitsOfMeasurePayload = {
|
||||||
itemNo: getValue(row, articleNoIdx),
|
itemNo: getValue(row, articleNoIdx),
|
||||||
qtyPerUnitOfMeasure6: toBcDecimal(getValue(row, unitsOuterIdx)),
|
code: 'OUTER',
|
||||||
width4: toBcDecimal(getValue(row, innerWIdx)),
|
qtyPerUnitOfMeasure: toBcDecimal(getValue(row, unitsOuterIdx)),
|
||||||
length4: toBcDecimal(getValue(row, innerLIdx)),
|
width: toBcDecimal(getValue(row, outerWIdx)),
|
||||||
height4: toBcDecimal(getValue(row, innerHIdx)),
|
length: toBcDecimal(getValue(row, outerLIdx)),
|
||||||
width6: toBcDecimal(getValue(row, outerWIdx)),
|
height: toBcDecimal(getValue(row, outerHIdx)),
|
||||||
length6: toBcDecimal(getValue(row, outerLIdx)),
|
|
||||||
height6: toBcDecimal(getValue(row, outerHIdx)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -115,13 +109,10 @@ export function buildBusinessCentralMappingPreview(headers, row) {
|
|||||||
],
|
],
|
||||||
itemUnitsFields: [
|
itemUnitsFields: [
|
||||||
makeFieldPreview('Article No.', articleNoIdx, 'itemNo', itemUnitsOfMeasurePayload.itemNo),
|
makeFieldPreview('Article No.', articleNoIdx, 'itemNo', itemUnitsOfMeasurePayload.itemNo),
|
||||||
makeFieldPreview('Units Outer', unitsOuterIdx, 'qtyPerUnitOfMeasure6', itemUnitsOfMeasurePayload.qtyPerUnitOfMeasure6),
|
makeFieldPreview('Units Outer', unitsOuterIdx, 'qtyPerUnitOfMeasure', itemUnitsOfMeasurePayload.qtyPerUnitOfMeasure),
|
||||||
makeFieldPreview('CDU/Inner W (cm)', innerWIdx, 'width4', itemUnitsOfMeasurePayload.width4),
|
makeFieldPreview('Outer W (cm)', outerWIdx, 'width', itemUnitsOfMeasurePayload.width),
|
||||||
makeFieldPreview('CDU/Inner L (cm)', innerLIdx, 'length4', itemUnitsOfMeasurePayload.length4),
|
makeFieldPreview('Outer L (cm)', outerLIdx, 'length', itemUnitsOfMeasurePayload.length),
|
||||||
makeFieldPreview('CDU/Inner H (cm)', innerHIdx, 'height4', itemUnitsOfMeasurePayload.height4),
|
makeFieldPreview('Outer H (cm)', outerHIdx, 'height', itemUnitsOfMeasurePayload.height),
|
||||||
makeFieldPreview('Outer W (cm)', outerWIdx, 'width6', itemUnitsOfMeasurePayload.width6),
|
|
||||||
makeFieldPreview('Outer L (cm)', outerLIdx, 'length6', itemUnitsOfMeasurePayload.length6),
|
|
||||||
makeFieldPreview('Outer H (cm)', outerHIdx, 'height6', itemUnitsOfMeasurePayload.height6),
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -238,13 +229,21 @@ function getItemsSelect() {
|
|||||||
function getItemUnitsSelect() {
|
function getItemUnitsSelect() {
|
||||||
return [
|
return [
|
||||||
'itemNo',
|
'itemNo',
|
||||||
'qtyPerUnitOfMeasure6',
|
'code',
|
||||||
'width4',
|
'qtyPerUnitOfMeasure',
|
||||||
'length4',
|
'qtyRoundingPrecision',
|
||||||
'height4',
|
'length',
|
||||||
'width6',
|
'width',
|
||||||
'length6',
|
'height',
|
||||||
'height6',
|
'cubage',
|
||||||
|
'weight',
|
||||||
|
'layerPerPalet2CRZ',
|
||||||
|
'outerPerLayer2CRZ',
|
||||||
|
'barCodeCRZ',
|
||||||
|
'layerPerPaletCRZ',
|
||||||
|
'outerPerLayerCRZ',
|
||||||
|
'netWeightCRZ',
|
||||||
|
'innerTypeBCT',
|
||||||
].join(',');
|
].join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +269,7 @@ export async function fetchBusinessCentralSnapshot(config, token, articleNo) {
|
|||||||
const { json: itemsJson, etag: itemsEtag } = await fetchJsonOrThrow(itemsUrl, token, 'BC GET items');
|
const { json: itemsJson, etag: itemsEtag } = await fetchJsonOrThrow(itemsUrl, token, 'BC GET items');
|
||||||
const item = Array.isArray(itemsJson.value) && itemsJson.value.length > 0 ? itemsJson.value[0] : null;
|
const item = Array.isArray(itemsJson.value) && itemsJson.value.length > 0 ? itemsJson.value[0] : null;
|
||||||
|
|
||||||
const uomFilter = encodeURIComponent(`itemNo eq '${articleNo}'`);
|
const uomFilter = encodeURIComponent(`itemNo eq '${articleNo}' and code eq '${config.itemUnitsCode || 'OUTER'}'`);
|
||||||
const uomUrl = `${getItemUnitsOfMeasureUrl(config)}?$filter=${uomFilter}&$select=${getItemUnitsSelect()}`;
|
const uomUrl = `${getItemUnitsOfMeasureUrl(config)}?$filter=${uomFilter}&$select=${getItemUnitsSelect()}`;
|
||||||
const { json: uomJson, etag: uomEtag } = await fetchJsonOrThrow(uomUrl, token, 'BC GET itemUnitsOfMeasure');
|
const { json: uomJson, etag: uomEtag } = await fetchJsonOrThrow(uomUrl, token, 'BC GET itemUnitsOfMeasure');
|
||||||
const itemUnitsOfMeasure = Array.isArray(uomJson.value) && uomJson.value.length > 0 ? uomJson.value[0] : null;
|
const itemUnitsOfMeasure = Array.isArray(uomJson.value) && uomJson.value.length > 0 ? uomJson.value[0] : null;
|
||||||
@@ -388,13 +387,15 @@ function renderTemplate(template, context) {
|
|||||||
|
|
||||||
const DECIMAL_FIELDS = new Set([
|
const DECIMAL_FIELDS = new Set([
|
||||||
'minimumOrderQuantity',
|
'minimumOrderQuantity',
|
||||||
'qtyPerUnitOfMeasure6',
|
'qtyPerUnitOfMeasure',
|
||||||
'width4',
|
'length',
|
||||||
'length4',
|
'width',
|
||||||
'height4',
|
'height',
|
||||||
'width6',
|
'cubage',
|
||||||
'length6',
|
'weight',
|
||||||
'height6',
|
'layerPerPalet2CRZ',
|
||||||
|
'outerPerLayer2CRZ',
|
||||||
|
'netWeightCRZ',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const EMPTY_STRING_FIELDS = new Set([
|
const EMPTY_STRING_FIELDS = new Set([
|
||||||
@@ -518,6 +519,7 @@ export async function applyBusinessCentralSync(config, token, headers, row, prev
|
|||||||
itemUnitsOfMeasureUrl: getItemUnitsOfMeasureUrl(config),
|
itemUnitsOfMeasureUrl: getItemUnitsOfMeasureUrl(config),
|
||||||
articleNo: mapping.articleNo,
|
articleNo: mapping.articleNo,
|
||||||
itemNo: mapping.articleNo,
|
itemNo: mapping.articleNo,
|
||||||
|
itemUnitsCode: config.itemUnitsCode || 'OUTER',
|
||||||
systemId: snapshot.item?.systemId || '',
|
systemId: snapshot.item?.systemId || '',
|
||||||
cpnpNo: mapping.itemsPayload.cpnpNo,
|
cpnpNo: mapping.itemsPayload.cpnpNo,
|
||||||
itemsPayload: preview.items.desired,
|
itemsPayload: preview.items.desired,
|
||||||
|
|||||||
@@ -497,7 +497,7 @@ export function MatrixView({ data, headers, rowStatuses }: MatrixViewProps) {
|
|||||||
<div className="rounded-lg border border-slate-700 bg-slate-950/60 p-4">
|
<div className="rounded-lg border border-slate-700 bg-slate-950/60 p-4">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-semibold text-white">API `itemUnitsOfMeasure` preview</h3>
|
<h3 className="text-sm font-semibold text-white">API `itemUnitsOfMeasure2` preview</h3>
|
||||||
<p className="text-xs text-slate-400">SKU {bcMappingPreview.articleNo}</p>
|
<p className="text-xs text-slate-400">SKU {bcMappingPreview.articleNo}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -81,9 +81,6 @@ export function buildBusinessCentralMappingPreview(headers: string[], row: Excel
|
|||||||
const cpnpIdx = findHeaderIndex(headers, ['cpnp']);
|
const cpnpIdx = findHeaderIndex(headers, ['cpnp']);
|
||||||
|
|
||||||
const unitsOuterIdx = findHeaderIndex(headers, ['units', 'outer']);
|
const unitsOuterIdx = findHeaderIndex(headers, ['units', 'outer']);
|
||||||
const innerWIdx = findHeaderIndex(headers, ['inner', 'w']);
|
|
||||||
const innerLIdx = findHeaderIndex(headers, ['inner', 'l']);
|
|
||||||
const innerHIdx = findHeaderIndex(headers, ['inner', 'h']);
|
|
||||||
const outerWIdx = findHeaderIndex(headers, ['outer', 'w']);
|
const outerWIdx = findHeaderIndex(headers, ['outer', 'w']);
|
||||||
const outerLIdx = findHeaderIndex(headers, ['outer', 'l']);
|
const outerLIdx = findHeaderIndex(headers, ['outer', 'l']);
|
||||||
const outerHIdx = findHeaderIndex(headers, ['outer', 'h']);
|
const outerHIdx = findHeaderIndex(headers, ['outer', 'h']);
|
||||||
@@ -104,13 +101,11 @@ export function buildBusinessCentralMappingPreview(headers: string[], row: Excel
|
|||||||
|
|
||||||
const itemUnitsOfMeasurePayload = {
|
const itemUnitsOfMeasurePayload = {
|
||||||
itemNo: getValue(row, articleNoIdx),
|
itemNo: getValue(row, articleNoIdx),
|
||||||
qtyPerUnitOfMeasure6: toBcDecimal(getValue(row, unitsOuterIdx)),
|
code: 'OUTER',
|
||||||
width4: toBcDecimal(getValue(row, innerWIdx)),
|
qtyPerUnitOfMeasure: toBcDecimal(getValue(row, unitsOuterIdx)),
|
||||||
length4: toBcDecimal(getValue(row, innerLIdx)),
|
width: toBcDecimal(getValue(row, outerWIdx)),
|
||||||
height4: toBcDecimal(getValue(row, innerHIdx)),
|
length: toBcDecimal(getValue(row, outerLIdx)),
|
||||||
width6: toBcDecimal(getValue(row, outerWIdx)),
|
height: toBcDecimal(getValue(row, outerHIdx)),
|
||||||
length6: toBcDecimal(getValue(row, outerLIdx)),
|
|
||||||
height6: toBcDecimal(getValue(row, outerHIdx)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -130,13 +125,10 @@ export function buildBusinessCentralMappingPreview(headers: string[], row: Excel
|
|||||||
],
|
],
|
||||||
itemUnitsFields: [
|
itemUnitsFields: [
|
||||||
makeFieldPreview('Article No.', articleNoIdx, 'itemNo', itemUnitsOfMeasurePayload.itemNo),
|
makeFieldPreview('Article No.', articleNoIdx, 'itemNo', itemUnitsOfMeasurePayload.itemNo),
|
||||||
makeFieldPreview('Units Outer', unitsOuterIdx, 'qtyPerUnitOfMeasure6', itemUnitsOfMeasurePayload.qtyPerUnitOfMeasure6),
|
makeFieldPreview('Units Outer', unitsOuterIdx, 'qtyPerUnitOfMeasure', itemUnitsOfMeasurePayload.qtyPerUnitOfMeasure),
|
||||||
makeFieldPreview('CDU/Inner W (cm)', innerWIdx, 'width4', itemUnitsOfMeasurePayload.width4),
|
makeFieldPreview('Outer W (cm)', outerWIdx, 'width', itemUnitsOfMeasurePayload.width),
|
||||||
makeFieldPreview('CDU/Inner L (cm)', innerLIdx, 'length4', itemUnitsOfMeasurePayload.length4),
|
makeFieldPreview('Outer L (cm)', outerLIdx, 'length', itemUnitsOfMeasurePayload.length),
|
||||||
makeFieldPreview('CDU/Inner H (cm)', innerHIdx, 'height4', itemUnitsOfMeasurePayload.height4),
|
makeFieldPreview('Outer H (cm)', outerHIdx, 'height', itemUnitsOfMeasurePayload.height),
|
||||||
makeFieldPreview('Outer W (cm)', outerWIdx, 'width6', itemUnitsOfMeasurePayload.width6),
|
|
||||||
makeFieldPreview('Outer L (cm)', outerLIdx, 'length6', itemUnitsOfMeasurePayload.length6),
|
|
||||||
makeFieldPreview('Outer H (cm)', outerHIdx, 'height6', itemUnitsOfMeasurePayload.height6),
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user