Fix: Updates to OrderFullfilmentServices and box now clients, order views and checkout services
This commit is contained in:
@@ -5,6 +5,8 @@ namespace Modules\Core\Shipping\Extensions;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Infolists\Components\RepeatableEntry;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Illuminate\Support\Collection;
|
||||
use Modules\Core\Shipping\Models\ShipmentInfo;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
@@ -112,10 +114,34 @@ class OrderShipmentsExtension extends ViewPageExtension
|
||||
->action(fn (Shipment $record) => $this->cancel($record))
|
||||
->visible(fn (Shipment $record) => ! $record->cancelled_at),
|
||||
]),
|
||||
RepeatableEntry::make('shipmentInfo')
|
||||
->label('Tracking history')
|
||||
->state(fn (Shipment $record) => $this->orderedCheckpoints($record))
|
||||
->hidden(fn (Shipment $record) => $record->shipmentInfo->isEmpty())
|
||||
->schema([
|
||||
TextEntry::make('status')
|
||||
->label(fn (ShipmentInfo $record) => $record->occurred_at->format('Y-m-d H:i'))
|
||||
->inlineLabel()
|
||||
->state(fn (ShipmentInfo $record) => (string) str($record->status->value)->replace('_', ' ')->title())
|
||||
->helperText(fn (ShipmentInfo $record) => $record->location),
|
||||
]),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Oldest first — a delivery journey (Collected → In Transit →
|
||||
* Delivered) reads naturally top-to-bottom in that order, unlike
|
||||
* statusLabel()/statusColor() above which only ever need the single
|
||||
* latest checkpoint and so use latestShipmentInfo() directly instead.
|
||||
*
|
||||
* @return Collection<int, ShipmentInfo>
|
||||
*/
|
||||
private function orderedCheckpoints(Shipment $record): Collection
|
||||
{
|
||||
return $record->shipmentInfo->sortBy('occurred_at')->values();
|
||||
}
|
||||
|
||||
private function carrierLabel(Shipment $record): string
|
||||
{
|
||||
return match ($record->carrier) {
|
||||
|
||||
@@ -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(),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user