salesrender / plugin-core-logistic
SalesRender plugin logistic core
Package info
github.com/SalesRender/plugin-core-logistic
pkg:composer/salesrender/plugin-core-logistic
Requires
- php: ^7.4.0
- ext-json: *
- salesrender/plugin-component-logistic: ^2.1.0
- salesrender/plugin-component-purpose: ^2.0
- salesrender/plugin-core: ^0.4.0
- salesrender/plugin-core-geocoder: ^0.3.0
- xakepehok/array-to-uuid-helper: ^0.1.0
Requires (Dev)
- mockery/mockery: ^1.5
- phpunit/phpunit: ^9.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-master
- 0.10.3
- 0.10.2
- 0.10.1
- 0.10.0
- 0.9.5
- 0.9.4
- 0.9.3
- 0.9.2
- 0.9.1
- 0.9.0
- 0.8.6
- 0.8.5
- 0.8.4
- 0.8.3
- 0.8.2
- 0.8.1
- 0.8.0
- 0.7.1
- 0.7.0
- 0.6.0
- 0.5.4
- 0.5.3
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.6
- 0.4.4
- 0.4.3
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.9
- 0.3.8
- 0.3.7
- 0.3.6
- 0.3.5
- 0.3.3
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.1
- 0.2.0
- 0.1.6
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1.0
- 0.0.7
- 0.0.6
- 0.0.5
- 0.0.4
- 0.0.3
- 0.0.2
- 0.0.1
- dev-s_logistic_status_with_index
- dev-s_ff
This package is auto-updated.
Last update: 2026-08-28 11:50:40 UTC
README
Core framework for SalesRender logistic (shipping & fulfillment) plugins
Russian version / Русская версия
Overview
salesrender/plugin-core-logistic is the most feature-rich specialized core library in the SalesRender plugin ecosystem. It extends salesrender/plugin-core (via salesrender/plugin-core-geocoder) to provide the complete infrastructure for logistic plugins.
Logistic plugins handle two major domains:
- Shipping (delivery) -- Creating shipments, generating waybills, adding orders to shipments, tracking packages, canceling shipments, and removing orders
- Fulfillment -- Syncing inventory with external warehouses, binding SalesRender SKUs to external product identifiers, handling order fulfillment sync
A single logistic plugin operates in one mode determined by its class in Info::config():
| Class | Mode | Description |
|---|---|---|
LogisticPluginClass::CLASS_DELIVERY |
Shipping | Standard delivery providers (courier, pickup point, post) |
LogisticPluginClass::CLASS_FULFILLMENT |
Fulfillment | Warehouse/fulfillment center integrations |
Installation
composer require salesrender/plugin-core-logistic
Requirements
- PHP >= 7.4
- ext-json
salesrender/plugin-core^0.4.0salesrender/plugin-core-geocoder^0.3.0salesrender/plugin-component-logistic^2.1.0salesrender/plugin-component-purpose^2.0xakepehok/array-to-uuid-helper^0.1.0
Architecture
How It Extends plugin-core
plugin-core-logistic extends the chain: plugin-core -> plugin-core-geocoder -> plugin-core-logistic.
-
WebAppFactory (
SalesRender\Plugin\Core\Logistic\Factories\WebAppFactory) extends the geocoder'sWebAppFactoryand automatically:- Adds CORS support
- Registers batch processing actions
- Registers the waybill form and handler at
/protected/forms/waybill - Registers the track status endpoint at
/protected/track/statuses/{trackNumber} - For shipping mode: Registers
ShippingCancelActionandRemoveOrdersActionas special request actions - For fulfillment mode: Registers
SyncActionas a special request action; adds a settings save handler that triggers fulfillment binding sync
-
ConsoleAppFactory (
SalesRender\Plugin\Core\Logistic\Factories\ConsoleAppFactory) extends the base console factory and:- Adds batch processing commands
- Adds the
FulfillmentSyncCommand(fulfillment:sync) - For fulfillment mode: Registers three cron tasks for automatic binding sync
What the Developer Must Implement
For Shipping plugins:
| Interface / Class | Purpose |
|---|---|
WaybillHandlerInterface |
Process waybill form data and return a WaybillResponse |
BatchShippingHandler (extend) |
Handle batch shipping: create shipments, add orders, mark as exported |
ShippingCancelAction (extend) |
Handle shipment cancellation requests |
RemoveOrdersAction (extend) |
Handle order removal from shipments |
For Fulfillment plugins:
| Interface / Class | Purpose |
|---|---|
WaybillHandlerInterface |
Process waybill form data and return a WaybillResponse |
FulfillmentBindingHandlerInterface |
Build product bindings from plugin settings |
FulfillmentSyncHandlerInterface |
Sync individual orders with the external fulfillment system |
FulfillmentRemoveHandlerInterface |
Handle order removal from the fulfillment system |
BatchFulfillmentHandler (extend) |
Handle batch fulfillment processing |
Bootstrap Configuration Steps
The bootstrap.php file wires everything together (see bootstrap.example.php in the repository):
- Configure the database connection (
Connector::config) - Set the default language (
Translator::config) - Configure file upload settings (
UploadersContainer::addDefaultUploader) - Configure plugin info (
Info::configwithPluginType::LOGISTIC) - Configure the settings form (
Settings::setForm) - Configure autocompletes (optional)
- Configure table previews (optional)
- Configure batch forms and handler (
BatchContainer::config) - Configure waybill form and handler (
WaybillContainer::config) - For shipping: Configure cancel and remove actions (
ShippingContainer::config) - For fulfillment: Configure fulfillment handlers (
FulfillmentContainer::config)
Getting Started: Creating a Logistic Plugin
Step 1: Project Setup
Create composer.json:
{
"name": "your-vendor/plugin-logistic-your-provider",
"type": "project",
"autoload": {
"psr-4": {
"YourVendor\\Plugin\\Logistic\\YourProvider\\": "src/"
}
},
"require": {
"php": "^7.4.0",
"ext-json": "*",
"salesrender/plugin-core-logistic": "^0.7.0"
}
}
composer install
Step 2: Bootstrap Configuration
Create bootstrap.php. This example shows a shipping plugin:
<?php use SalesRender\Plugin\Components\Batch\BatchContainer; use SalesRender\Plugin\Components\Db\Components\Connector; use SalesRender\Plugin\Components\Info\Developer; use SalesRender\Plugin\Components\Info\Info; use SalesRender\Plugin\Components\Info\PluginType; use SalesRender\Plugin\Components\Purpose\LogisticPluginClass; use SalesRender\Plugin\Components\Purpose\PluginEntity; use SalesRender\Plugin\Components\Settings\Settings; use SalesRender\Plugin\Components\Translations\Translator; use SalesRender\Plugin\Core\Actions\Upload\LocalUploadAction; use SalesRender\Plugin\Core\Actions\Upload\UploadersContainer; use SalesRender\Plugin\Core\Logistic\Components\Actions\Shipping\ShippingContainer; use SalesRender\Plugin\Core\Logistic\Components\Waybill\WaybillContainer; use YourVendor\Plugin\Logistic\YourProvider\Batch\Batch_1; use YourVendor\Plugin\Logistic\YourProvider\Batch\BatchShippingHandler; use YourVendor\Plugin\Logistic\YourProvider\Actions\CancelAction; use YourVendor\Plugin\Logistic\YourProvider\Actions\RemoveOrdersAction; use YourVendor\Plugin\Logistic\YourProvider\Settings\SettingsForm; use YourVendor\Plugin\Logistic\YourProvider\Waybill\WaybillForm; use YourVendor\Plugin\Logistic\YourProvider\Waybill\WaybillHandler; use Medoo\Medoo; use XAKEPEHOK\Path\Path; # 1. Configure DB Connector::config(new Medoo([ 'database_type' => 'sqlite', 'database_file' => Path::root()->down('db/database.db') ])); # 2. Set default language Translator::config('ru_RU'); # 3. Configure file upload UploadersContainer::addDefaultUploader(new LocalUploadAction([])); # 4. Configure plugin info Info::config( new PluginType(PluginType::LOGISTIC), fn() => Translator::get('info', 'Your Logistic Plugin'), fn() => Translator::get('info', 'Plugin **description**'), [ "class" => LogisticPluginClass::CLASS_DELIVERY, "entity" => PluginEntity::ENTITY_ORDER, "currency" => ["RUB"], "codename" => "SR_LOGISTIC_YOUR_PROVIDER", ], new Developer( 'Your Company', 'support@example.com', 'example.com', ) ); # 5. Configure settings form Settings::setForm(fn() => new SettingsForm()); # 6. Configure batch forms and handler BatchContainer::config( function (int $number) { switch ($number) { case 1: return new Batch_1(); default: return null; } }, new BatchShippingHandler() ); # 7. Configure waybill form and handler WaybillContainer::config( fn() => new WaybillForm(), new WaybillHandler() ); # 8. Configure shipping cancel and remove actions ShippingContainer::config( new CancelAction(), new RemoveOrdersAction(), );
Step 3: Implement Required Interfaces
3a. WaybillHandlerInterface
The waybill handler processes waybill form submissions and returns a WaybillResponse:
<?php namespace YourVendor\Plugin\Logistic\YourProvider\Waybill; use SalesRender\Plugin\Components\Form\Form; use SalesRender\Plugin\Components\Form\FormData; use SalesRender\Plugin\Components\Logistic\Logistic; use SalesRender\Plugin\Components\Logistic\LogisticStatus; use SalesRender\Plugin\Components\Logistic\Waybill\Waybill; use SalesRender\Plugin\Components\Logistic\Waybill\Track; use SalesRender\Plugin\Core\Logistic\Components\Waybill\Response\WaybillAddress; use SalesRender\Plugin\Core\Logistic\Components\Waybill\Response\WaybillResponse; use SalesRender\Plugin\Core\Logistic\Components\Waybill\WaybillHandlerInterface; use SalesRender\Components\Address\Address; class WaybillHandler implements WaybillHandlerInterface { public function __invoke(Form $form, FormData $data): WaybillResponse { // Create waybill from form data $track = $data->get('waybill.track') ? new Track($data->get('waybill.track')) : null; $waybill = new Waybill( $track, null, // price null, // delivery terms null, // delivery type false // COD ); $logistic = new Logistic( $waybill, new LogisticStatus(LogisticStatus::CREATED) ); // Optionally return updated address $address = new WaybillAddress( $data->get('address.field.0'), new Address( (string) $data->get('address.region'), (string) $data->get('address.city'), (string) $data->get('address.address_1'), (string) $data->get('address.address_2'), (string) $data->get('address.postcode'), (string) $data->get('address.countryCode.0') ) ); return new WaybillResponse($logistic, $address); } }
3b. BatchShippingHandler
Extend the abstract BatchShippingHandler to implement the batch shipping workflow:
<?php namespace YourVendor\Plugin\Logistic\YourProvider\Batch; use SalesRender\Plugin\Components\Batch\Batch; use SalesRender\Plugin\Components\Batch\Process\Error; use SalesRender\Plugin\Components\Batch\Process\Process; use SalesRender\Plugin\Core\Logistic\Components\BatchShippingHandler; class BatchShippingHandler extends \SalesRender\Plugin\Core\Logistic\Components\BatchShippingHandler { public function __invoke(Process $process, Batch $batch) { // 1. Create a shipping in SalesRender $shippingId = $this->createShipping($batch); // 2. Iterate over orders, lock them, prepare data // 3. Add orders to the shipping $this->addOrders($batch, $shippingId, $ordersData); // 4. Mark shipping as exported $this->markAsExported($batch, $shippingId, $ordersCount); // On failure: // $this->markAsFailed($batch, $shippingId); $process->finish(true); $process->save(); } }
The base class provides these protected methods:
| Method | Returns | Description |
|---|---|---|
createShipping(Batch $batch, array $removeOnCancelFields = []) |
int |
Create a shipping in SalesRender, returns shipping ID |
addOrders(Batch $batch, string $shippingId, array $orders) |
ResponseInterface |
Add orders to a shipping |
markAsExported(Batch $batch, string $shippingId, int $ordersCount) |
ResponseInterface |
Mark shipping as exported |
markAsFailed(Batch $batch, string $shippingId) |
ResponseInterface |
Mark shipping as failed |
addShippingAttachments(Batch $batch, string $shippingId, ShippingAttachment ...$attachments) |
ResponseInterface |
Add file attachments to a shipping |
lockOrder(int $timeout, int $orderId, Batch $batch) |
bool |
Lock an order to prevent concurrent modifications |
3c. ShippingCancelAction and RemoveOrdersAction
Extend the abstract classes to handle cancellation and order removal:
<?php namespace YourVendor\Plugin\Logistic\YourProvider\Actions; use SalesRender\Plugin\Core\Logistic\Components\Actions\Shipping\ShippingCancelAction; use Slim\Http\Response; use Slim\Http\ServerRequest; class CancelAction extends ShippingCancelAction { protected function handle(array $body, ServerRequest $request, Response $response, array $args): Response { // Implement cancellation logic with your provider // $body contains the special request payload return $response->withStatus(202); } }
<?php namespace YourVendor\Plugin\Logistic\YourProvider\Actions; use SalesRender\Plugin\Core\Logistic\Components\Actions\Shipping\RemoveOrdersAction; use Slim\Http\Response; use Slim\Http\ServerRequest; class RemoveOrdersAction extends \SalesRender\Plugin\Core\Logistic\Components\Actions\Shipping\RemoveOrdersAction { protected function handle(array $body, ServerRequest $request, Response $response, array $args): Response { // Implement order removal logic return $response->withStatus(202); } }
Step 4: Web & Console Entry Points
public/index.php (HTTP entry point):
<?php use SalesRender\Plugin\Core\Logistic\Factories\WebAppFactory; require_once __DIR__ . '/../vendor/autoload.php'; $factory = new WebAppFactory(); $application = $factory->build(); $application->run();
console.php (CLI entry point):
#!/usr/bin/env php <?php use SalesRender\Plugin\Core\Logistic\Factories\ConsoleAppFactory; require __DIR__ . '/vendor/autoload.php'; require __DIR__ . '/bootstrap.php'; $factory = new ConsoleAppFactory(); $application = $factory->build(); $application->run();
Step 5: Deploy
- Point the web server document root to
public/ - Ensure the
db/directory is writable for SQLite storage - Set up a cron job:
* * * * * php /path/to/console.php cron
HTTP Routes
Inherited from plugin-core
| Method | Path | Description |
|---|---|---|
| POST | /protected/info |
Plugin information |
| POST | /protected/settings |
Get/save settings |
| POST | /protected/registration |
Plugin registration |
Waybill
| Method | Path | Description |
|---|---|---|
| GET | /protected/forms/waybill |
Get the waybill form definition |
| POST | /protected/forms/waybill |
Submit waybill form data |
The waybill endpoint validates the form data via the registered WaybillContainer form, then calls the WaybillHandlerInterface implementation. The response includes a signed JWT token for the logistic data, the address, waybill details, and the initial status.
Track
| Method | Path | Description |
|---|---|---|
| GET | /protected/track/statuses/{trackNumber} |
Get tracking statuses for a shipment |
The trackNumber parameter must match the pattern [A-z\d\-_]{6,36}.
Batch
| Method | Path | Description |
|---|---|---|
| POST | /protected/batch/{number} |
Get batch form definition |
| POST | /protected/batch/handle |
Execute batch processing |
Special Request Routes
Shipping mode:
| Method | Path | Description |
|---|---|---|
| POST | /protected/special-request/shippingCancel |
Cancel a shipping |
| POST | /protected/special-request/removeOrders |
Remove orders from a shipping |
Fulfillment mode:
| Method | Path | Description |
|---|---|---|
| POST | /protected/special-request/ffSync |
Sync an order with the fulfillment system |
CLI Commands
| Command | Description |
|---|---|
cron |
Run scheduled cron tasks |
special-request:send |
Process queued special requests |
db:migrate |
Run database migrations |
batch:handle |
Execute batch processing (internal) |
fulfillment:sync {selectWithinHours} [-o|--outdatedOnly] |
Sync fulfillment bindings |
fulfillment:sync Command
Synchronizes product bindings with the SalesRender backend. Used in fulfillment mode only.
| Argument/Option | Description |
|---|---|
selectWithinHours (required) |
Only sync bindings updated within this many hours |
--outdatedOnly / -o |
Only sync bindings where syncedAt < updatedAt |
Automatic cron schedules (registered for fulfillment mode):
| Schedule | Command | Purpose |
|---|---|---|
*/10 * * * * |
fulfillment:sync 12 -o |
Sync recently changed bindings every 10 minutes |
15 * * * * |
fulfillment:sync 24 |
Full sync of bindings updated in last 24 hours |
25 5 * * 6 |
fulfillment:sync 720 |
Weekly deep sync (last 30 days) |
Key Classes & Interfaces
Shipping
ShippingContainer
Namespace: SalesRender\Plugin\Core\Logistic\Components\Actions\Shipping\ShippingContainer
Static container for shipping-related actions. Must be configured in bootstrap.php for delivery plugins.
| Method | Description |
|---|---|
static config(ShippingCancelAction, RemoveOrdersAction) |
Register cancel and remove actions |
static getShippingCancelAction(): ShippingCancelAction |
Get the cancel action (throws ShippingContainerException if not configured) |
static getRemoveOrdersAction(): RemoveOrdersAction |
Get the remove action (throws ShippingContainerException if not configured) |
ShippingCancelAction
Namespace: SalesRender\Plugin\Core\Logistic\Components\Actions\Shipping\ShippingCancelAction
Abstract class extending SpecialRequestAction. The action name is 'shippingCancel'. Override the handle(array $body, ServerRequest $request, Response $response, array $args): Response method.
RemoveOrdersAction
Namespace: SalesRender\Plugin\Core\Logistic\Components\Actions\Shipping\RemoveOrdersAction
Abstract class extending SpecialRequestAction. The action name is 'removeOrders'. Override the handle() method.
BatchShippingHandler
Namespace: SalesRender\Plugin\Core\Logistic\Components\BatchShippingHandler
Abstract batch handler for shipping operations. Implements BatchHandlerInterface and uses BatchLockTrait.
Protected methods for interacting with SalesRender backend:
| Method | HTTP | Endpoint | Description |
|---|---|---|---|
createShipping($batch, $removeOnCancelFields) |
POST | /CRM/plugin/logistic/shipping |
Create a new shipping, returns ID |
addOrders($batch, $shippingId, $orders) |
PATCH | /CRM/plugin/logistic/shipping/{id}/orders |
Add orders to shipping |
markAsExported($batch, $shippingId, $ordersCount) |
POST | /CRM/plugin/logistic/shipping/{id}/status/exported |
Mark as exported |
markAsFailed($batch, $shippingId) |
POST | /CRM/plugin/logistic/shipping/{id}/status/failed |
Mark as failed |
addShippingAttachments($batch, $shippingId, ...$attachments) |
PATCH | /CRM/plugin/logistic/shipping/{id}/attachments/add |
Add attachments |
Fulfillment
FulfillmentContainer
Namespace: SalesRender\Plugin\Core\Logistic\Components\Fulfillment\FulfillmentContainer
Static container for fulfillment handlers. Must be configured in bootstrap.php for fulfillment plugins.
| Method | Description |
|---|---|
static config(FulfillmentBindingHandlerInterface, FulfillmentSyncHandlerInterface, FulfillmentRemoveHandlerInterface) |
Register all three handlers |
static getBindingHandler() |
Get the binding handler |
static getSyncHandler() |
Get the sync handler |
static getRemoveHandler() |
Get the remove handler |
All getters throw FulfillmentContainerException if the handler is not configured.
FulfillmentBindingHandlerInterface
Namespace: SalesRender\Plugin\Core\Logistic\Components\Fulfillment\FulfillmentBindingHandlerInterface
| Method | Returns | Description |
|---|---|---|
__invoke(Settings $settings) |
Binding |
Build product bindings from plugin settings |
Called when settings are saved (in fulfillment mode). The returned Binding object is automatically synced to the backend.
FulfillmentSyncHandlerInterface
Namespace: SalesRender\Plugin\Core\Logistic\Components\Fulfillment\FulfillmentSyncHandlerInterface
| Method | Returns | Description |
|---|---|---|
getGraphqlOrderFields() |
array |
GraphQL field selection for orders (used to fetch order data) |
handle(array $graphqlOrder) |
?string |
Process an order sync; return null on success or error message on failure |
FulfillmentRemoveHandlerInterface
Namespace: SalesRender\Plugin\Core\Logistic\Components\Fulfillment\FulfillmentRemoveHandlerInterface
| Method | Returns | Description |
|---|---|---|
handle(string $orderId) |
?string |
Remove an order from the fulfillment system; return null on success or error message on failure |
SyncAction
Namespace: SalesRender\Plugin\Core\Logistic\Components\Actions\Fulfillment\SyncAction
Special request action for fulfillment order sync. Action name is 'ffSync'. Automatically fetches the order via GraphQL, calls the FulfillmentSyncHandlerInterface, and sends the result to the backend at /CRM/plugin/logistic/fulfillment/sync.
BatchFulfillmentHandler
Namespace: SalesRender\Plugin\Core\Logistic\Components\BatchFulfillmentHandler
Abstract batch handler for fulfillment operations. Implements BatchHandlerInterface and uses BatchLockTrait.
| Method | Returns | Description |
|---|---|---|
updateLogistic(Batch $batch, string $orderId, Waybill $waybill) |
ResponseInterface |
Send logistic update for an order (PUT to /CRM/plugin/logistic/fulfillment) |
Waybill
WaybillContainer
Namespace: SalesRender\Plugin\Core\Logistic\Components\Waybill\WaybillContainer
Static container for the waybill form and handler.
| Method | Description |
|---|---|
static config(callable $form, WaybillHandlerInterface $handler) |
Register form factory and handler |
static getForm(array $context = []): Form |
Get the form (throws WaybillContainerException if not configured) |
static getHandler(): WaybillHandlerInterface |
Get the handler |
WaybillHandlerInterface
Namespace: SalesRender\Plugin\Core\Logistic\Components\Waybill\WaybillHandlerInterface
| Method | Returns | Description |
|---|---|---|
__invoke(Form $form, FormData $data) |
WaybillResponse |
Process waybill form data and return the response |
WaybillHandlerAction
Namespace: SalesRender\Plugin\Core\Logistic\Components\Waybill\WaybillHandlerAction
Internal action class implementing ActionInterface. Handles the HTTP endpoint for waybill submission:
- Gets the form from
WaybillContainer - Validates form data
- Calls the
WaybillHandlerInterface - Signs the logistic data with JWT
- Returns the response with
logistic(JWT token),address,waybill, andstatus
WaybillResponse
Namespace: SalesRender\Plugin\Core\Logistic\Components\Waybill\Response\WaybillResponse
Value object returned by WaybillHandlerInterface.
| Property | Type | Description |
|---|---|---|
$logistic |
Logistic |
Logistic data (waybill + status) |
$address |
?WaybillAddress |
Optional updated delivery address |
WaybillAddress
Namespace: SalesRender\Plugin\Core\Logistic\Components\Waybill\Response\WaybillAddress
Delivery address associated with a waybill. Implements JsonSerializable.
| Property | Type | Description |
|---|---|---|
$field |
string |
The order field name for this address |
$address |
Address |
The address object |
Track
Track
Namespace: SalesRender\Plugin\Core\Logistic\Components\Track\Track
Database model for tracking shipments. Extends Model and implements PluginModelInterface. Stored in the tracks table.
| Property/Method | Type | Description |
|---|---|---|
$track |
string |
Tracking number |
$shippingId |
string |
Associated shipping ID |
$createdAt |
int |
Creation timestamp |
$nextTrackingAt |
?int |
Next scheduled tracking check |
$lastTrackedAt |
?int |
Last tracking check timestamp |
$statuses |
LogisticStatus[] |
Array of tracking statuses |
$stoppedAt |
?int |
When tracking was stopped |
$waybill |
Waybill |
Associated waybill |
addStatus(LogisticStatus) |
void |
Add a single status |
setStatuses(LogisticStatus[]) |
void |
Set/merge all statuses |
setLastTrackedAt() |
void |
Mark as just tracked |
setNextTrackingAt(int $minutes) |
void |
Schedule next tracking |
setStoppedAt() |
void |
Stop tracking this shipment |
static findForTracking(string $segments, int $limit) |
Track[] |
Find tracks ready for checking |
static findByTrack(string $track) |
Track[] |
Find tracks by tracking number |
The MAX_TRACKING_TIME constant is 5 months (150 days). Tracks older than this are not selected for tracking.
Status merging logic (mergeStatuses) handles deduplication and automatically maps statuses after RETURNED to RETURNING_TO_SENDER (when status sorting is enabled).
When new statuses are detected, the Track automatically creates a notification (special request) to the SalesRender backend at /CRM/plugin/logistic/status/{class}.
Each notification carries a sequential index (starting from 1) — the ordinal number of the notification within the shipping. The index is set via the immutable LogisticStatus::withIndex() and is included in the status field of the notification payload, so the backend can reject an outdated status delivered out of order. The index does not participate in the status hash, so deduplication by notificationsHashes is unaffected. The indexed status is also substituted in the statuses history, so the index is persisted in the tracks.statuses column and survives reloads — gaps and monotonicity can be checked with a single SELECT. The counter is bound to the shipping, not to the tracking number: replacing the waybill does not reset it.
TrackGetStatusesAction
Namespace: SalesRender\Plugin\Core\Logistic\Components\Actions\Track\TrackGetStatusesAction
HTTP action for the GET /protected/track/statuses/{trackNumber} endpoint. Finds the track by number, resolves and sorts statuses, and returns them as JSON.
Binding
Binding
Namespace: SalesRender\Plugin\Core\Logistic\Components\Binding\Binding
Database model for product bindings (SKU mappings). Extends Model and implements SinglePluginModelInterface. Stored in the bindings table.
| Method | Returns | Description |
|---|---|---|
getPairs() |
BindingPair[] |
Get all binding pairs |
getPairBySku(int $itemId, int $variation) |
?BindingPair |
Find pair by SalesRender SKU |
getPairByExternalId(string $externalId) |
?BindingPair |
Find pair by external product ID |
setPair(BindingPair $pair) |
void |
Add or update a binding pair |
deletePair(BindingPair $pair) |
void |
Remove a binding pair |
clearAllPairs() |
void |
Remove all pairs |
sync() |
void |
Sync bindings to SalesRender backend (PUT to /CRM/plugin/logistic/fulfillment/stock) |
static find() |
Binding |
Find or create the binding for current plugin reference |
The sync() method signs the stock data with a JWT token and queues a SpecialRequestTask.
BindingPair
Namespace: SalesRender\Plugin\Core\Logistic\Components\Binding\BindingPair
A single SKU-to-external-product mapping with stock balances. Implements JsonSerializable.
| Method | Returns | Description |
|---|---|---|
__construct(int $itemId, int $variation, string $externalId, array $balances) |
-- | Create a binding pair |
getItemId() |
int |
SalesRender item ID |
getVariation() |
int |
SalesRender variation |
getExternalId() |
string |
External product identifier (defaults to {itemId}_{variation} if empty) |
getBalanceByLabel(string $label) |
?int |
Get balance for a specific label |
getBalances() |
array |
Get all balances as associative array |
FulfillmentSyncCommand
Namespace: SalesRender\Plugin\Core\Logistic\Components\Commands\FulfillmentSyncCommand
Console command fulfillment:sync that iterates over all Binding records matching the criteria and calls Binding::sync() on each. Uses mutex locking to prevent concurrent execution.
Helpers & Services
LogisticHelper
Namespace: SalesRender\Plugin\Core\Logistic\Helpers\LogisticHelper
Utility class for logistic mode detection and configuration.
| Method | Returns | Description |
|---|---|---|
static config(bool $sortTrackStatuses = true) |
void |
Configure track status sorting behavior |
static isFulfillment() |
bool |
Check if current plugin is in fulfillment mode |
static isSortTrackStatuses() |
bool |
Check if track status sorting is enabled |
OrderFetcherIterator
Namespace: SalesRender\Plugin\Core\Logistic\Components\OrderFetcherIterator
Iterator for fetching orders via the SalesRender GraphQL API. Extends ApiFetcherIterator. Uses the ordersFetcher GraphQL query.
BatchLockTrait
Namespace: SalesRender\Plugin\Core\Logistic\Components\Traits\BatchLockTrait
Trait providing order locking functionality for batch handlers.
| Method | Returns | Description |
|---|---|---|
lockOrder(int $timeout, int $orderId, Batch $batch) |
bool |
Lock an order via GraphQL mutation for the specified timeout (seconds) |
LogisticStatusesResolverService
Namespace: SalesRender\Plugin\Core\Logistic\Services\LogisticStatusesResolverService
Service for sorting and resolving logistic statuses.
| Method | Returns | Description |
|---|---|---|
__construct(Track $track) |
-- | Initialize with a track |
sort() |
LogisticStatus[] |
Sort statuses according to LogisticStatus::values() order, then by timestamp |
getLastStatusForNotify() |
?LogisticStatus |
Get the latest status that has not been notified yet |
Exceptions
| Exception | Thrown By | Description |
|---|---|---|
ShippingContainerException |
ShippingContainer |
Container not configured |
FulfillmentContainerException |
FulfillmentContainer |
Container not configured |
FulfillmentSyncException |
SyncAction |
Plugin not registered |
WaybillContainerException |
WaybillContainer |
Form or handler not configured |
BindingSyncException |
Binding::sync() |
Plugin not registered |
TrackException |
Track::createNotification() |
Plugin not registered |
Special Requests
| Sender | HTTP Method | Backend Endpoint | Description |
|---|---|---|---|
BatchShippingHandler (various) |
POST/PATCH/POST | /CRM/plugin/logistic/shipping/... |
Shipping lifecycle operations |
BatchFulfillmentHandler |
PUT | /CRM/plugin/logistic/fulfillment |
Fulfillment logistic updates |
SyncAction |
PUT | /CRM/plugin/logistic/fulfillment/sync |
Individual order sync results |
Binding::sync() |
PUT | /CRM/plugin/logistic/fulfillment/stock |
Stock/balance data |
Track::createNotification() |
PATCH | /CRM/plugin/logistic/status/{class} |
Tracking status notifications |
Example Plugin
A complete working example is available at salesrender/plugin-logistic-example.
The example demonstrates a shipping plugin with:
BatchShippingHandler-- full batch shipping workflow: creates shipping, iterates orders withOrderFetcherIterator, locks orders, adds orders to shipping, marks as exported/failedWaybillHandler-- creates waybills with track numbers, pricing, delivery terms, delivery type, and COD supportWaybillForm-- complete waybill form with delivery fields (track, price, terms, type, COD) and address fields (postcode, region, city, address, country, coordinates)Batch_1-- batch form with sender selection and delivery type overrideCancelActionandRemoveOrdersAction-- stub implementations of shipping cancel and order removalSettingsForm-- settings form with login/password and multiple sender configurations
Dependencies
| Package | Version | Purpose |
|---|---|---|
salesrender/plugin-core |
^0.4.0 | Base plugin framework |
salesrender/plugin-core-geocoder |
^0.3.0 | Geocoding support |
salesrender/plugin-component-logistic |
^2.1.0 | Logistic data models (Waybill, LogisticStatus with sequential status index, Logistic, etc.) |
salesrender/plugin-component-purpose |
^2.0 | Plugin type definitions (LogisticPluginClass) |
xakepehok/array-to-uuid-helper |
^0.1.0 | UUID generation from arrays (for binding change detection) |
See Also
- salesrender/plugin-core -- Base plugin framework
- salesrender/plugin-core-geocoder -- Geocoder plugin core
- salesrender/plugin-logistic-example -- Complete logistic plugin example
- salesrender/plugin-core-pbx -- PBX plugin core