@contrail/flexplm 1.1.67-alpha.1 → 1.1.67-alpha.3

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.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,7 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
11
11
 
12
12
  ### Added
13
13
  - Added EventShortMessageStatus status values for adding items to projects functionality.
14
+ - Added `TypeConversionUtils.isOutboundCreatableFromEntity` method to determine if VibeIQ entities are creatable in FlexPLM (defaults to true).
14
15
 
15
16
  ### Changed
16
17
  - Added optional `context` parameter to `TypeConversionUtils.isInboundCreatableFromObject` method to allow passing context data to map file `isInboundCreatable` functions.
@@ -82,7 +82,7 @@ export interface ItemPayloadType extends EntityPayloadType {
82
82
  LCSProduct?: ProductFederation;
83
83
  }
84
84
  export interface SeasonalPayload extends EntityPayloadType {
85
- eventType: 'UPSERT_ON_SEASON' | 'REMOVE_FROM_SEASON';
85
+ eventType: 'UPSERT_ON_SEASON' | 'UPDATE_ON_SEASON' | 'REMOVE_FROM_SEASON';
86
86
  objectClass: 'LCSProductSeasonLink' | 'LCSSKUSeasonLink';
87
87
  LCSSeason: SeasonFederation;
88
88
  LCSProduct?: ProductFederation;
@@ -759,6 +759,7 @@ class BaseProcessPublishAssortment {
759
759
  console.info('getEventsForItemFamilyChanges()');
760
760
  const events = [];
761
761
  const LCSSeason = Object.assign({}, seasonFed);
762
+ const assortment = await this.getAssortment(assortmentId);
762
763
  const projectItem = this.getProjectItem(itemFamilyChanges, itemFamilyChanges.itemFamilyId);
763
764
  const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
764
765
  || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
@@ -771,8 +772,10 @@ class BaseProcessPublishAssortment {
771
772
  let seasonalData = await this.getSeasonalData(projectItem);
772
773
  seasonalData = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSProductSeasonLink', 'vibe2flex');
773
774
  const LCSProduct = await this.getProductFederation(entityReference, itemToFederatedIdMapping, prodEntityData);
775
+ const isCreatable = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item: prodEntityData, assortment });
776
+ const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
774
777
  const psUpsert = {
775
- eventType: 'UPSERT_ON_SEASON',
778
+ eventType,
776
779
  objectClass: 'LCSProductSeasonLink',
777
780
  entityReference: 'item:' + entityReference,
778
781
  LCSSeason,
@@ -801,8 +804,10 @@ class BaseProcessPublishAssortment {
801
804
  let seasonalData = await this.getSeasonalData(projectItem);
802
805
  seasonalData = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSSKUSeasonLink', 'vibe2flex');
803
806
  const LCSSKU = await this.getSKUFederation(entityReference, itemToFederatedIdMapping, item);
807
+ const isCreatable = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item, assortment });
808
+ const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
804
809
  const csUpsert = {
805
- eventType: 'UPSERT_ON_SEASON',
810
+ eventType,
806
811
  objectClass: 'LCSSKUSeasonLink',
807
812
  entityReference: 'item:' + entityReference,
808
813
  LCSSeason,
@@ -1467,3 +1467,204 @@ describe('meetsCriteria', () => {
1467
1467
  expect(results).toBeTruthy();
1468
1468
  });
1469
1469
  });
