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

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.
@@ -12,6 +12,7 @@ export declare class ItemFamilyChanges {
12
12
  colorDeletes: string[];
13
13
  colorUpdates: string[];
14
14
  colorUnchanged: string[];
15
+ colorSuppressed: string[];
15
16
  assortmentItemFullChangeMap: Map<string, any>;
16
17
  assortmentItemDeleteMap: Map<string, any>;
17
18
  itemToFederatedIdMapping: Map<string, string>;
@@ -14,6 +14,7 @@ class ItemFamilyChanges {
14
14
  this.colorDeletes = [];
15
15
  this.colorUpdates = [];
16
16
  this.colorUnchanged = [];
17
+ this.colorSuppressed = []; // changed this window, but excluded by the lifecycle filter
17
18
  this.assortmentItemFullChangeMap = new Map();
18
19
  this.assortmentItemDeleteMap = new Map();
19
20
  this.itemToFederatedIdMapping = new Map();
@@ -32,6 +33,7 @@ class ItemFamilyChanges {
32
33
  s += ' colorDeletes: [' + this.colorDeletes + ']\n';
33
34
  s += ' colorUpdates: [' + this.colorUpdates + ']\n';
34
35
  s += ' colorUnchanged: [' + this.colorUnchanged + ']\n';
36
+ s += ' colorSuppressed: [' + this.colorSuppressed + ']\n';
35
37
  s += 'assortmentItemFullChangeMap:\n';
36
38
  s += 'size: ' + this.assortmentItemFullChangeMap.size + '\n[';
37
39
  for (const key of this.assortmentItemFullChangeMap.keys()) {
@@ -15,7 +15,7 @@ class BaseProcessPublishAssortment {
15
15
  constructor(_config, _dc, _mapFileUtil) {
16
16
  this.TTL = 30 * 24 * 60 * 60 * 1000; // 30 days
17
17
  this.cache = {
18
- carriedFromSeason: {}
18
+ carriedFromSeason: {},
19
19
  };
20
20
  this.config = _config;
21
21
  this.dc = _dc;
@@ -46,13 +46,13 @@ class BaseProcessPublishAssortment {
46
46
  console.log(message);
47
47
  const output = {
48
48
  results: { message, event },
49
- skip_await: true
49
+ skip_await: true,
50
50
  };
51
51
  return output;
52
52
  }
53
53
  apcHistory = await this.getApcHistory(assortmentId);
54
54
  const sinceDate = await this.getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory);
55
- //Get detail information
55
+ // Get detail information
56
56
  const assortmentPublishChange = await this.downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId);
57
57
  publisher = this.getPublisher(assortmentPublishChange);
58
58
  const changeDetail = await this.downloadHydratedChangeDetail(assortmentPublishChange);
@@ -91,7 +91,7 @@ class BaseProcessPublishAssortment {
91
91
  versionComments: apc?.versionComments,
92
92
  publishDate: apc?.createdOn,
93
93
  publisher,
94
- versionHistoryNumber
94
+ versionHistoryNumber,
95
95
  };
96
96
  return publishInfo;
97
97
  }
@@ -111,15 +111,15 @@ class BaseProcessPublishAssortment {
111
111
  return 'unknown';
112
112
  }
113
113
  const createdOnDate = new Date(createdOnString);
114
- createdOnDate.setMonth(createdOnDate.getMonth() - 1); //subtract 1 month
114
+ createdOnDate.setMonth(createdOnDate.getMonth() - 1); // subtract 1 month
115
115
  const createdOnStringMinus1 = createdOnDate.toISOString();
116
116
  const createdOn = 'ISGREATERTHAN ' + createdOnStringMinus1;
117
117
  const snapshots = await new sdk_1.Entities().get({
118
118
  entityName: 'entity-snapshot',
119
119
  criteria: {
120
120
  entityReference,
121
- createdOn
122
- }
121
+ createdOn,
122
+ },
123
123
  });
124
124
  const assortmentPublishChangeId = apc?.id;
125
125
  const snapshot = snapshots.find(snap => snap?.assortmentChangeId === assortmentPublishChangeId);
@@ -169,7 +169,7 @@ class BaseProcessPublishAssortment {
169
169
  }
170
170
  this.assortment = await new sdk_1.Entities().get({
171
171
  entityName: 'assortment',
172
- id: assortmentId
172
+ id: assortmentId,
173
173
  });
174
174
  return this.assortment;
175
175
  }
@@ -214,9 +214,9 @@ class BaseProcessPublishAssortment {
214
214
  return sdk_1.Request.request(resource, {
215
215
  method: 'GET',
216
216
  headers: {
217
- Accept: 'application/json',
218
- 'Content-Type': 'application/json'
219
- }
217
+ "Accept": 'application/json',
218
+ 'Content-Type': 'application/json',
219
+ },
220
220
  });
221
221
  }
