Skip to main content

b2c-custom-job-steps

Create custom job steps for B2C Commerce batch processing. Use this skill whenever the user needs to write a batch job, data export script, scheduled cleanup task, or any server-side processing that runs on a schedule. Also use when they ask about steptypes.json, chunk-oriented vs task-oriented job steps, read/process/write patterns, how to get a custom job to appear in Business Manager, or how to author and import a jobs.xml job definition (job/flow/step structure, step type, the required triggers element) so a step type becomes a runnable, schedulable job — even if they just say "I need a script that runs nightly" or "batch process orders".

来源信息

仓库
SalesforceCommerceCloud/b2c-developer-tooling
最近来源活动
2026年7月31日 13:36
检测到的 SKILL.md 语言
英语
星标
56
分支
22

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
6 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
b2c-custom-job-steps
description
Create custom job steps for B2C Commerce batch processing. Use this skill whenever the user needs to write a batch job, data export script, scheduled cleanup task, or any server-side processing that runs on a schedule. Also use when they ask about steptypes.json, chunk-oriented vs task-oriented job steps, read/process/write patterns, how to get a custom job to appear in Business Manager, or how to author and import a jobs.xml job definition (job/flow/step structure, step type, the required triggers element) so a step type becomes a runnable, schedulable job — even if they just say "I need a script that runs nightly" or "batch process orders".
# Custom Job Steps Skill This skill guides you through **creating new custom job steps** for Salesforce B2C Commerce batch processing. > **Running an existing job?** If you need to execute jobs or import site archives via CLI, use the `b2c-cli:b2c-job` skill instead. ## When to Use - Creating a **new scheduled job** for batch processing - Building a **data import job** (customers, products, orders) - Building a **data export job** (reports, feeds, sync) - Implementing **data sync** between systems - Creating **cleanup or maintenance tasks** ## Overview Custom job steps allow you to execute custom business logic as part of B2C Commerce jobs. There are two execution models: | Model | Use Case | Progress Tracking | |-------|----------|-------------------| | **Task-oriented** | Single operations (FTP, import/export) | Limited | | **Chunk-oriented** | Bulk data processing | Fine-grained | ## File Structure ``` 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) ``` **Important:** The `steptypes.json` file must be placed in the **root** folder of the cartridge, not inside the `cartridge/` directory. Only one `steptypes.json` file per cartridge. ## Step Type Definition (steptypes.json) ```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" } ] } } ] } } ``` ## From Step Type to Runnable Job (jobs.xml) `steptypes.json` only *declares* a step type — it does not create a job. To get a job that `b2c job run` can execute and Business Manager can schedule, author a **job definition** (`jobs.xml`) that references your step type, then import it: ```bash b2c job import ./my-job-archive # jobs.xml at the archive root ``` A minimal valid definition wires one step into a flow and includes the **required `<triggers>`** element: ```xml <?xml version="1.0" encoding="UTF-8"?> <jobs xmlns="http://www.demandware.com/xml/impex/jobs/2015-07-01"> <job job-id="MyNightlyExport"> <flow> <context site-id="RefArch"/> <!-- type="..." matches the @type-id declared in steptypes.json --> <step step-id="ExportStep" type="custom.ProductExport"> <parameters> <parameter name="OutputFile">/export/products.csv</parameter> </parameters> </step> </flow> <triggers> <run-once enabled="false"> <date>2025-01-01</date> <time>00:00:00.000Z</time> </run-once> </triggers> </job> </jobs> ``` Key rules (full details in the [jobs.xml Reference](references/JOBS-XML.md)): - **`<triggers>` is required** by the schema — a `jobs.xml` without it fails import validation. Use `<run-once enabled="false">` for an on-demand/manually-run job, or `<run-recurring>` to schedule it. - The `<step>` **`type`** attribute references the step type; its value must match the **`@type-id`** you declared in `steptypes.json` (don't confuse the two — `steptypes.json` uses `@type-id`, `jobs.xml` uses `type`). - `<job>` children must appear in order: `description → parameters → flow/split → rules → triggers`. - The cartridge carrying the step's `steptypes.json` + module must be deployed and on the cartridge path before the job can resolve the step type. After import, run it with `b2c job run MyNightlyExport --wait` (see the `b2c-cli:b2c-job` skill). ## Task-Oriented Steps Use for single operations like FTP transfers, file generation, or import/export. ### Script (scripts/steps/myTaskStep.js) ```javascript '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); } }; ``` ### Status Codes ```javascript // 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'); ``` **Important:** Custom status codes work **only** with OK status. If you use a custom code with ERROR status, it is replaced with ERROR. Custom status codes cannot contain commas, wildcards, leading/trailing whitespace, or exceed 100 characters. ## Chunk-Oriented Steps Use for bulk processing of countable data (products, orders, customers). **Important:** You cannot define custom exit status for chunk-oriented steps. Chunk modules always finish with either **OK** or **ERROR**. ### Required Functions | Function | Purpose | Returns | |----------|---------|---------| | `read()` | Get next item | Item or nothing | | `process(item)` | Transform item | Processed item or nothing (filters) | | `write(items)` | Save chunk of items | Nothing | ### Optional Functions | Function | Purpose | Returns | |----------|---------|---------| | `beforeStep()` | Initialize (open files, queries) | Nothing | | `afterStep(success)` | Cleanup (close files) | Nothing | | `getTotalCount()` | Return total items for progress | Number | | `beforeChunk()` | Before each chunk | Nothing | | `afterChunk()` | After each chunk | Nothing | ### Script (scripts/steps/myChunkStep.js) ```javascript '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'); } }; ``` ## Parameter Types | Type | Description | Example Value | |------|-------------|---------------| | `string` | Text value | `"my-value"` | | `boolean` | true/false | `true` | | `long` | Integer | `12345` | | `double` | Decimal | `123.45` | | `datetime-string` | ISO datetime | `"2024-01-15T10:30:00Z"` | | `date-string` | ISO date | `"2024-01-15"` |
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看