1470
+ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
1471
+ const config = {
1472
+ identifierAtts: {
1473
+ LCSSeason: ['flexPLMSeasonName']
1474
+ },
1475
+ itemPreDevelopmentLifecycleStages: ['concept']
1476
+ };
1477
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
1478
+ const dc = new data_converter_1.DataConverter(config, mapFileUtil);
1479
+ afterEach(() => {
1480
+ jest.restoreAllMocks();
1481
+ });
1482
+ it('should use UPSERT_ON_SEASON for LCSProductSeasonLink when isOutboundCreatableFromEntity returns true', async () => {
1483
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1484
+ const itemFamilyId = 'family123';
1485
+ const prodEntityData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1486
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1487
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1488
+ const seasonFed = {
1489
+ entityReference: 'assortment:assort123',
1490
+ objectClass: 'LCSSeason',
1491
+ flexPLMSeasonName: 'Fall 2023'
1492
+ };
1493
+ const itemFamilyChanges = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1494
+ itemFamilyChanges.familyAdd = true;
1495
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1496
+ item: prodEntityData,
1497
+ projectItem
1498
+ });
1499
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1500
+ jest.spyOn(bppa, 'getProjectItem').mockReturnValue(projectItem);
1501
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1502
+ jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1503
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1504
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1505
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'isOutboundCreatableFromEntity').mockResolvedValue(true);
1506
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1507
+ expect(events).toHaveLength(1);
1508
+ expect(events[0].eventType).toBe('UPSERT_ON_SEASON');
1509
+ expect(events[0].objectClass).toBe('LCSProductSeasonLink');
1510
+ expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(undefined, mapFileUtil, projectItem, { item: prodEntityData, assortment });
1511
+ });
1512
+ it('should use UPDATE_ON_SEASON for LCSProductSeasonLink when isOutboundCreatableFromEntity returns false', async () => {
1513
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1514
+ const itemFamilyId = 'family123';
1515
+ const prodEntityData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1516
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1517
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1518
+ const seasonFed = {
1519
+ entityReference: 'assortment:assort123',
1520
+ objectClass: 'LCSSeason',
1521
+ flexPLMSeasonName: 'Fall 2023'
1522
+ };
1523
+ const itemFamilyChanges = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1524
+ itemFamilyChanges.familyUpdate = true;
1525
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1526
+ item: prodEntityData,
1527
+ projectItem
1528
+ });
1529
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1530
+ jest.spyOn(bppa, 'getProjectItem').mockReturnValue(projectItem);
1531
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1532
+ jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1533
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1534
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1535
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'isOutboundCreatableFromEntity').mockResolvedValue(false);
1536
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1537
+ expect(events).toHaveLength(1);
1538
+ expect(events[0].eventType).toBe('UPDATE_ON_SEASON');
1539
+ expect(events[0].objectClass).toBe('LCSProductSeasonLink');
1540
+ expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(undefined, mapFileUtil, projectItem, { item: prodEntityData, assortment });
1541
+ });
1542
+ it('should use UPSERT_ON_SEASON for LCSSKUSeasonLink when isOutboundCreatableFromEntity returns true', async () => {
1543
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1544
+ const itemFamilyId = 'family123';
1545
+ const colorId = 'color456';
1546
+ const familyItemData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1547
+ const itemData = { id: colorId, identifier: 'COLOR-456', itemNumber: 'COLOR-456' };
1548
+ const familyProjectItem = { id: 'proj0', updatedOn: '2023-01-14T00:00:00.000Z' };
1549
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1550
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1551
+ const seasonFed = {
1552
+ entityReference: 'assortment:assort123',
1553
+ objectClass: 'LCSSeason',
1554
+ flexPLMSeasonName: 'Fall 2023'
1555
+ };
1556
+ const itemFamilyChanges = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1557
+ itemFamilyChanges.colorAdds.push(colorId);
1558
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1559
+ item: familyItemData,
1560
+ projectItem: familyProjectItem
1561
+ });
1562
+ itemFamilyChanges.assortmentItemFullChangeMap.set(colorId, {
1563
+ item: itemData,
1564
+ projectItem
1565
+ });
1566
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1567
+ jest.spyOn(bppa, 'getProjectItem')
1568
+ .mockReturnValueOnce(familyProjectItem)
1569
+ .mockReturnValueOnce(projectItem);
1570
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1571
+ jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1572
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1573
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1574
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'isOutboundCreatableFromEntity')
1575
+ .mockResolvedValueOnce(true)
1576
+ .mockResolvedValueOnce(true);
1577
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1578
+ expect(events).toHaveLength(2);
1579
+ const skuEvent = events.find(e => e.objectClass === 'LCSSKUSeasonLink');
1580
+ expect(skuEvent.eventType).toBe('UPSERT_ON_SEASON');
1581
+ expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(undefined, mapFileUtil, projectItem, { item: itemData, assortment });
1582
+ });
1583
+ it('should use UPDATE_ON_SEASON for LCSSKUSeasonLink when isOutboundCreatableFromEntity returns false', async () => {
1584
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1585
+ const itemFamilyId = 'family123';
1586
+ const colorId = 'color456';
1587
+ const familyItemData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1588
+ const itemData = { id: colorId, identifier: 'COLOR-456', itemNumber: 'COLOR-456' };
1589
+ const familyProjectItem = { id: 'proj0', updatedOn: '2023-01-14T00:00:00.000Z' };
1590
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1591
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1592
+ const seasonFed = {
1593
+ entityReference: 'assortment:assort123',
1594
+ objectClass: 'LCSSeason',
1595
+ flexPLMSeasonName: 'Fall 2023'
1596
+ };
1597
+ const itemFamilyChanges = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1598
+ itemFamilyChanges.colorUpdates.push(colorId);
1599
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1600
+ item: familyItemData,
1601
+ projectItem: familyProjectItem
1602
+ });
1603
+ itemFamilyChanges.assortmentItemFullChangeMap.set(colorId, {
1604
+ item: itemData,
1605
+ projectItem
1606
+ });
1607
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1608
+ jest.spyOn(bppa, 'getProjectItem')
1609
+ .mockReturnValueOnce(familyProjectItem)
1610
+ .mockReturnValueOnce(projectItem);
1611
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1612
+ jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1613
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1614
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1615
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'isOutboundCreatableFromEntity')
1616
+ .mockResolvedValueOnce(false)
1617
+ .mockResolvedValueOnce(false);
1618
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1619
+ expect(events).toHaveLength(2);
1620
+ const skuEvent = events.find(e => e.objectClass === 'LCSSKUSeasonLink');
1621
+ expect(skuEvent.eventType).toBe('UPDATE_ON_SEASON');
1622
+ expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(undefined, mapFileUtil, projectItem, { item: itemData, assortment });
1623
+ });
1624
+ it('should create both product and SKU events with correct eventTypes based on isOutboundCreatableFromEntity', async () => {
1625
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1626
+ const itemFamilyId = 'family123';
1627
+ const colorId = 'color456';
1628
+ const prodEntityData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1629
+ const itemData = { id: colorId, identifier: 'COLOR-456', itemNumber: 'COLOR-456' };
1630
+ const familyProjectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1631
+ const colorProjectItem = { id: 'proj2', updatedOn: '2023-01-15T00:00:00.000Z' };
1632
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1633
+ const seasonFed = {
1634
+ entityReference: 'assortment:assort123',
1635
+ objectClass: 'LCSSeason',
1636
+ flexPLMSeasonName: 'Fall 2023'
1637
+ };
1638
+ const itemFamilyChanges = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1639
+ itemFamilyChanges.familyAdd = true;
1640
+ itemFamilyChanges.colorAdds.push(colorId);
1641
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1642
+ item: prodEntityData,
1643
+ projectItem: familyProjectItem
1644
+ });
1645
+ itemFamilyChanges.assortmentItemFullChangeMap.set(colorId, {
1646
+ item: itemData,
1647
+ projectItem: colorProjectItem
1648
+ });
1649
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1650
+ jest.spyOn(bppa, 'getProjectItem')
1651
+ .mockReturnValueOnce(familyProjectItem)
1652
+ .mockReturnValueOnce(colorProjectItem);
1653
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1654
+ jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1655
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1656
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1657
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'isOutboundCreatableFromEntity')
1658
+ .mockResolvedValueOnce(true)
1659
+ .mockResolvedValueOnce(false);
1660
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1661
+ expect(events).toHaveLength(2);
1662
+ const productEvent = events.find(e => e.objectClass === 'LCSProductSeasonLink');
1663
+ const skuEvent = events.find(e => e.objectClass === 'LCSSKUSeasonLink');
1664
+ expect(productEvent.eventType).toBe('UPSERT_ON_SEASON');
1665
+ expect(skuEvent.eventType).toBe('UPDATE_ON_SEASON');
1666
+ expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledTimes(2);
1667
+ expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenNthCalledWith(1, undefined, mapFileUtil, familyProjectItem, { item: prodEntityData, assortment });
1668
+ expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenNthCalledWith(2, undefined, mapFileUtil, colorProjectItem, { item: itemData, assortment });
1669
+ });
1670
+ });
@@ -3,6 +3,7 @@ export declare enum EventShortMessageStatus {
3
3
  FAILURE = "Failure",
4
4
  CREATED = "Created",
5
5
  ITEM_FAMILY_ID_MISSING = "Item_family_id_missing",
6
+ ITEM_FAMILY_NOT_FOUND = "Item_family_not_found",
6
7
  PROJECT_ITEM_NOT_FOUND = "Project_item_not_found",
7
8
  MISSING_IDENTIFIER_PROPERTIES = "Missing_identifier_properties",
8
9
  MISSING_INPUT = "Missing_input",
@@ -7,6 +7,7 @@ var EventShortMessageStatus;
7
7
  EventShortMessageStatus["FAILURE"] = "Failure";
8
8
  EventShortMessageStatus["CREATED"] = "Created";
9
9
  EventShortMessageStatus["ITEM_FAMILY_ID_MISSING"] = "Item_family_id_missing";
10
+ EventShortMessageStatus["ITEM_FAMILY_NOT_FOUND"] = "Item_family_not_found";
10
11
  EventShortMessageStatus["PROJECT_ITEM_NOT_FOUND"] = "Project_item_not_found";
11
12
  EventShortMessageStatus["MISSING_IDENTIFIER_PROPERTIES"] = "Missing_identifier_properties";
12
13
  EventShortMessageStatus["MISSING_INPUT"] = "Missing_input";
@@ -134,6 +134,9 @@ exports.mapping = {
134
134
  }
135
135
  },
