@contrail/flexplm 1.1.67-alpha.2 → 1.1.67-alpha.4
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 +1 -0
- package/lib/interfaces/interfaces.d.ts +1 -1
- package/lib/publish/base-process-publish-assortment.js +7 -2
- package/lib/publish/base-process-publish-assortment.spec.js +201 -0
- package/lib/util/type-conversion-utils-spec-mockData.js +9 -0
- package/lib/util/type-conversion-utils.d.ts +1 -0
- package/lib/util/type-conversion-utils.js +21 -0
- package/lib/util/type-conversion-utils.spec.js +89 -0
- package/package.json +1 -1
- package/src/interfaces/interfaces.ts +1 -1
- package/src/publish/base-process-publish-assortment.spec.ts +276 -0
- package/src/publish/base-process-publish-assortment.ts +11 -3
- package/src/util/event-short-message-status.ts +3 -3
- package/src/util/type-conversion-utils-spec-mockData.ts +9 -0
- package/src/util/type-conversion-utils.spec.ts +101 -0
- package/src/util/type-conversion-utils.ts +26 -0
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
|
|
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
|
|
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
|
+
});
|
|
@@ -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: {
|
|
@@ -160,6 +163,12 @@ exports.mapping = {
|
|
|
160
163
|
}
|
|
161
164
|
return true;
|
|
162
165
|
},
|
|
166
|
+
isOutboundCreatable: (entity, context) => {
|
|
167
|
+
if (context && context.skipPrefix) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
},
|
|
163
172
|
vibe2flex: {
|
|
164
173
|
transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
|
|
165
174
|
rekey: {
|
|
@@ -16,5 +16,6 @@ export declare class TypeConversionUtils {
|
|
|
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
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
|
}
|
|
@@ -191,6 +191,27 @@ class TypeConversionUtils {
|
|
|
191
191
|
}
|
|
192
192
|
return isInboundCreatable;
|
|
193
193
|
}
|
|
194
|
+
static async isOutboundCreatableFromEntity(fileId, mapFileUtil, entity, context) {
|
|
195
|
+
let isOutboundCreatable = true;
|
|
196
|
+
if (!fileId) {
|
|
197
|
+
return isOutboundCreatable;
|
|
198
|
+
}
|
|
199
|
+
let mapKey;
|
|
200
|
+
try {
|
|
201
|
+
mapKey = await this.getMapKey(fileId, mapFileUtil, entity, TypeConversionUtils.VIBE2FLEX_DIRECTION);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return isOutboundCreatable;
|
|
205
|
+
}
|
|
206
|
+
if (!mapKey) {
|
|
207
|
+
return isOutboundCreatable;
|
|
208
|
+
}
|
|
209
|
+
const mapData = await map_utils_1.MapUtil.getFullMapSection(fileId, mapFileUtil, mapKey);
|
|
210
|
+
if (mapData && mapData['isOutboundCreatable']) {
|
|
211
|
+
isOutboundCreatable = await mapData['isOutboundCreatable'](entity, context);
|
|
212
|
+
}
|
|
213
|
+
return isOutboundCreatable;
|
|
214
|
+
}
|
|
194
215
|
static getObjectType(object) {
|
|
195
216
|
let objectType = object['flexPLMObjectClass'];
|
|
196
217
|
return objectType;
|
|
@@ -616,4 +616,93 @@ describe('conversion-utils', () => {
|
|
|
616
616
|
}
|
|
617
617
|
});
|
|
618
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
|
+
});
|
|
707
|
+
});
|
|
619
708
|
});
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|
|
974
|
+
eventType,
|
|
967
975
|
objectClass: 'LCSSKUSeasonLink',
|
|
968
976
|
entityReference: 'item:' + entityReference,
|
|
969
977
|
LCSSeason,
|
|
@@ -2,9 +2,9 @@ export enum EventShortMessageStatus {
|
|
|
2
2
|
SUCCESS = 'Success',
|
|
3
3
|
FAILURE = 'Failure',
|
|
4
4
|
CREATED = 'Created',
|
|
5
|
-
ITEM_FAMILY_ID_MISSING =
|
|
6
|
-
ITEM_FAMILY_NOT_FOUND =
|
|
7
|
-
PROJECT_ITEM_NOT_FOUND =
|
|
5
|
+
ITEM_FAMILY_ID_MISSING = 'Item_family_id_missing',
|
|
6
|
+
ITEM_FAMILY_NOT_FOUND = 'Item_family_not_found',
|
|
7
|
+
PROJECT_ITEM_NOT_FOUND = 'Project_item_not_found',
|
|
8
8
|
MISSING_IDENTIFIER_PROPERTIES = 'Missing_identifier_properties',
|
|
9
9
|
MISSING_INPUT = 'Missing_input',
|
|
10
10
|
NOT_CREATABLE = 'Not_creatable',
|
|
@@ -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,32 @@ 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
|
+
|
|
358
|
+
if (!fileId) {
|
|
359
|
+
return isOutboundCreatable;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let mapKey: string | undefined;
|
|
363
|
+
try {
|
|
364
|
+
mapKey = await this.getMapKey(fileId, mapFileUtil, entity, TypeConversionUtils.VIBE2FLEX_DIRECTION);
|
|
365
|
+
} catch {
|
|
366
|
+
return isOutboundCreatable;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (!mapKey) {
|
|
370
|
+
return isOutboundCreatable;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const mapData = await MapUtil.getFullMapSection(fileId, mapFileUtil, mapKey);
|
|
374
|
+
if (mapData && mapData['isOutboundCreatable']) {
|
|
375
|
+
isOutboundCreatable = await mapData['isOutboundCreatable'](entity, context);
|
|
376
|
+
}
|
|
377
|
+
return isOutboundCreatable;
|
|
378
|
+
}
|
|
379
|
+
|
|
354
380
|
static getObjectType(object:any) {
|
|
355
381
|
let objectType = object['flexPLMObjectClass'];
|
|
356
382
|
return objectType;
|