Files
inkreach-official-website/apps/api/src/sync/sds-product-detail.mapper.spec.ts
T
yeuimu b5875d33b2 fix(sync): drop local size-code column (P/M/G/GG) from SDS size charts
巴西/西语市场上游尺码表带一个名为「尺码」的列,值是本地尺码码
P/M/G/GG(=S/M/L/XL),与行首尺码重复且非测量值;解析时整列丢弃。
其他非数字参考列(身高/体重/建议体重)保留。存量 2 条详情已修复
并重算受影响族。
2026-08-29 08:55:16 +08:00

124 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
normalizeProductDetail,
parsePackageSpecs,
parseSizeChart,
} from './sds-product-detail.mapper';
const sizeTable = JSON.stringify([
['尺码', '衣长(cm/in)', '胸围(cm/in)', '肩宽(cm/in)', '袖长(cm/in)'].map((content) => ({ content, remark: '' })),
['S', '71', '92', '43', '22'].map((content) => ({ content, remark: '' })),
['M', '74', '102', '45', '22'].map((content) => ({ content, remark: '' })),
['L', '76', '112', '48', '23'].map((content) => ({ content, remark: '' })),
['XL', '79', '122', '51', '23'].map((content) => ({ content, remark: '' })),
['2XL', '82', '132', '53', '25'].map((content) => ({ content, remark: '' })),
['3XL', '84', '142', '56', '25'].map((content) => ({ content, remark: '' })),
]);
const packageTable = JSON.stringify([
['尺码', '包装尺寸(cm', '包装尺寸(in', '包装体积(cm³)', '包装体积(in³)', '含包装重量(g', '含包装重量(lb'].map((content) => ({ content })),
['S', '36.0*26.0*1.0\t', '14.17*10.24*0.39\t', '936.00', '57.12', '208.00', '0.46'].map((content) => ({ content })),
['M', '36.0*26.0*1.0', '14.17*10.24*0.39', '936.00', '57.12', '218.00', '0.48'].map((content) => ({ content })),
]);
describe('SDS product detail mapper', () => {
it('parses the product_detail.txt size table into structured rows', () => {
const chart = parseSizeChart(sizeTable) as any;
expect(chart.columns.map((column: any) => column.key)).toEqual([
'bodyLength',
'chest',
'shoulder',
'sleeveLength',
]);
expect(chart.rows).toHaveLength(6);
expect(chart.rows[0].measurements[0]).toEqual({ key: 'bodyLength', cm: '71', in: '27.95' });
});
it('drops the local size-code column (P/M/G/GG) named 尺码 when all values are non-numeric', () => {
// 巴西/西语市场的 SDS 上游在表头第二列再放一个「尺码」列,值是本地尺码码
// P/M/G/GG=S/M/L/XL),与行首尺码重复且非测量值,应整列丢弃
const raw = JSON.stringify([
['尺码', '尺码(cm/in)', '肩宽(cm/in)', '胸围(cm/in)'].map((content) => ({ content, remark: '' })),
['S', 'P', '52', '106'].map((content) => ({ content, remark: '' })),
['M', 'M', '55', '112'].map((content) => ({ content, remark: '' })),
['L', 'G', '58', '118'].map((content) => ({ content, remark: '' })),
['XL', 'GG', '61', '124'].map((content) => ({ content, remark: '' })),
]);
const chart = parseSizeChart(raw) as any;
expect(chart.columns.map((c: any) => c.name)).toEqual(['肩宽', '胸围']);
expect(chart.rows[0].measurements.map((m: any) => m.key)).toEqual(['shoulder', 'chest']);
expect(chart.rows[0].measurements[0]).toEqual({ key: 'shoulder', cm: '52', in: '20.47' });
});
it('keeps a 尺码-named column when its values are numeric', () => {
const raw = JSON.stringify([
['尺码', '尺码(cm/in)', '胸围(cm/in)'].map((content) => ({ content, remark: '' })),
['S', '1', '106'].map((content) => ({ content, remark: '' })),
['M', '2', '112'].map((content) => ({ content, remark: '' })),
]);
const chart = parseSizeChart(raw) as any;
expect(chart.columns).toHaveLength(2);
expect(chart.rows[0].measurements[0].cm).toBe('1');
});
it('keeps non-numeric reference columns like 身高/体重', () => {
const raw = JSON.stringify([
['尺码', '身高(cm/in)', '体重(cm/in)'].map((content) => ({ content, remark: '' })),
['S', '50-60', '3-4kg'].map((content) => ({ content, remark: '' })),
['M', '60-70', '4-5kg'].map((content) => ({ content, remark: '' })),
]);
const chart = parseSizeChart(raw) as any;
expect(chart.columns.map((c: any) => c.name)).toEqual(['身高', '体重']);
});
it('parses packaging dimensions and weights', () => {
const specs = parsePackageSpecs(packageTable) as any;
expect(specs.rows).toHaveLength(2);
expect(specs.rows[0].dimensionsCm).toEqual({ length: '36.0', width: '26.0', height: '1.0' });
expect(specs.rows[0].grossWeightG).toBe('208.00');
});
it('normalizes detail text, options and variants', () => {
const normalized = normalizeProductDetail({
id: 168746,
sku: 'OZ10827003',
english_name: 't-shirt',
productionCycle: 24,
minWeight: 250,
product_details: {
production_process: '白墨烫画',
material_description: '100%纯棉',
product_size: sizeTable,
packaging_specification: packageTable,
},
subproducts: {
attributers: [{
size: 'S',
sizeId: 1922304,
colors: [{ colorId: 1139383, color: '#000300', color_name: 'black', colorSort: 1 }],
}],
items: [{
id: 168747,
sku: 'OZ10827003001',
size: 'S',
sizeId: 1922304,
colorId: 1139383,
color: { colorId: 1139383, color: '#000300', color_name: 'black' },
currentPrice: 38,
originalPrice: 38,
weight: 250,
box_length: 30,
box_width: 20,
box_height: 5,
status: 1,
delFlag: '0',
}],
},
});
expect(normalized.productCode).toBe('OZ10827003');
expect(normalized.productionProcess).toBe('白墨烫画');
expect(normalized.variants[0].sku).toBe('OZ10827003001');
expect(normalized.variants[0].price?.toString()).toBe('38');
});
});