136
136
  packaging: {
137
+ isOutboundCreatable: (entity, context) => {
138
+ return false;
139
+ },
137
140
  vibe2flex: {
138
141
  transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
139
142
  rekey: {
@@ -154,7 +157,18 @@ exports.mapping = {
154
157
  }
155
158
  },
156
159
  prefix: {
157
- isInboundCreatable: () => true,
160
+ isInboundCreatable: (object, context) => {
161
+ if (context && context.skipPrefix) {
162
+ return false;
163
+ }
164
+ return true;
165
+ },
166
+ isOutboundCreatable: (entity, context) => {
167
+ if (context && context.skipPrefix) {
168
+ return false;
169
+ }
170
+ return true;
171
+ },
158
172
  vibe2flex: {
159
173
  transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
160
174
  rekey: {
@@ -15,6 +15,7 @@ export declare class TypeConversionUtils {
15
15
  static getIdentifierPropertiesFromObject(fileId: any, mapFileUtil: MapFileUtil, object: any): Promise<string[]>;
16
16
  static getInformationalPropertiesFromObject(fileId: any, mapFileUtil: MapFileUtil, object: any): Promise<string[]>;
17
17
  static getMapKeyFromObject(fileId: any, mapFileUtil: MapFileUtil, object: any, direction: string): Promise<string>;
18
- static isInboundCreatableFromObject(fileId: string, mapFileUtil: MapFileUtil, object: any): Promise<boolean>;
18
+ static isInboundCreatableFromObject(fileId: string, mapFileUtil: MapFileUtil, object: any, context?: any): Promise<boolean>;
19
+ static isOutboundCreatableFromEntity(fileId: string, mapFileUtil: MapFileUtil, entity: any, context?: any): Promise<boolean>;
19
20
  static getObjectType(object: any): any;
20
21
  }
@@ -180,17 +180,28 @@ class TypeConversionUtils {
180
180
  return type;
181
181
  }
182
182
  }
183
- static async isInboundCreatableFromObject(fileId, mapFileUtil, object) {
183
+ static async isInboundCreatableFromObject(fileId, mapFileUtil, object, context) {
184
184
  let isInboundCreatable = false;
185
185
  if (fileId) {
186
186
  const mapKey = await this.getMapKeyFromObject(fileId, mapFileUtil, object, TypeConversionUtils.FLEX2VIBE_DIRECTION);
187
187
  const mapData = await map_utils_1.MapUtil.getFullMapSection(fileId, mapFileUtil, mapKey);
188
188
  if (mapData && mapData['isInboundCreatable']) {
189
- isInboundCreatable = await mapData['isInboundCreatable'](object);
189
+ isInboundCreatable = await mapData['isInboundCreatable'](object, context);
190
190
  }
191
191
  }
192
192
  return isInboundCreatable;
193
193
  }
194
+ static async isOutboundCreatableFromEntity(fileId, mapFileUtil, entity, context) {
195
+ let isOutboundCreatable = true;
196
+ if (fileId) {
197
+ const mapKey = await this.getMapKey(fileId, mapFileUtil, entity, TypeConversionUtils.VIBE2FLEX_DIRECTION);
198
+ const mapData = await map_utils_1.MapUtil.getFullMapSection(fileId, mapFileUtil, mapKey);
199
+ if (mapData && mapData['isOutboundCreatable']) {
200
+ isOutboundCreatable = await mapData['isOutboundCreatable'](entity, context);
201
+ }
202
+ }
203
+ return isOutboundCreatable;
204
+ }
194
205
  static getObjectType(object) {
195
206
  let objectType = object['flexPLMObjectClass'];
196
207
  return objectType;
@@ -580,5 +580,129 @@ describe('conversion-utils', () => {
580
580
  spy.mockRestore();
581
581
  }
582
582
  });
583
+ it('should pass context to isInboundCreatable function', async () => {
584
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
585
+ .mockImplementation(async () => {
586
+ return mapping;
587
+ });
588
+ const object = {
589
+ flexPLMObjectClass: 'LCSRevisableEntity',
590
+ flexPLMTypePath: 'Revisable Entity\\prefix'
591
+ };
592
+ const context = { skipPrefix: true };
593
+ try {
594
+ const results = await type_conversion_utils_1.TypeConversionUtils.isInboundCreatableFromObject(TRANSFORM_MAP_FILE, mapFileUtil, object, context);
595
+ expect(results).toBeFalsy();
596
+ }
597
+ finally {
598
+ spy.mockRestore();
599
+ }
600
+ });
601
+ it('should work without context parameter', async () => {
602
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
603
+ .mockImplementation(async () => {
604
+ return mapping;
605
+ });
606
+ const object = {
607
+ flexPLMObjectClass: 'LCSRevisableEntity',
608
+ flexPLMTypePath: 'Revisable Entity\\prefix'
609
+ };
610
+ try {
611
+ const results = await type_conversion_utils_1.TypeConversionUtils.isInboundCreatableFromObject(TRANSFORM_MAP_FILE, mapFileUtil, object);
612
+ expect(results).toBeTruthy();
613
+ }
614
+ finally {
615
+ spy.mockRestore();
616
+ }
617
+ });
618
+ });
619
+ describe('isOutboundCreatableFromEntity', () => {
620
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
621
+ it('should return false for custom-entity:pack', async () => {
622
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
623
+ .mockImplementation(async () => {
624
+ return mapping;
625
+ });
626
+ const entity = {
627
+ entityType: 'custom-entity',
628
+ typePath: 'custom-entity:pack'
629
+ };
630
+ try {
631
+ const results = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
632
+ expect(results).toBeFalsy();
633
+ }
634
+ finally {
635
+ spy.mockRestore();
636
+ }
637
+ });
638
+ it('should return true for custom-entity:prefix', async () => {
639
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
640
+ .mockImplementation(async () => {
641
+ return mapping;
642
+ });
643
+ const entity = {
644
+ entityType: 'custom-entity',
645
+ typePath: 'custom-entity:prefix'
646
+ };
647
+ try {
648
+ const results = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
649
+ expect(results).toBeTruthy();
650
+ }
651
+ finally {
652
+ spy.mockRestore();
653
+ }
654
+ });
655
+ it('should pass context to isOutboundCreatable function', async () => {
656
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
657
+ .mockImplementation(async () => {
658
+ return mapping;
659
+ });
660
+ const entity = {
661
+ entityType: 'custom-entity',
662
+ typePath: 'custom-entity:prefix'
663
+ };
664
+ const context = { skipPrefix: true };
665
+ try {
666
+ const results = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity, context);
667
+ expect(results).toBeFalsy();
668
+ }
669
+ finally {
670
+ spy.mockRestore();
671
+ }
672
+ });
673
+ it('should work without context parameter', async () => {
674
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
675
+ .mockImplementation(async () => {
676
+ return mapping;
677
+ });
678
+ const entity = {
679
+ entityType: 'custom-entity',
680
+ typePath: 'custom-entity:prefix'
681
+ };
682
+ try {
683
+ const results = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
684
+ expect(results).toBeTruthy();
685
+ }
686
+ finally {
687
+ spy.mockRestore();
688
+ }
689
+ });
690
+ it('should default to true when no mapping exists', async () => {
691
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
692
+ .mockImplementation(async () => {
693
+ return mapping;
694
+ });
695
+ const entity = {
696
+ entityType: 'custom-entity',
697
+ typePath: 'custom-entity:catName'
698
+ };
699
+ try {
700
+ const results = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
701
+ expect(results).toBeTruthy();
702
+ }
703
+ finally {
704
+ spy.mockRestore();
705
+ }
706
+ });
583
707
  });
584
708
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.1.67-alpha.1",
3
+ "version": "1.1.67-alpha.3",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -93,7 +93,7 @@ export interface ItemPayloadType extends EntityPayloadType {
93
93
  ///////// Custom seasonalPayload: start
94
94
  /////////////////////////////////////////////////////////////////////////////
95
95
  export interface SeasonalPayload extends EntityPayloadType {
96
- eventType: 'UPSERT_ON_SEASON' | 'REMOVE_FROM_SEASON';
96
+ eventType: 'UPSERT_ON_SEASON' | 'UPDATE_ON_SEASON' | 'REMOVE_FROM_SEASON';
97
97
  objectClass: 'LCSProductSeasonLink' | 'LCSSKUSeasonLink';
98
98
  LCSSeason : SeasonFederation;
99
99
  LCSProduct? : ProductFederation;
@@ -1713,4 +1713,280 @@ describe('meetsCriteria', () =>{
1713
1713
  expect(results).toBeTruthy();
1714
1714
  });
1715
1715
 
1716
+ });
1717
+
1718
+ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
1719
+ const config = {
1720
+ identifierAtts: {
1721
+ LCSSeason: ['flexPLMSeasonName']
1722
+ },
1723
+ itemPreDevelopmentLifecycleStages: ['concept']
1724
+ } as unknown as FCConfig;
1725
+ const mapFileUtil = new MapFileUtil(new Entities());
1726
+ const dc = new DataConverter(config, mapFileUtil);
1727
+
1728
+ afterEach(() => {
1729
+ jest.restoreAllMocks();
1730
+ });
1731
+
1732
+ it('should use UPSERT_ON_SEASON for LCSProductSeasonLink when isOutboundCreatableFromEntity returns true', async () => {
1733
+ const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
1734
+
1735
+ const itemFamilyId = 'family123';
1736
+ const prodEntityData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1737
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1738
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1739
+ const seasonFed: SeasonFederation = {
1740
+ entityReference: 'assortment:assort123',
1741
+ objectClass: 'LCSSeason',
1742
+ flexPLMSeasonName: 'Fall 2023'
1743
+ };
1744
+
1745
+ const itemFamilyChanges = new ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1746
+ itemFamilyChanges.familyAdd = true;
1747
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1748
+ item: prodEntityData,
1749
+ projectItem
1750
+ });
1751
+
1752
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1753
+ jest.spyOn(bppa, 'getProjectItem').mockReturnValue(projectItem);
1754
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1755
+ jest.spyOn(MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1756
+ jest.spyOn(TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1757
+ jest.spyOn(TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1758
+ jest.spyOn(TypeConversionUtils, 'isOutboundCreatableFromEntity').mockResolvedValue(true);
1759
+
1760
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1761
+
1762
+ expect(events).toHaveLength(1);
1763
+ expect(events[0].eventType).toBe('UPSERT_ON_SEASON');
1764
+ expect(events[0].objectClass).toBe('LCSProductSeasonLink');
1765
+ expect(TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(
1766
+ undefined,
1767
+ mapFileUtil,
1768
+ projectItem,
1769
+ { item: prodEntityData, assortment }
1770
+ );
1771
+ });
1772
+
1773
+ it('should use UPDATE_ON_SEASON for LCSProductSeasonLink when isOutboundCreatableFromEntity returns false', async () => {
1774
+ const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
1775
+
1776
+ const itemFamilyId = 'family123';
1777
+ const prodEntityData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1778
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1779
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1780
+ const seasonFed: SeasonFederation = {
1781
+ entityReference: 'assortment:assort123',
1782
+ objectClass: 'LCSSeason',
1783
+ flexPLMSeasonName: 'Fall 2023'
1784
+ };
1785
+
1786
+ const itemFamilyChanges = new ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1787
+ itemFamilyChanges.familyUpdate = true;
1788
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1789
+ item: prodEntityData,
1790
+ projectItem
1791
+ });
1792
+
1793
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1794
+ jest.spyOn(bppa, 'getProjectItem').mockReturnValue(projectItem);
1795
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1796
+ jest.spyOn(MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1797
+ jest.spyOn(TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1798
+ jest.spyOn(TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1799
+ jest.spyOn(TypeConversionUtils, 'isOutboundCreatableFromEntity').mockResolvedValue(false);
1800
+
1801
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1802
+
1803
+ expect(events).toHaveLength(1);
1804
+ expect(events[0].eventType).toBe('UPDATE_ON_SEASON');
1805
+ expect(events[0].objectClass).toBe('LCSProductSeasonLink');
1806
+ expect(TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(
1807
+ undefined,
1808
+ mapFileUtil,
1809
+ projectItem,
1810
+ { item: prodEntityData, assortment }
1811
+ );
1812
+ });
1813
+
1814
+ it('should use UPSERT_ON_SEASON for LCSSKUSeasonLink when isOutboundCreatableFromEntity returns true', async () => {
1815
+ const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
1816
+
1817
+ const itemFamilyId = 'family123';
1818
+ const colorId = 'color456';
1819
+ const familyItemData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1820
+ const itemData = { id: colorId, identifier: 'COLOR-456', itemNumber: 'COLOR-456' };
1821
+ const familyProjectItem = { id: 'proj0', updatedOn: '2023-01-14T00:00:00.000Z' };
1822
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1823
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1824
+ const seasonFed: SeasonFederation = {
1825
+ entityReference: 'assortment:assort123',
1826
+ objectClass: 'LCSSeason',
1827
+ flexPLMSeasonName: 'Fall 2023'
1828
+ };
1829
+
1830
+ const itemFamilyChanges = new ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1831
+ itemFamilyChanges.colorAdds.push(colorId);
1832
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1833
+ item: familyItemData,
1834
+ projectItem: familyProjectItem
1835
+ });
1836
+ itemFamilyChanges.assortmentItemFullChangeMap.set(colorId, {
1837
+ item: itemData,
1838
+ projectItem
1839
+ });
1840
+
1841
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1842
+ jest.spyOn(bppa, 'getProjectItem')
1843
+ .mockReturnValueOnce(familyProjectItem)
1844
+ .mockReturnValueOnce(projectItem);
1845
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1846
+ jest.spyOn(MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1847
+ jest.spyOn(TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1848
+ jest.spyOn(TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1849
+ jest.spyOn(TypeConversionUtils, 'isOutboundCreatableFromEntity')
1850
+ .mockResolvedValueOnce(true) // for family
1851
+ .mockResolvedValueOnce(true); // for SKU
1852
+
1853
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1854
+
1855
+ // Should have both family and SKU events because familyAssortmentItem is true when colorAdds.length > 0
1856
+ expect(events).toHaveLength(2);
1857
+
1858
+ const skuEvent = events.find(e => e.objectClass === 'LCSSKUSeasonLink');
1859
+ expect(skuEvent.eventType).toBe('UPSERT_ON_SEASON');
1860
+ expect(TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(
1861
+ undefined,
1862
+ mapFileUtil,
1863
+ projectItem,
1864
+ { item: itemData, assortment }
1865
+ );
1866
+ });
1867
+
1868
+ it('should use UPDATE_ON_SEASON for LCSSKUSeasonLink when isOutboundCreatableFromEntity returns false', async () => {
1869
+ const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
1870
+
1871
+ const itemFamilyId = 'family123';
1872
+ const colorId = 'color456';
1873
+ const familyItemData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1874
+ const itemData = { id: colorId, identifier: 'COLOR-456', itemNumber: 'COLOR-456' };
1875
+ const familyProjectItem = { id: 'proj0', updatedOn: '2023-01-14T00:00:00.000Z' };
1876
+ const projectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1877
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1878
+ const seasonFed: SeasonFederation = {
1879
+ entityReference: 'assortment:assort123',
1880
+ objectClass: 'LCSSeason',
1881
+ flexPLMSeasonName: 'Fall 2023'
1882
+ };
1883
+
1884
+ const itemFamilyChanges = new ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1885
+ itemFamilyChanges.colorUpdates.push(colorId);
1886
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1887
+ item: familyItemData,
1888
+ projectItem: familyProjectItem
1889
+ });
1890
+ itemFamilyChanges.assortmentItemFullChangeMap.set(colorId, {
1891
+ item: itemData,
1892
+ projectItem
1893
+ });
1894
+
1895
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1896
+ jest.spyOn(bppa, 'getProjectItem')
1897
+ .mockReturnValueOnce(familyProjectItem)
1898
+ .mockReturnValueOnce(projectItem);
1899
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1900
+ jest.spyOn(MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1901
+ jest.spyOn(TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1902
+ jest.spyOn(TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1903
+ jest.spyOn(TypeConversionUtils, 'isOutboundCreatableFromEntity')
1904
+ .mockResolvedValueOnce(false) // for family
1905
+ .mockResolvedValueOnce(false); // for SKU
1906
+
1907
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1908
+
1909
+ // Should have both family and SKU events because familyAssortmentItem is true when colorUpdates.length > 0
1910
+ expect(events).toHaveLength(2);
1911
+
1912
+ const skuEvent = events.find(e => e.objectClass === 'LCSSKUSeasonLink');
1913
+ expect(skuEvent.eventType).toBe('UPDATE_ON_SEASON');
1914
+ expect(TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledWith(
1915
+ undefined,
1916
+ mapFileUtil,
1917
+ projectItem,
1918
+ { item: itemData, assortment }
1919
+ );
1920
+ });
1921
+
1922
+ it('should create both product and SKU events with correct eventTypes based on isOutboundCreatableFromEntity', async () => {
1923
+ const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
1924
+
1925
+ const itemFamilyId = 'family123';
1926
+ const colorId = 'color456';
1927
+ const prodEntityData = { id: itemFamilyId, identifier: 'FAM-123', itemNumber: 'FAM-123' };
1928
+ const itemData = { id: colorId, identifier: 'COLOR-456', itemNumber: 'COLOR-456' };
1929
+ const familyProjectItem = { id: 'proj1', updatedOn: '2023-01-15T00:00:00.000Z' };
1930
+ const colorProjectItem = { id: 'proj2', updatedOn: '2023-01-15T00:00:00.000Z' };
1931
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1932
+ const seasonFed: SeasonFederation = {
1933
+ entityReference: 'assortment:assort123',
1934
+ objectClass: 'LCSSeason',
1935
+ flexPLMSeasonName: 'Fall 2023'
1936
+ };
1937
+
1938
+ const itemFamilyChanges = new ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1939
+ itemFamilyChanges.familyAdd = true;
1940
+ itemFamilyChanges.colorAdds.push(colorId);
1941
+ itemFamilyChanges.assortmentItemFullChangeMap.set(itemFamilyId, {
1942
+ item: prodEntityData,
1943
+ projectItem: familyProjectItem
1944
+ });
1945
+ itemFamilyChanges.assortmentItemFullChangeMap.set(colorId, {
1946
+ item: itemData,
1947
+ projectItem: colorProjectItem
1948
+ });
1949
+
1950
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1951
+ jest.spyOn(bppa, 'getProjectItem')
1952
+ .mockReturnValueOnce(familyProjectItem)
1953
+ .mockReturnValueOnce(colorProjectItem);
1954
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1955
+ jest.spyOn(MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1956
+ jest.spyOn(TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1957
+ jest.spyOn(TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1958
+ // Product is creatable (true), SKU is not (false)
1959
+ jest.spyOn(TypeConversionUtils, 'isOutboundCreatableFromEntity')
1960
+ .mockResolvedValueOnce(true)
1961
+ .mockResolvedValueOnce(false);
1962
+
1963
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1964
+
1965
+ expect(events).toHaveLength(2);
1966
+
1967
+ const productEvent = events.find(e => e.objectClass === 'LCSProductSeasonLink');
1968
+ const skuEvent = events.find(e => e.objectClass === 'LCSSKUSeasonLink');
1969
+
1970
+ expect(productEvent.eventType).toBe('UPSERT_ON_SEASON');
1971
+ expect(skuEvent.eventType).toBe('UPDATE_ON_SEASON');
1972
+
1973
+ expect(TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenCalledTimes(2);
1974
+ // Verify product event called with assortment entity
1975
+ expect(TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenNthCalledWith(
1976
+ 1,
1977
+ undefined,
1978
+ mapFileUtil,
1979
+ familyProjectItem,
1980
+ { item: prodEntityData, assortment }
1981
+ );
1982
+ // Verify SKU event called with assortment entity
1983
+ expect(TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenNthCalledWith(
1984
+ 2,
1985
+ undefined,
1986
+ mapFileUtil,
1987
+ colorProjectItem,
1988
+ { item: itemData, assortment }
1989
+ );
1990
+ });
1991
+
1716
1992
  });
@@ -906,7 +906,7 @@ export class BaseProcessPublishAssortment {
906
906
  console.info('getEventsForItemFamilyChanges()');
907
907
  const events: SeasonalPayload[] = [];
908
908
  const LCSSeason = Object.assign({}, seasonFed);
909
-
909
+ const assortment = await this.getAssortment(assortmentId);
910
910
  const projectItem = this.getProjectItem(itemFamilyChanges, itemFamilyChanges.itemFamilyId);
911
911
  const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
912
912
  || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
@@ -925,8 +925,12 @@ export class BaseProcessPublishAssortment {
925
925
  seasonalData = await MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSProductSeasonLink', 'vibe2flex');
926
926
 
927
927
  const LCSProduct: ProductFederation = await this.getProductFederation(entityReference, itemToFederatedIdMapping, prodEntityData);
928
+
929
+ const isCreatable = await TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item: prodEntityData, assortment });
930
+ const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
931
+
928
932
  const psUpsert: SeasonalPayload = {
929
- eventType: 'UPSERT_ON_SEASON',
933
+ eventType,
930
934
  objectClass: 'LCSProductSeasonLink',
931
935
  entityReference: 'item:' + entityReference,
932
936
  LCSSeason,
@@ -962,8 +966,12 @@ export class BaseProcessPublishAssortment {
962
966
  seasonalData = await MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSSKUSeasonLink', 'vibe2flex');
963
967
 
964
968
  const LCSSKU: SkuFederation = await this.getSKUFederation(entityReference, itemToFederatedIdMapping, item);
969
+
970
+ const isCreatable = await TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item, assortment });
971
+ const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
972
+
965
973
  const csUpsert: SeasonalPayload = {
966
- eventType: 'UPSERT_ON_SEASON',
974
+ eventType,
967
975
  objectClass: 'LCSSKUSeasonLink',
968
976
  entityReference: 'item:' + entityReference,
969
977
  LCSSeason,
@@ -139,6 +139,9 @@ exports.mapping = {
139
139
  },
140
140
 
141
141
  packaging: {
142
+ isOutboundCreatable: (entity, context) => {
143
+ return false;
144
+ },
142
145
  vibe2flex: {
143
146
  transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
144
147
  rekey: {
@@ -166,6 +169,12 @@ exports.mapping = {
166
169
  }
167
170
  return true;
168
171
  },
172
+ isOutboundCreatable: (entity, context) => {
173
+ if (context && context.skipPrefix) {
174
+ return false;
175
+ }
176
+ return true;
177
+ },
169
178
  vibe2flex: {
170
179
  transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
171
180
  rekey: {
@@ -680,4 +680,105 @@ describe('conversion-utils', () => {
680
680
  });
681
681
 
682
682
  });
683
+
684
+ describe('isOutboundCreatableFromEntity', () =>{
685
+ const mapFileUtil = new MapFileUtil(new Entities());
686
+
687
+ it('should return false for custom-entity:pack', async () =>{
688
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
689
+ .mockImplementation(async () =>{
690
+ return mapping;
691
+ });
692
+ const entity = {
693
+ entityType: 'custom-entity',
694
+ typePath: 'custom-entity:pack'
695
+ }
696
+
697
+ try{
698
+ const results = await TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
699
+ expect(results).toBeFalsy();
700
+
701
+ } finally {
702
+ spy.mockRestore();
703
+ }
704
+ });
705
+
706
+ it('should return true for custom-entity:prefix', async () =>{
707
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
708
+ .mockImplementation(async () =>{
709
+ return mapping;
710
+ });
711
+ const entity = {
712
+ entityType: 'custom-entity',
713
+ typePath: 'custom-entity:prefix'
714
+ }
715
+
716
+ try{
717
+ const results = await TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
718
+ expect(results).toBeTruthy();
719
+
720
+ } finally {
721
+ spy.mockRestore();
722
+ }
723
+ });
724
+
725
+ it('should pass context to isOutboundCreatable function', async () =>{
726
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
727
+ .mockImplementation(async () =>{
728
+ return mapping;
729
+ });
730
+ const entity = {
731
+ entityType: 'custom-entity',
732
+ typePath: 'custom-entity:prefix'
733
+ }
734
+ const context = { skipPrefix: true };
735
+
736
+ try{
737
+ const results = await TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity, context);
738
+ expect(results).toBeFalsy();
739
+
740
+ } finally {
741
+ spy.mockRestore();
742
+ }
743
+ });
744
+
745
+ it('should work without context parameter', async () =>{
746
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
747
+ .mockImplementation(async () =>{
748
+ return mapping;
749
+ });
750
+ const entity = {
751
+ entityType: 'custom-entity',
752
+ typePath: 'custom-entity:prefix'
753
+ }
754
+
755
+ try{
756
+ const results = await TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
757
+ expect(results).toBeTruthy();
758
+
759
+ } finally {
760
+ spy.mockRestore();
761
+ }
762
+ });
763
+
764
+ it('should default to true when no mapping exists', async () =>{
765
+ const spy = jest.spyOn(mapFileUtil, 'getMapFile')
766
+ .mockImplementation(async () =>{
767
+ return mapping;
768
+ });
769
+ const entity = {
770
+ entityType: 'custom-entity',
771
+ typePath: 'custom-entity:catName'
772
+ }
773
+
774
+ try{
775
+ const results = await TypeConversionUtils.isOutboundCreatableFromEntity(TRANSFORM_MAP_FILE, mapFileUtil, entity);
776
+ expect(results).toBeTruthy();
777
+
778
+ } finally {
779
+ spy.mockRestore();
780
+ }
781
+ });
782
+
783
+ });
683
784
  });
@@ -351,6 +351,21 @@ export class TypeConversionUtils {
351
351
  return isInboundCreatable;
352
352
  }
353
353
 
354
+ static async isOutboundCreatableFromEntity(fileId: string, mapFileUtil: MapFileUtil, entity: any, context?: any): Promise<boolean> {
355
+
356
+ let isOutboundCreatable = true;
357
+ if(fileId){
358
+ const mapKey = await this.getMapKey(fileId, mapFileUtil, entity, TypeConversionUtils.VIBE2FLEX_DIRECTION);
359
+
360
+ const mapData = await MapUtil.getFullMapSection(fileId, mapFileUtil, mapKey);
361
+ if(mapData && mapData['isOutboundCreatable']){
362
+ isOutboundCreatable = await mapData['isOutboundCreatable'](entity, context);
363
+ }
364
+ }
365
+
366
+ return isOutboundCreatable;
367
+ }
368
+
354
369
  static getObjectType(object:any) {
355
370
  let objectType = object['flexPLMObjectClass'];
356
371
  return objectType;