I wrote this schedulable B2B Commerce Product Index Builder class based on documentation from Salesforce and consulting a custom rep.
global class ccScheduledProductIndexBuilder implements Schedulable {
global void execute(SchedulableContext SC) {
Map<Integer, Map<String, String>> batchScopeValues = ccrz.ccProductIndexBuilder.getBatchScopeValues(new Map<String, Object> {
ccrz.ccProductIndexBuilder.NUMBER_OF_SCOPE_KEY_PAIRS => 5,
ccrz.ccProductIndexBuilder.IS_FULL_REFRESH => FALSE
});
for (Integer key : batchScopeValues.keySet()){
ccrz.ccProductIndexBuilder.cleanAndBuild(new Map<String, Object> {
ccrz.ccProductIndexBuilder.BATCH_JOB_PRODUCT_SCOPE => batchScopeValues.get(key),
ccrz.ccApiProductIndex.CLEAN_ONLY => TRUE,
ccrz.ccAPIProductIndex.LOCALES => new Set<String>{'en_US','en'}
});
}
}
}
I then wrote a tester class for it
@isTest
private class ccScheduledProductIndexBuilderTest {
@isTest
static void TestScheduledJob() {
/*
from https://help.salesforce.com/articleView?id=sf.b2b_commerce_product_index.htm&type=5
A single product index record references a corresponding CC Product record.
The product index record also reflects data from a single instance of the following types of
records related to that product:
- CC Product Category
- CC Price List Item
- CC Product Item I18N
To create an index record for a particular product, the product must meet the following criteria:
- The value of the Product Status field on the product record is Released or Not Orderable.
- The product has a price list item assigned to an entitled price list.
- The price list that the price list item belongs to has the Enabled field selected.
- The value of the End Date field for the product, price list, and price list item records are greater than the current date.
- If you’re using B2B Commerce for Visualforce Spring ’20 (version 4.12), the Exclude from Product Index field on the product isn't selected.
*/
ccrz__E_Category__c c1 = new ccrz__E_Category__c();
c1.Name = 'Test category';
c1.ccrz__CategoryID__c = 'CAT-123';
c1.ccrz__StartDate__c = date.ValueOf('2021-01-01');
c1.ccrz__EndDate__c = date.ValueOf('2100-01-01');
insert c1;
ccrz__E_PriceList__c pl1 = new ccrz__E_PriceList__c();
pl1.Name = 'Test price list';
pl1.ccrz__StartDate__c = date.ValueOf('2021-01-01');
pl1.ccrz__EndDate__c = date.ValueOf('2100-01-01');
pl1.ccrz__Enabled__c = true;
insert pl1;
ccrz__E_Product__c p1 = new ccrz__E_Product__c();
p1.Name = 'Test product 1';
p1.ccrz__SKU__c = 'SKU-1234';
p1.ccrz__ProductStatus__c = 'Released';
p1.ccrz__EndDate__c = date.ValueOf('2100-01-01');
p1.ccrz__ExcludeFromProductIndex__c = false;
insert p1;
ccrz__E_PriceListItem__c pLI1 = new ccrz__E_PriceListItem__c();
plI1.ccrz__Pricelist__c = pl1.Id;
plI1.ccrz__StartDate__c = date.ValueOf('2021-01-01');
plI1.ccrz__EndDate__c = date.ValueOf('2100-01-01');
plI1.ccrz__Price__c = 10.00;
plI1.ccrz__Product__c = p1.Id;
plI1.ccrz__ItemType__c = 'test price line item type';
plI1.ccrz__PricelistItemId__c = 'external price list item id';
plI1.ccrz__MaxQty__c = 15;
plI1.ccrz__MinQty__c = 10;
plI1.ccrz__PricingTiers__c = 'Pricing tier 1';
plI1.ccrz__RecurringPrice__c = false;
pLI1.ccrz__UnitOfMeasure__c = 'Measure 1';
plI1.ccrz__AttributePricing__c = 'Atr pricing 1';
insert plI1;
// System.debug('plI1.Id = ' + plI1.Id);
ccrz__E_ProductCategory__c pc1 = new ccrz__E_ProductCategory__c();
pc1.ccrz__Category__c = c1.Id;
pc1.ccrz__Product__c = p1.Id;
pc1.ccrz__StartDate__c = date.ValueOf('2021-01-01');
pc1.ccrz__EndDate__c = date.ValueOf('2100-01-01');
insert pc1;
ccrz__E_ProductItemI18N__c pI1 = new ccrz__E_ProductItemI18N__c();
pI1.Name = 'Test product item name';
pI1.ccrz__Product__c = p1.Id;
insert pI1;
ccrz__E_Product__c p2 = new ccrz__E_Product__c(ccrz__SKU__c = 'SKU-3456');
p2.Name = 'Test product 2';
p2.ccrz__SKU__c = 'SKU-3456';
p2.ccrz__ProductStatus__c = 'Released';
p2.ccrz__EndDate__c = date.ValueOf('2100-01-01');
p2.ccrz__ExcludeFromProductIndex__c = false;
insert p2;
ccrz__E_PriceListItem__c pLI2 = new ccrz__E_PriceListItem__c();
plI2.ccrz__Pricelist__c = pl1.Id;
plI2.ccrz__StartDate__c = date.ValueOf('2021-01-01');
plI2.ccrz__EndDate__c = date.ValueOf('2100-01-01');
plI2.ccrz__Price__c = 10.00;
plI2.ccrz__Product__c = p2.Id;
plI2.ccrz__ItemType__c = 'test price line item type 2';
plI2.ccrz__PricelistItemId__c = 'external price list item id 2';
plI2.ccrz__MaxQty__c = 15;
plI2.ccrz__MinQty__c = 10;
plI2.ccrz__PricingTiers__c = 'Pricing tier 2';
plI2.ccrz__RecurringPrice__c = false;
plI2.ccrz__UnitOfMeasure__c = 'Measure 2';
plI2.ccrz__AttributePricing__c = 'Atr pricing 2';
insert plI2;
// System.debug('plI2.Id = ' + plI2.Id);
ccrz__E_ProductCategory__c pc2 = new ccrz__E_ProductCategory__c();
pc2.ccrz__Category__c = c1.Id;
pc2.ccrz__Product__c = p2.Id;
pc2.ccrz__StartDate__c = date.ValueOf('2021-01-01');
pc2.ccrz__EndDate__c = date.ValueOf('2100-01-01');
insert pc2;
ccrz__E_ProductItemI18N__c pI2 = new ccrz__E_ProductItemI18N__c();
pI2.Name = 'Test product item name';
pI2.ccrz__Product__c = p2.Id;
insert pI2;
String sch = '0 0 23 * * ?';
Crontrigger ct;
ccScheduledProductIndexBuilder myClass = new ccScheduledProductIndexBuilder();
String jobId = System.Schedule('Test Sched', sch, myClass);
ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE id = :jobId];
System.assertEquals(sch, ct.CronExpression);
System.assertEquals(0, ct.TimesTriggered);
// System.assertEquals(true, true);
}
}
When I run the test, I'm always getting this error "System.NullPointerException: Attempt to de-reference a null object Class.ccScheduledProductIndexBuilder.execute: line 9, column 1"
I created all the objects necessary for Indexing a Product so I do not know what else I need to add. Do you have any ideas how I can fix this?