Domain model component for logistics plugins in the SalesRender ecosystem. Provides value objects and data structures for shipments, waybills, delivery tracking, status management, pickup offices, and delivery terms.
composer require salesrender/plugin-component-logistic| Requirement | Version |
|---|---|
| PHP | >= 7.4 |
| ext-json | * |
| ext-mbstring | * |
| xakepehok/enum-helper | ^0.1.0 |
| salesrender/component-address | ^1.0.0 |
| spatie/opening-hours | ^2.10 |
| xakepehok/value-object-builder | ^0.1.2 |
| php-dto/uri | ^0.1.0 |
This package defines the core domain types used by all SalesRender logistics plugins. It is used both when creating waybills (initial shipment registration) and when tracking delivery statuses over time. The component is consumed by plugin-core-logistic and by individual carrier plugins (CDEK, Nova Poshta, Belpost, InPost, and many others).
Namespace: SalesRender\Plugin\Components\Logistic
Top-level aggregate that combines a waybill with its current status and optional arbitrary data.
| Method | Return Type | Description |
|---|---|---|
__construct(Waybill $waybill, LogisticStatus $status, ?array $data = null) |
Creates a logistic record. Throws LogisticDataTooBigException if $data exceeds 2048 bytes. |
|
getWaybill() |
Waybill |
Returns the waybill. |
setWaybill(Waybill $waybill) |
void |
Replaces the waybill. |
getStatus() |
LogisticStatus |
Returns the current status. |
setStatus(LogisticStatus $status) |
void |
Replaces the current status. |
getData() |
?array |
Returns optional arbitrary data (max 2 KB serialized). |
setData(?array $data) |
void |
Sets arbitrary data. Throws LogisticDataTooBigException if over 2048 bytes. |
Namespace: SalesRender\Plugin\Components\Logistic
Represents a shipment status at a point in time. Extends EnumHelper and implements JsonSerializable.
| Method | Return Type | Description |
|---|---|---|
__construct(int $code, string $text = '', ?int $timestamp = null, ?LogisticOffice $office = null) |
Creates a status. Validates code against enum. Throws LogisticStatusTooLongException if text > 250 chars. Timestamp defaults to time(). |
|
getTimestamp() |
int |
Unix timestamp of this status event. |
getCode() |
int |
Numeric status code (see table below). |
getText() |
?string |
Human-readable status description. |
getHash() |
string |
MD5 hash of the serialized status, used for deduplication. |
getOffice() |
?LogisticOffice |
Office/pickup point associated with this status. |
values() |
array |
Returns all valid status codes. |
code2strings() |
array |
Returns associative array mapping codes to string names. |
jsonSerialize() |
array |
Returns ['timestamp', 'code', 'text', 'office']. |
| Constant | Code | Description |
|---|---|---|
UNREGISTERED |
-1 |
Shipment is not registered or has been deleted in the carrier system. |
CREATED |
1 |
Waybill created in the plugin but not yet sent to the carrier. |
REGISTERED |
50 |
Registered with the carrier (tracking number assigned). |
ACCEPTED |
100 |
Accepted at the carrier warehouse / customs processing. |
PACKED |
150 |
Shipment packed and ready for dispatch. |
IN_TRANSIT |
200 |
In transit between cities or warehouses. |
ARRIVED |
300 |
Arrived at the destination city warehouse or pickup point. |
ON_DELIVERY |
400 |
Out for delivery by courier. |
PENDING |
450 |
Delivery attempt failed (recipient absent, rescheduled, etc.). |
DELIVERED |
500 |
Successfully delivered to the recipient. |
PAID |
550 |
Delivered and cash-on-delivery payment collected. |
RETURNED |
600 |
Shipment returned (recipient refused, undeliverable). |
RETURNING_TO_SENDER |
650 |
Shipment is in transit back to the sender. |
DELIVERED_TO_SENDER |
699 |
Return shipment delivered back to the sender. |
UNKNOWN |
1000 |
Status could not be mapped to any known code. |
Namespace: SalesRender\Plugin\Components\Logistic\Waybill
Immutable value object representing a shipping waybill. Setters return a cloned instance (immutable pattern).
| Method | Return Type | Description |
|---|---|---|
__construct(?Track $track = null, ?float $shippingCost = null, ?DeliveryTerms $deliveryTerms = null, ?DeliveryType $deliveryType = null, ?bool $cod = null) |
Creates a waybill. Throws NegativePriceException if shipping cost is negative. |
|
getTrack() |
?Track |
Returns the tracking number. |
setTrack(?Track $track) |
Waybill |
Returns a new Waybill with the updated track. |
getShippingCost() |
?float |
Shipping cost (in the currency's base unit). |
setShippingCost(?float $shippingCost) |
Waybill |
Returns a new Waybill with the updated cost. Throws NegativePriceException if negative. |
getDeliveryTerms() |
?DeliveryTerms |
Estimated delivery time range. |
setDeliveryTerms(?DeliveryTerms $deliveryTerms) |
Waybill |
Returns a new Waybill with updated terms. |
getDeliveryType() |
?DeliveryType |
Type of delivery. |
setDeliveryType(?DeliveryType $deliveryType) |
Waybill |
Returns a new Waybill with updated type. |
isCod() |
?bool |
Whether cash-on-delivery is enabled. |
setCod(?bool $cod) |
Waybill |
Returns a new Waybill with updated COD flag. |
jsonSerialize() |
array |
Returns the waybill as an associative array. |
createFromArray(array $data) |
Waybill |
Static factory that reconstructs a Waybill from a serialized array. |
Namespace: SalesRender\Plugin\Components\Logistic\Waybill
Value object for a tracking number.
| Method | Return Type | Description |
|---|---|---|
__construct(string $track) |
Validates: 6-36 chars, only A-Z, 0-9, -, _. Throws LogisticTrackException on invalid input. |
|
get() |
string |
Returns the tracking number. |
__toString() |
string |
Returns the tracking number as a string. |
jsonSerialize() |
string |
Returns the tracking number. |
Namespace: SalesRender\Plugin\Components\Logistic\Waybill
Value object for estimated delivery time range in hours.
| Method | Return Type | Description |
|---|---|---|
__construct(int $minHours, int $maxHours) |
Validates: $minHours >= 0, $maxHours <= 8760 (1 year), $minHours <= $maxHours. Throws DeliveryTermsException. |
|
getMinHours() |
int |
Minimum delivery time in hours. |
getMaxHours() |
int |
Maximum delivery time in hours. |
jsonSerialize() |
array |
Returns ['minHours' => ..., 'maxHours' => ...]. |
Namespace: SalesRender\Plugin\Components\Logistic\Waybill
Enum value object for the delivery method. Extends EnumHelper.
| Constant | Value | Description |
|---|---|---|
SELF_PICKUP |
'SELF_PICKUP' |
Sender delivers to carrier themselves. |
PICKUP_POINT |
'PICKUP_POINT' |
Recipient picks up at a designated point. |
COURIER |
'COURIER' |
Courier delivery to the recipient's address. |
| Method | Return Type | Description |
|---|---|---|
__construct(string $type) |
Validates against allowed values. Throws OutOfEnumException. |
|
get() |
string |
Returns the delivery type string. |
getAsString() |
string |
Returns the type as a readable string. |
values() |
array |
Returns ['SELF_PICKUP', 'PICKUP_POINT', 'COURIER']. |
jsonSerialize() |
string |
Returns the delivery type string. |
Namespace: SalesRender\Plugin\Components\Logistic
Represents a carrier office or pickup point with address, phone numbers, and opening hours.
| Method | Return Type | Description |
|---|---|---|
__construct(?Address $address, array $phones, ?OpeningHours $openingHours) |
Validates phone numbers against pattern ^\+?\d{9,16}$. Throws LogisticOfficePhoneException. |
|
getAddress() |
?Address |
Returns the office address. |
getPhones() |
array |
Returns the list of phone numbers. |
getOpeningHours() |
?OpeningHours |
Returns opening hours schedule. |
jsonSerialize() |
array |
Returns ['address', 'phones', 'openingHours']. |
createFromArray(?array $data) |
?self |
Static factory that reconstructs from an array. Returns null if input is null. |
Namespace: SalesRender\Plugin\Components\Logistic\Components
Wrapper around spatie/opening-hours for office schedule validation.
| Method | Return Type | Description |
|---|---|---|
__construct(array $schedule) |
Validates schedule via Spatie\OpeningHours. Throws OpeningHoursException. |
|
getSchedule() |
array |
Returns the validated schedule array. |
jsonSerialize() |
array |
Returns the schedule. |
Namespace: SalesRender\Plugin\Components\Logistic\Components
Represents a file attachment (label, invoice, etc.) associated with a shipment.
| Method | Return Type | Description |
|---|---|---|
__construct(string $name, Uri $uri) |
Validates name: 1-255 characters. Throws ShippingAttachmentException. |
|
getName() |
string |
Returns the attachment name. |
getUri() |
Uri |
Returns the attachment URI. |
createFromArray(array $data) |
self |
Static factory from ['name' => ..., 'uri' => ...]. |
jsonSerialize() |
array |
Returns ['name' => ..., 'uri' => ...]. |
| Exception | Thrown By | Condition |
|---|---|---|
DeliveryTermsException |
DeliveryTerms |
Invalid min/max hours (negative, exceeds 8760, or min > max). |
LogisticDataTooBigException |
Logistic |
Data array exceeds 2048 bytes when serialized. |
LogisticOfficePhoneException |
LogisticOffice |
Phone number does not match ^\+?\d{9,16}$. |
LogisticStatusTooLongException |
LogisticStatus |
Status text exceeds 250 characters. |
LogisticTrackException |
Track |
Track number invalid (not 6-36 chars or contains disallowed characters). |
NegativePriceException |
Waybill |
Shipping cost is negative. |
OpeningHoursException |
OpeningHours |
Invalid schedule format (Spatie validation). |
ShippingAttachmentException |
ShippingAttachment |
Attachment name is empty or exceeds 255 characters. |
Taken from plugin-logistic-example (WaybillHandler):
use SalesRender\Plugin\Components\Logistic\Logistic;
use SalesRender\Plugin\Components\Logistic\LogisticStatus;
use SalesRender\Plugin\Components\Logistic\Waybill\DeliveryTerms;
use SalesRender\Plugin\Components\Logistic\Waybill\DeliveryType;
use SalesRender\Plugin\Components\Logistic\Waybill\Track;
use SalesRender\Plugin\Components\Logistic\Waybill\Waybill;
// Create a tracking number
$track = new Track($data->get('waybill.track'));
// Build delivery terms (hours)
$terms = new DeliveryTerms(24, 72); // 1-3 days
// Create the waybill
$waybill = new Waybill(
$track,
$price, // shipping cost
$terms, // delivery terms
new DeliveryType(DeliveryType::PICKUP_POINT), // delivery type
true // cash on delivery
);
// Build the logistic aggregate
$logistic = new Logistic(
$waybill,
new LogisticStatus(LogisticStatus::CREATED, 'Waybill created')
);Taken from plugin-logistic-cdek (TrackingHelper):
use SalesRender\Plugin\Components\Logistic\LogisticStatus;
// Map CDEK API statuses to LogisticStatus codes
const STATUS_CODES = [
'CREATED' => LogisticStatus::REGISTERED,
'ACCEPTED' => LogisticStatus::ACCEPTED,
'RECEIVED_AT_SHIPMENT_WAREHOUSE' => LogisticStatus::ACCEPTED,
'SENT_TO_RECIPIENT_CITY' => LogisticStatus::IN_TRANSIT,
'ACCEPTED_AT_RECIPIENT_CITY_WAREHOUSE' => LogisticStatus::ARRIVED,
'TAKEN_BY_COURIER' => LogisticStatus::ON_DELIVERY,
'DELIVERED' => LogisticStatus::DELIVERED,
'NOT_DELIVERED' => LogisticStatus::RETURNED,
];
// Create a status from tracking data
$status = new LogisticStatus(
self::STATUS_CODES[$apiStatusCode],
mb_substr($statusText, 0, 255),
(new DateTime($statusDate))->getTimestamp(),
$logisticOffice
);Taken from plugin-logistic-cdek (TrackingHelper):
use SalesRender\Components\Address\Address;
use SalesRender\Plugin\Components\Logistic\Components\OpeningHours;
use SalesRender\Plugin\Components\Logistic\LogisticOffice;
$openingHours = new OpeningHours([
'monday' => ['09:00-18:00'],
'tuesday' => ['09:00-18:00'],
'wednesday' => ['09:00-18:00'],
'thursday' => ['09:00-18:00'],
'friday' => ['09:00-18:00'],
'saturday' => ['10:00-15:00'],
'sunday' => [],
]);
$office = new LogisticOffice(
new Address('', 'Moscow', '123 Main St'),
['+79001234567'],
$openingHours
);Taken from plugin-logistic-novaposhta (WaybillHandler):
use SalesRender\Plugin\Components\Logistic\Logistic;
use SalesRender\Plugin\Components\Logistic\LogisticStatus;
use SalesRender\Plugin\Components\Logistic\Waybill\Track;
use SalesRender\Plugin\Components\Logistic\Waybill\Waybill;
$track = null;
if (!empty($trackNumber)) {
$track = new Track($trackNumber);
}
$waybill = new Waybill($track);
$logistic = new Logistic(
$waybill,
new LogisticStatus(LogisticStatus::CREATED, 'Waybill created'),
$deliveryData // optional metadata (max 2 KB)
);Taken from plugin-logistic-novaposhta (TrackingCommand):
use SalesRender\Plugin\Components\Logistic\LogisticStatus;
// Status mapping table
const TRACKING_STATUSES_TABLE = [
1 => ['code' => LogisticStatus::REGISTERED, 'text' => 'Sender created the waybill'],
4 => ['code' => LogisticStatus::IN_TRANSIT, 'text' => 'Shipment en route to city'],
7 => ['code' => LogisticStatus::ARRIVED, 'text' => 'Arrived at branch'],
9 => ['code' => LogisticStatus::DELIVERED, 'text' => 'Shipment received'],
11 => ['code' => LogisticStatus::PAID, 'text' => 'Delivered, COD payment issued'],
103 => ['code' => LogisticStatus::RETURNED, 'text' => 'Recipient refused the shipment'],
];
// Create a status and add it to the track
$status = new LogisticStatus(
$statusMapping['code'],
$statusDescription,
$statusTimestamp
);
$track->addStatus($status);
$track->save();Taken from plugin-logistic-example (BatchShippingHandler):
use SalesRender\Plugin\Components\Logistic\LogisticStatus;
use SalesRender\Plugin\Components\Logistic\Waybill\DeliveryType;
$data[$orderId] = [
'waybill' => [
'price' => rand(100, 350) * 100,
'track' => 'TN' . rand(10000000, 99999999),
'deliveryTerms' => [
'minHours' => rand(1, 6),
'maxHours' => rand(6, 72),
],
'deliveryType' => DeliveryType::values()[rand(0, count(DeliveryType::values()) - 1)],
'cod' => (bool) rand(0, 1),
],
'status' => [
'code' => LogisticStatus::CREATED,
'text' => 'Created status',
'timestamp' => time(),
'office' => null,
],
];| Package | Purpose |
|---|---|
salesrender/component-address |
Address and Location value objects |
xakepehok/enum-helper |
Base class for enum types (LogisticStatus, DeliveryType) |
xakepehok/value-object-builder |
Factory for building value objects from arrays |
spatie/opening-hours |
Opening hours validation |
php-dto/uri |
URI value object for attachments |
- salesrender/plugin-core-logistic -- core logistic plugin infrastructure (Track model, WaybillHandler, BatchShippingHandler)
- salesrender/component-address -- Address and Location value objects
- salesrender/plugin-component-special-request -- used by Track to send status notification requests
- salesrender/plugin-component-batch -- batch processing infrastructure used by shipping handlers