@contrail/flexplm 1.7.4-alpha.ad2bc4e → 1.7.4-alpha.b088cfc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -214,7 +214,7 @@ class BaseProcessPublishAssortment {
214
214
  return sdk_1.Request.request(resource, {
215
215
  method: 'GET',
216
216
  headers: {
217
- "Accept": 'application/json',
217
+ 'Accept': 'application/json',
218
218
  'Content-Type': 'application/json',
219
219
  },
220
220
  });
@@ -308,7 +308,7 @@ class BaseProcessPublishAssortment {
308
308
  return await sdk_1.Request.request(resourceString, {
309
309
  method: 'GET',
310
310
  Headers: {
311
- "Accept": 'application/json',
311
+ 'Accept': 'application/json',
312
312
  'Content-Type': 'application/json',
313
313
  },
314
314
  });
@@ -391,6 +391,31 @@ class BaseProcessPublishAssortment {
391
391
  console.info('getDeleteChanges()-currentAssortmentItemIds: ' + currentAssortmentItemIds);
392
392
  console.info('getDeleteChanges()-deleteIds: ' + deleteIds);
393
393
  }
394
+ // ---- ET-243: a family row removed while the family still has option rows is reported
395
+ // in neither the delete data nor the publish-change detail. Derive it by diffing the
396
+ // baseline as of sinceDate against the current baseline. ----
397
+ const priorApc = apcHistory.find(apc => apc?.id !== assortmentPublishChange?.id
398
+ && Date.parse(apc?.createdOn) === sinceDateMilliseconds)
399
+ || apcHistory[previousApcIndex];
400
+ const priorBaseline = priorApc?.assortmentBaselineDownloadLink
401
+ ? await this.downloadAssortmentBaseline(priorApc)
402
+ : await this.downloadAssortmentBaseline(await this.downloadAssortmentPublishChange(priorApc?.assortmentId, priorApc?.id));
403
+ const currentItemIds = this.getBaselineItemIds(assortmentBaseline);
404
+ const collectedIds = apcDeletes.map(dItem => dItem?.itemId);
405
+ for (const aItem of priorBaseline?.assortmentItems || []) {
406
+ const itemId = aItem?.itemId;
407
+ const item = aItem?.item;
408
+ if (!itemId || !item)
409
+ continue;
410
+ if (itemId !== item?.itemFamilyId)
411
+ continue; // family rows only
412
+ if (currentItemIds.includes(itemId))
413
+ continue; // still in the assortment
414
+ if (collectedIds.includes(itemId))
415
+ continue; // already reported as a delete
416
+ console.info('getDeleteChanges(): adding removed family row: ' + itemId);
417
+ apcDeletes.push(aItem);
418
+ }
394
419
  return apcDeletes;
395
420
  }
396
421
  getBaselineItemIds(assortmentBaseline) {
@@ -839,40 +864,6 @@ class BaseProcessPublishAssortment {
839
864
  ifc.colorDeletes.push(itemId);
840
865
  }
841
866
  } // End deletes for loop
842
- // ---- ET-243: the family's own row left the assortment while the family still has
843
- // option rows. The platform reports this in changeDetail.familyItemsRemoved, and the
844
- // row is in neither the baseline nor the delete-data file, so neither loop above can
845
- // see it. `deletes` is folded in as a fallback in case a removal is ever classified
846
- // there while still missing from the delete-data file. ----
847
- for (const removedRow of [...familyItemsRemoved, ...deletes]) {
848
- const itemId = removedRow?.itemId ?? removedRow?.id;
849
- const item = removedRow?.item;
850
- if (!itemId || !item)
851
- continue;
852
- const itemFamilyId = item?.itemFamilyId ?? itemId;
853
- if (itemId !== itemFamilyId)
854
- continue; // family rows only
855
- if (assortmentItemFullChangeMap.has(itemId))
856
- continue; // still in baseline -> loop 1
857
- if (assortmentItemDeleteMap.has(itemId))
858
- continue; // hard delete -> loop 2
859
- if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId))
860
- continue;
861
- if (!this.meetsCriteria(removedRow))
862
- continue;
863
- let ifc = itemFamilyChanges.get(itemFamilyId);
864
- if (!ifc) {
865
- ifc = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, pcd.sinceDate);
866
- itemFamilyChanges.set(itemFamilyId, ifc);
867
- }
868
- if (!ifc.itemFamilyObject) {
869
- ifc.itemFamilyObject = item;
870
- }
871
- if (removedRow?.projectItem) {
872
- ifc.assortmentItemFullChangeMap.set(itemId, removedRow);
873
- }
874
- ifc.familyItemRemoved = true;
875
- }
876
867
  if (app_framework_1.Logger.isDebugOn()) {
877
868
  console.debug('returning size: ' + itemFamilyChanges.size);
878
869
  for (const [key, value] of itemFamilyChanges) {
@@ -914,15 +905,11 @@ class BaseProcessPublishAssortment {
914
905
  const LCSSeason = Object.assign({}, seasonFed);
915
906
  const assortment = await this.getAssortment(assortmentId);
916
907
  const projectItem = this.getProjectItem(itemFamilyChanges, itemFamilyChanges.itemFamilyId);
917
- // ---- NEW Step to enable ItemFamily publish ET-243: added color suppressed length to existing condition and familyItemRemovedFlag
908
+ // ---- NEW Step to enable ItemFamily publish ET-243: added color suppressed length
918
909
  const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
919
- || itemFamilyChanges.familyItemRemoved
920
910
  || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
921
911
  || itemFamilyChanges.colorSuppressed.length > 0
922
912
  || (projectItem && this.updatedSinceDate(projectItem, itemFamilyChanges.sinceDate));
923
- // familyItemRemoved: the family's own assortment row left the assortment while the
924
- // family still has option rows. The family stays in the season, so this publishes an
925
- // LCSProductSeasonLink update and no LCSSKUSeasonLink.
926
913
  if (familyAssortmentItem) {
927
914
  // Product-season add
928
915
  const entityReference = itemFamilyChanges.itemFamilyId;
@@ -921,6 +921,39 @@ describe('getDeleteChanges', () => {
921
921
  expect(itemIds).toContain('qNCQmTFcFxwbT-oG');
922
922
  spy.mockRestore();
923
923
  });
924
+ const stubDownloads = (ppa, history, priorBaseline) => {
925
+ jest.spyOn(ppa, 'downloadDeleteChanges').mockImplementation(async (apc) => {
926
+ if (!apc)
927
+ return [];
928
+ return history.find(h => h['id'] === apc['id'])?.['deleteData'] || [];
929
+ });
930
+ jest.spyOn(ppa, 'downloadAssortmentBaseline').mockResolvedValue(priorBaseline);
931
+ };
932
+ const famRow = (id, lifecycleStage = 'development') => ({
933
+ itemId: id,
934
+ item: { id, itemFamilyId: id, lifecycleStage, roles: ['family'] },
935
+ projectItem: { id: 'pi-' + id }
936
+ });
937
+ const optRow = (id, familyId) => ({
938
+ itemId: id,
939
+ item: { id, itemFamilyId: familyId, lifecycleStage: 'development', roles: ['color', 'option'] }
940
+ });
941
+ it('adds a family row that left the assortment while its option rows remain', async () => {
942
+ const history = new Array(...mockData_1.apc_2bOWR2j9R0QThDVu_delete_history);
943
+ const apcIndex = history.length - 1;
944
+ const apc = history[apcIndex];
945
+ const apcDate = new Date(history[apcIndex - 1]['createdOn']);
946
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
947
+ const fam = famRow('FAM1');
948
+ const opt = optRow('OPT1', 'FAM1');
949
+ stubDownloads(ppa, history, { assortmentItems: [fam, opt] });
950
+ const deleteChanges = await ppa.getDeleteChanges(apc, history, { assortmentItems: [opt] }, apcDate);
951
+ const itemIds = deleteChanges.map(d => d['itemId']);
952
+ expect(itemIds).toContain('FAM1');
953
+ expect(itemIds).not.toContain('OPT1');
954
+ expect(deleteChanges.find(d => d['itemId'] === 'FAM1')['projectItem']['id']).toEqual('pi-FAM1');
955
+ jest.restoreAllMocks();
956
+ });
924
957
  });
925
958
  describe('buildDeleteChanges', () => {
926
959
  const config = {};
@@ -1480,31 +1513,18 @@ describe('getItemFamilyChanges - lifecycle-suppressed options', () => {
1480
1513
  const ifc = pcd.itemFamilyChanges.get(familyId);
1481
1514
  expect(ifc.familyItemRemoved).toBe(true);
1482
1515
  expect(ifc.familyUpdate).toBe(false); // fails if the !familyItemRemoved guard is dropped
1483
- expect(ifc.assortmentItemFullChangeMap.size).toEqual(1); // third pass must not re-insert the row
1484
1516
  });
1485
- it('flags the family when its row is removed but option rows remain', () => {
1517
+ it('flags familyDelete when the family row left the assortment but option rows remain', () => {
1486
1518
  const ppa = buildPpa();
1487
1519
  const assortmentItems = [optionRow(maroonId, beforeSince), optionRow(limeId, beforeSince, 'released')];
1488
1520
  const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems });
1489
- const details = { ...noDetailChanges(), familyItemsRemoved: [{ itemId: familyId, item: familyItem() }] };
1490
- const pcd = buildPcd([familyId, limeId]);
1491
- pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, new Map());
1492
- const ifc = pcd.itemFamilyChanges.get(familyId);
1493
- expect(ifc.familyItemRemoved).toBe(true); // false before the third pass
1494
- expect(ifc.itemFamilyObject.id).toEqual(familyId);
1495
- expect(ifc.colorDeletes.length).toEqual(0); // no SKU event should follow
1496
- expect(ifc.assortmentItemFullChangeMap.has(familyId)).toBe(false); // removed row carries no projectItem
1497
- });
1498
- it('leaves a hard-deleted family row to the delete pass', () => {
1499
- const ppa = buildPpa();
1500
- const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems: [] });
1501
1521
  const deleteMap = new Map([[familyId, familyRow()]]);
1502
- const details = { ...noDetailChanges(), familyItemsRemoved: [{ itemId: familyId, item: familyItem() }] };
1503
- const pcd = buildPcd([familyId]);
1504
- pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, deleteMap);
1522
+ const pcd = buildPcd([familyId, limeId]);
1523
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, deleteMap);
1505
1524
  const ifc = pcd.itemFamilyChanges.get(familyId);
1506
- expect(ifc.familyDelete).toBe(true); // set by the delete pass
1507
- expect(ifc.familyItemRemoved).toBe(false); // third pass skipped it - no double handling
1525
+ expect(ifc.familyDelete).toBe(true);
1526
+ expect(ifc.colorDeletes.length).toEqual(0); // no SKU event
1527
+ expect(ppa.getProjectItem(ifc, familyId).id).toEqual('pi-' + familyId); // family's own projectItem
1508
1528
  });