222
222
  updatedSinceDate(entity, sinceDate) {
@@ -287,7 +287,7 @@ class BaseProcessPublishAssortment {
287
287
  }
288
288
  }
289
289
  }
290
- dateArray.sort((a, b) => { return Date.parse(a) - Date.parse(b); });
290
+ dateArray.sort((a, b) => Date.parse(a) - Date.parse(b));
291
291
  const arrIndex = (dateArray.length - pastPublishCount > 0) ? dateArray.length - pastPublishCount : 0;
292
292
  sinceDate = dateArray[arrIndex];
293
293
  return sinceDate;
@@ -308,9 +308,9 @@ class BaseProcessPublishAssortment {
308
308
  return await sdk_1.Request.request(resourceString, {
309
309
  method: 'GET',
310
310
  Headers: {
311
- Accept: 'application/json',
312
- 'Content-Type': 'application/json'
313
- }
311
+ "Accept": 'application/json',
312
+ 'Content-Type': 'application/json',
313
+ },
314
314
  });
315
315
  }
316
316
  async downloadHydratedChangeDetail(assortmentPublishChange) {
@@ -358,7 +358,7 @@ class BaseProcessPublishAssortment {
358
358
  console.info('sinceDateMilliseconds: ' + sinceDateMilliseconds);
359
359
  console.info('apcDateMilliseconds: ' + previousApcDate);
360
360
  }
361
- //if only 1 apc, no processing needed
361
+ // if only 1 apc, no processing needed
362
362
  if (sinceDateMilliseconds !== previousApcDate) {
363
363
  console.info('sinceDateMilliseconds !== apcDateMilliseconds');
364
364
  const currentAssortmentItemIds = this.getBaselineItemIds(assortmentBaseline);
@@ -444,7 +444,7 @@ class BaseProcessPublishAssortment {
444
444
  previousBaseline = await this.downloadAssortmentBaseline(previousApc);
445
445
  }
446
446
  const deleteIds = apc?.detail?.deletes.map(dItem => dItem?.id);
447
- //building deletes based on previous baseline; because some APCs don't have delete data
447
+ // building deletes based on previous baseline; because some APCs don't have delete data
448
448
  const deleteArray = previousBaseline?.assortmentItems.filter(aItem => deleteIds.includes(aItem?.itemId));
449
449
  console.info('deleteArray.length: ' + deleteArray.length);
450
450
  return deleteArray;
@@ -495,13 +495,18 @@ class BaseProcessPublishAssortment {
495
495
  const itemFamilyId = item?.itemFamilyId;
496
496
  if (meetsCriteria) {
497
497
  releasedForDevelopmentItemIds.push(itemId);
498
- if (!itemFamilySet.has(itemFamilyId) && itemId !== itemFamilyId) {
499
- const familyItem = item?.itemFamily;
500
- const familyItemMeetsCriteria = this.meetsCriteria({ item: familyItem });
501
- if (familyItemMeetsCriteria) {
498
+ }
499
+ // ---- NEW (Step 5): family resolution moved OUT of the meetsCriteria block.
500
+ // A family whose options are ALL pre-development has no other route into this
501
+ // list when it has no assortment-item row of its own. ----
502
+ if (!itemFamilySet.has(itemFamilyId) && itemId !== itemFamilyId) {
503
+ const familyItem = item?.itemFamily;
504
+ if (familyItem) {
505
+ if (this.meetsCriteria({ item: familyItem })
506
+ && !releasedForDevelopmentItemIds.includes(itemFamilyId)) {
502
507
  releasedForDevelopmentItemIds.push(itemFamilyId);
503
508
  }
504
- itemFamilySet.add(itemFamilyId);
509
+ itemFamilySet.add(itemFamilyId); // only mark resolved when we had data to judge
505
510
  }
506
511
  }
507
512
  }
@@ -539,7 +544,7 @@ class BaseProcessPublishAssortment {
539
544
  console.info('processPublish-start');
540
545
  const event = {
541
546
  assortmentId: pcd.assortmentId,
542
- assortmentPublishChangeId: pcd.assortmentPublishChangeId
547
+ assortmentPublishChangeId: pcd.assortmentPublishChangeId,
543
548
  };
544
549
  const assortmentItemFullChangeMap = this.getFullChangeAssortmentMap(fullChange);
545
550
  const assortmentItemDeleteMap = this.getDeleteChangesAssortmentMap(deleteChanges);
@@ -554,7 +559,7 @@ class BaseProcessPublishAssortment {
554
559
  console.log(message);
555
560
  return {
556
561
  results: { message, event },
557
- skip_await: true
562
+ skip_await: true,
558
563
  };
559
564
  }
560
565
  const results = await this.sendEvents(events);
@@ -599,7 +604,7 @@ class BaseProcessPublishAssortment {
599
604
  taskId: this.config.taskId,
600
605
  eventType,
601
606
  objectClass: 'LCSSeason',
602
- events
607
+ events,
603
608
  };
604
609
  const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
605
610
  const result = await this.sendAndRecordPublishEvent(flexPLMConnect, outboundPublishEvent);
@@ -645,13 +650,13 @@ class BaseProcessPublishAssortment {
645
650
  taskId: this.config.taskId,
646
651
  eventType,
647
652
  objectClass: 'LCSSeason',
648
- events
653
+ events,
649
654
  };
650
655
  await fileHandle.writeFile(JSON.stringify(outboundPublishEvent));
651
656
  results = {
652
657
  message: 'Successfully Saved File',
653
658
  outboundPublishEvent,
654
- fileLocation: dirName + fileName
659
+ fileLocation: dirName + fileName,
655
660
  };
656
661
  }
657
662
  catch (e) {
@@ -672,7 +677,7 @@ class BaseProcessPublishAssortment {
672
677
  objectClass: 'LCSSeason',
673
678
  eventsFileId: uploadFile.id,
674
679
  eventsDownloadLink: uploadFile.downloadUrl,
675
- eventsAdminDownloadLink: uploadFile.adminDownloadUrl
680
+ eventsAdminDownloadLink: uploadFile.adminDownloadUrl,
676
681
  };
677
682
  if (sendMode === 'vibeiqfile') {
678
683
  const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
@@ -686,7 +691,7 @@ class BaseProcessPublishAssortment {
686
691
  await this.sendPublishPayloadEvent(outboundPublishEvent);
687
692
  return {
688
693
  message: 'Successfully Uploaded File.',
689
- outboundPublishEvent
694
+ outboundPublishEvent,
690
695
  };
691
696
  }
692
697
  }
@@ -702,11 +707,11 @@ class BaseProcessPublishAssortment {
702
707
  originSystemType: 'VibeIQ',
703
708
  objectClass: 'AssortmentPublishedToFlexPLM',
704
709
  outboundPublishEvent,
705
- initialEvent
710
+ initialEvent,
706
711
  };
707
712
  await new sdk_1.Entities().create({
708
713
  entityName: 'external-event',
709
- object: eventBody
714
+ object: eventBody,
710
715
  });
711
716
  }
712
717
  getCurrentDateString() {
@@ -730,7 +735,7 @@ class BaseProcessPublishAssortment {
730
735
  for (const [itemId, aItem] of assortmentItemFullChangeMap) {
731
736
  const projectItem = aItem?.projectItem;
732
737
  const itemFamilyId = aItem?.item?.itemFamilyId;
733
- if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId) || !pcd.releasedForDevelopmentItemIds.includes(itemId)) {
738
+ if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId)) {
734
739
  continue;
735
740
  }
736
741
  const ifc = itemFamilyChanges.get(itemFamilyId) || new item_family_changes_1.ItemFamilyChanges(itemFamilyId, pcd.sinceDate);
@@ -742,6 +747,20 @@ class BaseProcessPublishAssortment {
742
747
  if (pcd.itemToFederatedIdMapping.has(itemId)) {
743
748
  ifc.itemToFederatedIdMapping.set(itemId, pcd.itemToFederatedIdMapping.get(itemId));
744
749
  }
750
+ // ---- NEW Step to enable ItemFamily publish ET-243: option is pre-development. It must not produce an
751
+ // LCSSKUSeasonLink, but a change to it still makes the family publishable. ----
752
+ if (!pcd.releasedForDevelopmentItemIds.includes(itemId)) {
753
+ const changedInWindow = addIds.includes(itemId)
754
+ || deleteIds.includes(itemId)
755
+ || updateIds.includes(itemId)
756
+ || this.updatedSinceDate(projectItem, pcd.sinceDate)
757
+ || this.updatedSinceDate(aItem?.item, pcd.sinceDate);
758
+ if (changedInWindow) {
759
+ ifc.colorSuppressed.push(itemId);
760
+ }
761
+ continue;
762
+ }
763
+ // NEW Step - ends
745
764
  if (itemId === itemFamilyId) {
746
765
  if (addIds.includes(itemId))
747
766
  ifc.familyAdd = true;
@@ -757,6 +776,12 @@ class BaseProcessPublishAssortment {
757
776
  ifc.familyUpdate = true;
758
777
  }
759
778
  }
779
+ // NEW Step to enable ItemFamily publish ET-243: item-level edits (incl. lifecycleStage) must publish
780
+ // the family. Guarded so the "first option added" flow stays suppressed. ----
781
+ if (!ifc.familyItemRemoved && this.updatedSinceDate(aItem?.item, pcd.sinceDate)) {
782
+ ifc.familyUpdate = true;
783
+ }
784
+ // NEW Step - ends
760
785
  if (projectItem && !projectItem?.roles && aItem?.item?.roles) {
761
786
  projectItem.roles = aItem?.item?.roles;
762
787
  }
@@ -813,7 +838,41 @@ class BaseProcessPublishAssortment {
813
838
  else {
814
839
  ifc.colorDeletes.push(itemId);
815
840
  }
816
- } //End deletes for loop
841
+ } // 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
+ }
817
876
  if (app_framework_1.Logger.isDebugOn()) {
818
877
  console.debug('returning size: ' + itemFamilyChanges.size);
819
878
  for (const [key, value] of itemFamilyChanges) {
@@ -855,13 +914,17 @@ class BaseProcessPublishAssortment {
855
914
  const LCSSeason = Object.assign({}, seasonFed);
856
915
  const assortment = await this.getAssortment(assortmentId);
857
916
  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
858
918
  const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
919
+ || itemFamilyChanges.familyItemRemoved
859
920
  || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
921
+ || itemFamilyChanges.colorSuppressed.length > 0
860
922
  || (projectItem && this.updatedSinceDate(projectItem, itemFamilyChanges.sinceDate));
861
- //familyItemRemoved is used when adding the first option to an assortment
862
- //and will have updates for the family item.
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.
863
926
  if (familyAssortmentItem) {
864
- //Product-season add
927
+ // Product-season add
865
928
  const entityReference = itemFamilyChanges.itemFamilyId;
866
929
  const prodEntityData = (itemFamilyChanges.assortmentItemFullChangeMap.has(entityReference))
867
930
  ? itemFamilyChanges.assortmentItemFullChangeMap.get(entityReference)?.item
@@ -877,7 +940,7 @@ class BaseProcessPublishAssortment {
877
940
  entityReference: 'item:' + entityReference,
878
941
  LCSSeason,
879
942
  LCSProduct,
880
- data: seasonalData
943
+ data: seasonalData,
881
944
  };
882
945
  if ('ASSORTMENT' === projectItem.addedFromSource && projectItem.addedFromAssortment) {
883
946
  const carriedFromSeason = await this.getCarriedFromSeason(projectItem);
@@ -887,9 +950,9 @@ class BaseProcessPublishAssortment {
887
950
  }
888
951
  events.push(psUpsert);
889
952
  }
890
- //colorway-season adds
891
- //colorAdds
892
- //colorUpdates
953
+ // colorway-season adds
954
+ // colorAdds
955
+ // colorUpdates
893
956
  const colorwayChanges = [];
894
957
  colorwayChanges.push(...itemFamilyChanges.colorAdds);
895
958
  colorwayChanges.push(...itemFamilyChanges.colorUpdates);
@@ -912,7 +975,7 @@ class BaseProcessPublishAssortment {
912
975
  entityReference: 'item:' + entityReference,
913
976
  LCSSeason,
914
977
  LCSSKU,
915
- data: seasonalData
978
+ data: seasonalData,
916
979
  };
917
980
  if ('ASSORTMENT' === projectItem.addedFromSource && projectItem.addedFromAssortment) {
918
981
  const carriedFromSeason = await this.getCarriedFromSeason(projectItem);
@@ -928,10 +991,10 @@ class BaseProcessPublishAssortment {
928
991
  let projectItem = {};
929
992
  if (itemFamilyChanges?.assortmentItemFullChangeMap.has(id)) {
930
993
  const aItem = itemFamilyChanges?.assortmentItemFullChangeMap.get(id);
931
- projectItem = aItem?.projectItem;
994
+ projectItem = aItem?.projectItem ?? {};
932
995
  }
933
996
  /////////////////////////////////////////////////////////////////////////////
934
- //Get from option assortmentItem because family not in assortmentItemFullChangeMap:start
997
+ // Get from option assortmentItem because family not in assortmentItemFullChangeMap:start
935
998
  /////////////////////////////////////////////////////////////////////////////
936
999
  if (id === itemFamilyChanges.itemFamilyId && Object.keys(projectItem).length == 0) {
937
1000
  for (const asstItem of itemFamilyChanges.assortmentItemFullChangeMap.values()) {
@@ -942,7 +1005,7 @@ class BaseProcessPublishAssortment {
942
1005
  }
943
1006
  }
944
1007
  /////////////////////////////////////////////////////////////////////////////
945
- //Get from option assortmentItem because family not in assortmentItemFullChangeMap:end
1008
+ // Get from option assortmentItem because family not in assortmentItemFullChangeMap:end
946
1009
  /////////////////////////////////////////////////////////////////////////////
947
1010
  return projectItem;
948
1011
  }
@@ -992,7 +1055,7 @@ class BaseProcessPublishAssortment {
992
1055
  if (this.cache.carriedFromSeason[addedFromAssortment]) {
993
1056
  return this.cache.carriedFromSeason[addedFromAssortment];
994
1057
  }
995
- let seasonFederation = undefined;
1058
+ let seasonFederation;
996
1059
  const assortmentEntity = await this.getAssormentEntityFromId(addedFromAssortment);
997
1060
  if (assortmentEntity) {
998
1061
  seasonFederation = await this.getSeasonFederationFromAssortment(assortmentEntity);
@@ -1005,7 +1068,7 @@ class BaseProcessPublishAssortment {
1005
1068
  try {
1006
1069
  assortment = await new sdk_1.Entities().get({
1007
1070
  entityName: 'assortment',
1008
- id: assortmentId
1071
+ id: assortmentId,
1009
1072
  });
1010
1073
  }
1011
1074
  catch (e) {
@@ -1019,7 +1082,7 @@ class BaseProcessPublishAssortment {
1019
1082
  let seasonObj = {
1020
1083
  entityReference: 'assortment:' + assortment.id,
1021
1084
  objectClass: 'LCSSeason',
1022
- flexPLMTypePath
1085
+ flexPLMTypePath,
1023
1086
  };
1024
1087
  try {
1025
1088
  const assortmentObj = await this.dc.getFlexPLMObjectData(assortment, [], true);