Loading...
Loading...
Create custom job steps for B2C Commerce batch processing. Use when writing scheduled tasks, data sync jobs, import/export scripts, or any server-side batch processing code. Covers steptypes.json, chunk-oriented processing, and task-oriented execution. For running existing jobs, use b2c-job instead.
npx skill4agent add salesforcecommercecloud/b2c-developer-tooling b2c-custom-job-stepsRunning an existing job? If you need to execute jobs or import site archives via CLI, use theskill instead.b2c-cli:b2c-job
| Model | Use Case | Progress Tracking |
|---|---|---|
| Task-oriented | Single operations (FTP, import/export) | Limited |
| Chunk-oriented | Bulk data processing | Fine-grained |
my_cartridge/
├── cartridge/
│ ├── scripts/
│ │ └── steps/
│ │ ├── myTaskStep.js # Task-oriented script
│ │ └── myChunkStep.js # Chunk-oriented script
│ └── my_cartridge.properties
└── steptypes.json # Step type definitions (at cartridge ROOT)steptypes.jsoncartridge/steptypes.json{
"step-types": {
"script-module-step": [
{
"@type-id": "custom.MyTaskStep",
"@supports-parallel-execution": "false",
"@supports-site-context": "true",
"@supports-organization-context": "false",
"description": "My custom task step",
"module": "my_cartridge/cartridge/scripts/steps/myTaskStep.js",
"function": "execute",
"timeout-in-seconds": 900,
"parameters": {
"parameter": [
{
"@name": "InputFile",
"@type": "string",
"@required": "true",
"description": "Path to input file"
},
{
"@name": "Enabled",
"@type": "boolean",
"@required": "false",
"default-value": "true",
"description": "Enable processing"
}
]
},
"status-codes": {
"status": [
{
"@code": "OK",
"description": "Step completed successfully"
},
{
"@code": "ERROR",
"description": "Step failed"
},
{
"@code": "NO_DATA",
"description": "No data to process"
}
]
}
}
],
"chunk-script-module-step": [
{
"@type-id": "custom.MyChunkStep",
"@supports-parallel-execution": "true",
"@supports-site-context": "true",
"@supports-organization-context": "false",
"description": "Bulk data processing step",
"module": "my_cartridge/cartridge/scripts/steps/myChunkStep.js",
"before-step-function": "beforeStep",
"read-function": "read",
"process-function": "process",
"write-function": "write",
"after-step-function": "afterStep",
"total-count-function": "getTotalCount",
"chunk-size": 100,
"transactional": "false",
"timeout-in-seconds": 1800,
"parameters": {
"parameter": [
{
"@name": "CategoryId",
"@type": "string",
"@required": "true"
}
]
}
}
]
}
}'use strict';
var Status = require('dw/system/Status');
var Logger = require('dw/system/Logger');
/**
* Execute the task step
* @param {Object} parameters - Job step parameters
* @param {dw.job.JobStepExecution} stepExecution - Step execution context
* @returns {dw.system.Status} Execution status
*/
exports.execute = function (parameters, stepExecution) {
var log = Logger.getLogger('job', 'MyTaskStep');
try {
var inputFile = parameters.InputFile;
var enabled = parameters.Enabled;
if (!enabled) {
log.info('Step disabled, skipping');
return new Status(Status.OK, 'SKIP', 'Step disabled');
}
// Your business logic here
log.info('Processing file: ' + inputFile);
// Return success
return new Status(Status.OK);
} catch (e) {
log.error('Step failed: ' + e.message);
return new Status(Status.ERROR, 'ERROR', e.message);
}
};// Success
return new Status(Status.OK);
return new Status(Status.OK, 'CUSTOM_CODE', 'Custom message');
// Error
return new Status(Status.ERROR);
return new Status(Status.ERROR, null, 'Error message');| Function | Purpose | Returns |
|---|---|---|
| Get next item | Item or nothing |
| Transform item | Processed item or nothing (filters) |
| Save chunk of items | Nothing |
| Function | Purpose | Returns |
|---|---|---|
| Initialize (open files, queries) | Nothing |
| Cleanup (close files) | Nothing |
| Return total items for progress | Number |
| Before each chunk | Nothing |
| After each chunk | Nothing |
'use strict';
var ProductMgr = require('dw/catalog/ProductMgr');
var Transaction = require('dw/system/Transaction');
var Logger = require('dw/system/Logger');
var File = require('dw/io/File');
var FileWriter = require('dw/io/FileWriter');
var log = Logger.getLogger('job', 'MyChunkStep');
var products;
var fileWriter;
/**
* Initialize before processing
*/
exports.beforeStep = function (parameters, stepExecution) {
log.info('Starting chunk processing');
// Open resources
var outputFile = new File(File.IMPEX + '/export/products.csv');
fileWriter = new FileWriter(outputFile);
fileWriter.writeLine('ID,Name,Price');
// Query products
products = ProductMgr.queryAllSiteProducts();
};
/**
* Get total count for progress tracking
*/
exports.getTotalCount = function (parameters, stepExecution) {
return products.count;
};
/**
* Read next item
* Return nothing to signal end of data
*/
exports.read = function (parameters, stepExecution) {
if (products.hasNext()) {
return products.next();
}
// Return nothing = end of data
};
/**
* Process single item
* Return nothing to filter out item
*/
exports.process = function (product, parameters, stepExecution) {
// Filter: skip offline products
if (!product.online) {
return; // Filtered out
}
// Transform
return {
id: product.ID,
name: product.name,
price: product.priceModel.price.value
};
};
/**
* Write chunk of processed items
*/
exports.write = function (items, parameters, stepExecution) {
for (var i = 0; i < items.size(); i++) {
var item = items.get(i);
fileWriter.writeLine(item.id + ',' + item.name + ',' + item.price);
}
};
/**
* Cleanup after all chunks
*/
exports.afterStep = function (success, parameters, stepExecution) {
// Close resources
if (fileWriter) {
fileWriter.close();
}
if (products) {
products.close();
}
if (success) {
log.info('Chunk processing completed successfully');
} else {
log.error('Chunk processing failed');
}
};| Type | Description | Example Value |
|---|---|---|
| Text value | |
| true/false | |
| Integer | |
| Decimal | |
| ISO datetime | |
| ISO date | |
| ISO time | |
| Attribute | Applies To | Description |
|---|---|---|
| All | Trim whitespace before validation (default: |
| All | Mark as required (default: |
| datetime-string, date-string, time-string | Convert to |
| string | Regex pattern for validation |
| string | Minimum string length (must be ≥1) |
| string | Maximum string length (max 1000 chars total) |
| long, double, datetime-string, time-string | Minimum numeric value |
| long, double, datetime-string, time-string | Maximum numeric value |
| All | Restrict to allowed values (dropdown in BM) |
| Attribute | Required | Description |
|---|---|---|
| Yes | Unique ID (must start with |
| No | Allow parallel execution (default: |
| No | Available in site-scoped jobs (default: |
| No | Available in org-scoped jobs (default: |
| Yes | Path to script module |
| Yes | Function name to execute (task-oriented) |
| No | Step timeout (recommended to set) |
| No | Wrap in single transaction (default: |
| Yes* | Items per chunk (*required for chunk steps) |
@supports-site-context@supports-organization-contexttruefalsetruefalseafterStep()Transaction.wrap()b2c-cli:b2c-jobb2c:b2c-webservices