1509
1529
  });
1510
1530
  describe('getProjectItem', () => {
@@ -1943,7 +1963,6 @@ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
1943
1963
  const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1944
1964
  const itemFamilyId = 'AERfdvdAt1HHEVvQ';
1945
1965
  const prodEntityData = { id: itemFamilyId, identifier: 'FAM-11', itemNumber: 'FAM-11' };
1946
- //const projectItem = { id: 'proj-fam', updatedOn: '2023-01-15T00:00:00.000Z' };
1947
1966
  const projectItem = { id: 'proj-fam', updatedOn: '2022-12-01T00:00:00.000Z' };
1948
1967
  const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1949
1968
  const seasonFed = {
@@ -1952,7 +1971,7 @@ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
1952
1971
  flexPLMSeasonName: 'Fall 2023'
1953
1972
  };
1954
1973
  const itemFamilyChanges = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1955
- itemFamilyChanges.familyItemRemoved = true; // the only trigger
1974
+ itemFamilyChanges.familyDelete = true; // the only trigger
1956
1975
  itemFamilyChanges.colorUnchanged.push('rsmWhWiXRNv_XBJG'); // surviving option must not emit
1957
1976
  itemFamilyChanges.itemFamilyObject = prodEntityData;
1958
1977
  jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.7.4-alpha.ad2bc4e",
3
+ "version": "1.7.4-alpha.b088cfc",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",