diff --git a/CHANGELOG.md b/CHANGELOG.md index e3df1cc..b4978e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [0.29.1] - 2026-09-30 +### Fixed +- Checkout crashed ("Driver [acs] not supported", a 500) when a shipping + method pointed at a driver that is no longer registered — e.g. a carrier + commented out while its shipping method still exists in the admin. Such + methods are now left out of the options offered at checkout + (`Modules\Core\Shipping\Support\RegisteredDriverRateResolver`); the admin + is unchanged. +- The Box Now locker map at checkout only appeared when the shipping + method's **code** was exactly `box-now`; a method with any other code + (e.g. `boxnow`) never showed it, and switching options cleared the chosen + locker. Box Now is now recognized by its driver being a `BoxNowRateDriver` + (`Modules\Core\Shipping\Support\BoxNowShipping`), not by any name. The + admin order page's Create Shipment (locker + boxes form) and the + shipment's locker line use the same check. + ## [0.29.0] - 2026-09-30 ### Added - **ELTA Courier** carrier (`elta` shipping driver): rates, shipment creation, diff --git a/composer.json b/composer.json index d8c6d79..3f94515 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "boboko/core", "description": "Core module — authentication and shared panel behaviour", "type": "library", - "version": "0.29.0", + "version": "0.29.1", "autoload": { "psr-4": { "Modules\\Core\\": "src/" diff --git a/package.json b/package.json index 75a4e58..2405a86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@boboko/core", - "version": "0.29.0", + "version": "0.29.1", "private": true, "type": "module", "description": "Portable Stimulus controllers and styles for boboko-core's cart + checkout module. Installed as a real npm dependency (file:../boboko-core in dev, a tagged git install in prod) so a consuming app's `npm install` resolves this package's own dependencies (leaflet, @hotwired/stimulus) transitively, the same way `composer update boboko/*` does for PHP. See CONTRIBUTE.md's \"JS/CSS: a real npm package\" section.", diff --git a/resources/js/checkout/bbk-checkout-form-controller.js b/resources/js/checkout/bbk-checkout-form-controller.js index 161b49a..a1bf650 100644 --- a/resources/js/checkout/bbk-checkout-form-controller.js +++ b/resources/js/checkout/bbk-checkout-form-controller.js @@ -123,7 +123,7 @@ export default class extends Controller { } async selectShipping(event) { - this.toggleBoxNowLocker(event.target.value) + this.toggleBoxNowLocker(event.target.dataset.boxNow === 'true') // Tracked so flush() can await it — nothing else stops "place order" // (a separate, unrelated click) from racing ahead of this request. @@ -136,15 +136,17 @@ export default class extends Controller { // re-renders wholesale on every shipping-option change — so its own // Stimulus controller reconnects fresh each time and has no memory of // which option was previously selected. This is the one place that - // knows the newly-chosen option's identifier, so it also owns - // showing/hiding the picker. - toggleBoxNowLocker(identifier) { + // knows the newly-chosen option, so it also owns showing/hiding the + // picker. Whether an option is Box Now comes from the server + // (data-box-now, by the method's driver) — the option's value is the + // merchant-typed method code, which needn't be "box-now". + toggleBoxNowLocker(isBoxNow) { const picker = this.shippingOptionsTarget.querySelector('#bbk-box-now-locker') if (!picker) return const controller = this.application.getControllerForElementAndIdentifier(picker, 'bbk-box-now-locker') - if (identifier === 'box-now') { + if (isBoxNow) { controller?.show() } else { controller?.hide() diff --git a/resources/views/checkout/partials/shipping-options.blade.php b/resources/views/checkout/partials/shipping-options.blade.php index 9030c5e..5f1db6a 100644 --- a/resources/views/checkout/partials/shipping-options.blade.php +++ b/resources/views/checkout/partials/shipping-options.blade.php @@ -8,6 +8,8 @@ $shippingAddress, $shippingOptions come from the controller / page scope. --}} @php($selected = $shippingAddress?->shipping_option) +{{-- Box Now by the method's driver, not its (merchant-typed) code --}} +@php($boxNowCodes = \Modules\Core\Shipping\Support\BoxNowShipping::codes()) {{-- Rate resolution needs country (always Greece here) + postcode; until a postcode is saved there's nothing to quote against yet. --}} @@ -34,6 +36,7 @@ type="radio" name="shipping_option" value="{{ $option->identifier }}" + data-box-now="{{ in_array($option->identifier, $boxNowCodes, true) ? 'true' : 'false' }}" @checked($selected === $option->identifier) data-action="change->bbk-checkout-form#selectShipping" > @@ -73,7 +76,7 @@ data-bbk-box-now-locker-select-label-value="{{ __('checkout.page.box_now_locker_select') }}" data-bbk-box-now-locker-selected-label-value="{{ __('checkout.page.box_now_locker_selected') }}" data-bbk-box-now-locker-no-results-value="{{ __('checkout.page.box_now_locker_no_results') }}" - @if ($selected !== 'box-now') hidden @endif + @if (! in_array($selected, $boxNowCodes, true)) hidden @endif >
{{ __('checkout.page.box_now_locker_label') }}
diff --git a/src/Checkout/Services/CheckoutService.php b/src/Checkout/Services/CheckoutService.php
index cb5bbc3..a379e59 100644
--- a/src/Checkout/Services/CheckoutService.php
+++ b/src/Checkout/Services/CheckoutService.php
@@ -29,6 +29,7 @@ use Modules\Core\Payment\Models\PaymentMethod;
use Modules\Core\Payment\Services\PaymentDriverRegistry;
use Modules\Core\Payment\Services\PaymentMethodCache;
use Modules\Core\Shipping\Contracts\SupportsCashCollection;
+use Modules\Core\Shipping\Support\BoxNowShipping;
use Modules\Core\Shipping\Support\FulfillmentType;
/**
@@ -181,7 +182,7 @@ class CheckoutService
// switches BACK to Box Now and it resurfaces as if still chosen,
// possibly for a locker that no longer exists/fits. Cleared here,
// the one place that knows the method just changed.
- if ($identifier !== 'box-now') {
+ if (! BoxNowShipping::isOption($identifier)) {
$address = $cart->shippingAddress;
if ($address && isset($address->meta['box_now_locker'])) {
diff --git a/src/Shipping/Extensions/OrderShipmentsExtension.php b/src/Shipping/Extensions/OrderShipmentsExtension.php
index 1c74d5d..328edde 100644
--- a/src/Shipping/Extensions/OrderShipmentsExtension.php
+++ b/src/Shipping/Extensions/OrderShipmentsExtension.php
@@ -19,6 +19,7 @@ use Modules\Core\Shipping\Enums\TrackingStatus;
use Modules\Core\Shipping\Events\ShipmentStatusUpdatedByCarrier;
use Modules\Core\Shipping\Filament\Resources\ShipmentResource;
use Modules\Core\Shipping\Models\Shipment;
+use Modules\Core\Shipping\Support\BoxNowShipping;
/**
* Adds a "Shipments" section to the order page's main column — previously
@@ -224,7 +225,7 @@ class OrderShipmentsExtension extends ViewPageExtension
{
$parts = ['Created '.$record->created_at->format('Y-m-d H:i')];
- if ($record->carrier === 'box-now' && $locker = $record->meta['locker_id'] ?? null) {
+ if (BoxNowShipping::isDriver($record->carrier) && $locker = $record->meta['locker_id'] ?? null) {
$parts[] = 'Locker '.$locker;
}
diff --git a/src/Shipping/Extensions/OrderViewExtension.php b/src/Shipping/Extensions/OrderViewExtension.php
index 7b46f1d..1ce4451 100644
--- a/src/Shipping/Extensions/OrderViewExtension.php
+++ b/src/Shipping/Extensions/OrderViewExtension.php
@@ -25,6 +25,7 @@ use Modules\Core\Shipping\Contracts\SupportsVoucherLookup;
use Modules\Core\Shipping\DTOs\ShipmentRequest;
use Modules\Core\Shipping\Enums\ExtraService;
use Modules\Core\Shipping\Models\Shipment;
+use Modules\Core\Shipping\Support\BoxNowShipping;
use Modules\Core\Shipping\Support\WeightCalculator;
use Throwable;
@@ -122,7 +123,7 @@ class OrderViewExtension extends ViewPageExtension
->modalSubmitActionLabel('Create Shipment')
->schema(function (Order $record) {
$carrier = $this->service()->carrierFor($record);
- $isBoxNow = $carrier === 'box-now';
+ $isBoxNow = BoxNowShipping::isDriver($carrier);
$lockerId = $record->shippingAddress?->meta['box_now_locker']['locationId'] ?? null;
if ($carrier === 'manual') {
diff --git a/src/Shipping/Support/BoxNowShipping.php b/src/Shipping/Support/BoxNowShipping.php
new file mode 100644
index 0000000..2b52e12
--- /dev/null
+++ b/src/Shipping/Support/BoxNowShipping.php
@@ -0,0 +1,53 @@
+first());
+ }
+
+ public static function isMethod(?ShippingMethod $method): bool
+ {
+ return self::isDriver($method?->driver);
+ }
+
+ /**
+ * A shipping driver key (a method's `driver`, a shipment's `carrier`)
+ * that resolves to Box Now. getSupportedDrivers() rather than
+ * Shipping::driver(), which throws for a key no longer registered.
+ */
+ public static function isDriver(?string $driver): bool
+ {
+ return $driver !== null
+ && Shipping::getSupportedDrivers()->get($driver) instanceof BoxNowRateDriver;
+ }
+
+ /**
+ * Codes of every Box Now shipping method — for marking options in bulk.
+ *
+ * @return array