Fix: Move carrier live-pricing choice onto ShippingMethod.charge_by
Reverts the earlier per-rate pricing_mode column in favor of extending Lunar's existing charge_by field (cart_total/weight) with a third "live" option, gated by a SupportsLivePricing capability check on the driver. Adds a shared ResolvesFixedPricing trait so any carrier driver can fall back to Lunar's normal price-break resolution, matching the vendor ShipBy driver's own charge_by handling instead of introducing a separate mechanism. Also fixes an incorrect Get() path in the admin form that silently hid the new "live" option.
This commit is contained in:
@@ -2,16 +2,13 @@
|
||||
|
||||
namespace Modules\Core\Shipping\Filament\Pages;
|
||||
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Lunar\Shipping\Facades\Shipping;
|
||||
use Lunar\Shipping\Filament\Resources\ShippingZoneResource\Pages\ManageShippingRates as BaseManageShippingRates;
|
||||
use Lunar\Shipping\Models\ShippingMethod;
|
||||
use Lunar\Shipping\Models\ShippingRate;
|
||||
use Lunar\Shipping\Filament\Resources\ShippingZoneResource\Pages\ManageShippingRates as BaseManageShippingRates;
|
||||
use Modules\Core\Shipping\Contracts\SupportsLivePricing;
|
||||
|
||||
/**
|
||||
* Bound in place of the vendor ManageShippingRates page via the container
|
||||
@@ -20,11 +17,13 @@ use Modules\Core\Shipping\Contracts\SupportsLivePricing;
|
||||
* ShippingZoneResource::getPages() — is untouched; the container simply
|
||||
* hands back this subclass whenever the vendor class is resolved.
|
||||
*
|
||||
* Adds a per-rate "Pricing" toggle (live API vs. fixed price) for methods
|
||||
* whose driver supports live pricing (see SupportsLivePricing). Rates on
|
||||
* methods without live pricing behave exactly as the vendor page always did
|
||||
* — no toggle shown, price fields always required, vendor save logic used
|
||||
* as-is.
|
||||
* Hides the price / price-break fields for a rate whose method has
|
||||
* charge_by = "live" (see ShippingMethodResourceExtension, which adds that
|
||||
* option to methods whose driver supports live pricing) — those fields
|
||||
* would otherwise be dead configuration the driver never reads. Pricing
|
||||
* strategy (cart_total / weight / live) stays entirely on the Shipping
|
||||
* Method, matching Lunar's own existing charge_by convention; nothing new
|
||||
* is stored on the rate itself.
|
||||
*/
|
||||
class ManageShippingRates extends BaseManageShippingRates
|
||||
{
|
||||
@@ -33,56 +32,13 @@ class ManageShippingRates extends BaseManageShippingRates
|
||||
$form = parent::form($form);
|
||||
|
||||
return $form->schema(
|
||||
$this->insertPricingModeFieldAfterShippingMethod(
|
||||
$this->hidePriceFieldsWhenLive($form->getComponents())
|
||||
)
|
||||
$this->hidePriceFieldsWhenLive($form->getComponents())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the "Pricing" field immediately after "shipping_method_id" so
|
||||
* it reads as a pair, rather than appending it elsewhere in the form.
|
||||
*/
|
||||
private function insertPricingModeFieldAfterShippingMethod(array $components): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($components as $component) {
|
||||
$result[] = $component;
|
||||
|
||||
if (method_exists($component, 'getName') && $component->getName() === 'shipping_method_id') {
|
||||
$result[] = $this->pricingModeField();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function pricingModeField(): Select
|
||||
{
|
||||
return Select::make('pricing_mode')
|
||||
->label('Pricing')
|
||||
->options([
|
||||
'live' => 'Use live API pricing',
|
||||
'fixed' => 'Use a fixed price',
|
||||
])
|
||||
->default('live')
|
||||
->live()
|
||||
->visible(fn (Get $get) => static::methodHasLivePricing($get('shipping_method_id')))
|
||||
->columnSpan(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the vendor's price / price-break fields whenever this rate is
|
||||
* set to live pricing — they'd otherwise be dead configuration,
|
||||
* silently ignored by the driver. Only the base "price" field was ever
|
||||
* required by the vendor form; the "prices" repeater (price breaks) is
|
||||
* always optional, so its required() state is left untouched.
|
||||
*/
|
||||
private function hidePriceFieldsWhenLive(array $components): array
|
||||
{
|
||||
$isFixedOrNotLiveCapable = fn (Get $get) => ! static::methodHasLivePricing($get('shipping_method_id'))
|
||||
|| $get('pricing_mode') === 'fixed';
|
||||
$isNotLive = fn (Get $get) => static::methodChargeBy($get('shipping_method_id')) !== 'live';
|
||||
|
||||
foreach ($components as $component) {
|
||||
if (! method_exists($component, 'getName')) {
|
||||
@@ -90,14 +46,11 @@ class ManageShippingRates extends BaseManageShippingRates
|
||||
}
|
||||
|
||||
if ($component->getName() === 'price') {
|
||||
$component->visible($isFixedOrNotLiveCapable)
|
||||
->required($isFixedOrNotLiveCapable)
|
||||
->dehydrated(true);
|
||||
$component->visible($isNotLive)->required($isNotLive)->dehydrated(true);
|
||||
}
|
||||
|
||||
if ($component->getName() === 'prices') {
|
||||
$component->visible($isFixedOrNotLiveCapable)
|
||||
->dehydrated(true);
|
||||
$component->visible($isNotLive)->dehydrated(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +67,7 @@ class ManageShippingRates extends BaseManageShippingRates
|
||||
return TextColumn::make('basePrices.0')
|
||||
->label(__('lunarpanel.shipping::relationmanagers.shipping_rates.table.price.label'))
|
||||
->formatStateUsing(function ($state, ShippingRate $record) {
|
||||
if (static::methodHasLivePricing($record->shipping_method_id) && $record->pricing_mode === 'live') {
|
||||
if (static::methodChargeBy($record->shipping_method_id) === 'live') {
|
||||
return 'Live API pricing';
|
||||
}
|
||||
|
||||
@@ -129,37 +82,23 @@ class ManageShippingRates extends BaseManageShippingRates
|
||||
|
||||
protected static function saveShippingRate(?ShippingRate $shippingRate = null, array $data = []): void
|
||||
{
|
||||
$isLive = static::methodHasLivePricing($data['shipping_method_id'] ?? $shippingRate?->shipping_method_id)
|
||||
&& ($data['pricing_mode'] ?? 'live') === 'live';
|
||||
|
||||
$shippingRate->pricing_mode = $isLive ? 'live' : 'fixed';
|
||||
$shippingRate->save();
|
||||
|
||||
if ($isLive) {
|
||||
if (static::methodChargeBy($data['shipping_method_id'] ?? $shippingRate?->shipping_method_id) === 'live') {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::saveShippingRate($shippingRate, $data);
|
||||
}
|
||||
|
||||
protected static function methodHasLivePricing(ShippingMethod|int|string|null $method): bool
|
||||
protected static function methodChargeBy(ShippingMethod|int|string|null $method): ?string
|
||||
{
|
||||
if (blank($method)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $method instanceof ShippingMethod) {
|
||||
$method = ShippingMethod::find($method);
|
||||
}
|
||||
|
||||
if (! $method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return Shipping::driver($method->driver) instanceof SupportsLivePricing;
|
||||
} catch (\InvalidArgumentException) {
|
||||
return false;
|
||||
}
|
||||
return $method?->data['charge_by'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user