Fix: Updates to OrderFullfilmentServices and box now clients, order views and checkout services

This commit is contained in:
2026-09-18 00:54:43 +03:00
parent dcdc998eee
commit 12aaa43f10
15 changed files with 450 additions and 18 deletions
+23 -12
View File
@@ -56,10 +56,11 @@ use Modules\Core\Shipping\Support\WeightCalculator;
* each with its own S/M/L size) instead — see
* Modules\Core\Shipping\Carriers\BoxNow\BoxNowFulfillmentService for how
* multiple boxes become multiple Shipment rows from one delivery request.
* Box Now's locker is locked to read-only once the shopper's own checkout
* selection ($order->shippingAddress->meta['box_now_locker']) is present
* — staff can only fill it in manually for the (current, checkout-UI-less)
* case where nothing set it yet.
* Box Now's locker field defaults from the shopper's own checkout
* selection ($order->shippingAddress->meta['box_now_locker']) when present,
* but stays editable — staff can override to a different locker (e.g. the
* customer's choice turns out to be unavailable) or fill it in manually for
* an order placed before the checkout locker picker existed.
*
* "Mark Paid" is a third, separate header action — Order::paid is
* independent of `status` (see OrderStatusFlow's own docblock), so it
@@ -124,16 +125,9 @@ class OrderViewExtension extends ViewPageExtension
TextInput::make('destination_location_id')
->label('Box Now locker ID')
->default($lockerId)
// Locked once the shopper's own checkout selection is
// known — staff should not be able to redirect a
// parcel to a different locker than the one the
// customer picked. Only editable for the (current,
// checkout-UI-less) case where nothing set it yet.
->disabled(filled($lockerId))
->dehydrated()
->required()
->helperText($lockerId
? 'Set by the customer at checkout.'
? 'Set by the customer at checkout — override if the parcel needs to go to a different locker.'
: 'No locker was selected at checkout — enter it manually.'),
Repeater::make('boxes')
->label('Boxes')
@@ -152,12 +146,29 @@ class OrderViewExtension extends ViewPageExtension
];
})
->action(function (Order $record, array $data, Action $action) {
// Derived from the order itself, never from staff input —
// whether a shipment collects cash on delivery is a fact
// about the order (which PaymentMethod it was placed
// against), not a choice to make again at dispatch time.
// Previously this was never set at all (defaulted to
// ShipmentRequest::$paymentMode's own null), which silently
// made AcsFulfillmentService::createShipment()'s
// `$request->paymentMode === 'cod'` branch (sends
// Cod_Ammount/Cod_Payment_Way to ACS) permanently
// unreachable, and BoxNowFulfillmentService::createShipment()
// always ship 'prepaid' with amountToBeCollected '0.00' —
// a real COD order would arrive with the carrier believing
// full payment was already settled, and never collect it.
$isCod = app(OrderStatusFlow::class)->isCod($record);
$result = $this->service()->createShipmentAndDispatch(
$record,
new ShipmentRequest(
weight: filled($data['weight'] ?? null) ? (float) $data['weight'] : null,
packageCount: (int) ($data['package_count'] ?? 1),
destinationLocationId: $data['destination_location_id'] ?? null,
paymentMode: $isCod ? 'cod' : 'prepaid',
amountToCollect: $isCod ? $record->total->decimal : null,
boxes: collect($data['boxes'] ?? [])->pluck('size')->all(),
),
);