sed -n '1,320p' tests/Feature/Admin/SettingsManagementTest.php

<?php

use App\Enums\ShippingRateType;
use App\Enums\StoreUserRole;
use App\Enums\TaxMode;
use App\Livewire\Admin\Settings\Checkout as AdminSettingsCheckout;
use App\Livewire\Admin\Settings\Index as AdminSettingsIndex;
use App\Livewire\Admin\Settings\Notifications as AdminSettingsNotifications;
use App\Livewire\Admin\Settings\Shipping as AdminSettingsShipping;
use App\Livewire\Admin\Settings\Taxes as AdminSettingsTaxes;
use App\Models\ShippingRate;
use App\Models\ShippingZone;
use App\Models\Store;
use App\Models\StoreDomain;
use App\Models\StoreSettings;
use App\Models\TaxSettings;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    $this->withoutVite();
    $this->seed(DatabaseSeeder::class);
});

function adminSettingsStore(): Store
{
    $store = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    app()->instance('current_store', $store);

    return $store;
}

function adminSettingsUser(): User
{
    return User::query()->where('email', 'admin@acme.test')->firstOrFail();
}

function adminSettingsUserWithRole(Store $store, StoreUserRole $role): User
{
    $user = User::factory()->create();
    $user->stores()->attach($store->getKey(), [
        'role' => $role->value,
        'created_at' => now(),
    ]);

    return $user;
}

test('settings routes render for owners and reject staff', function (): void {
    $store = adminSettingsStore();
    $owner = adminSettingsUser();
    $staff = adminSettingsUserWithRole($store, StoreUserRole::Staff);

    foreach (['/admin/settings', '/admin/settings/shipping', '/admin/settings/taxes', '/admin/settings/checkout', '/admin/settings/notifications'] as $path) {
        $this->actingAs($owner)
            ->withSession(['current_store_id' => $store->getKey()])
            ->get($path)
            ->assertSuccessful();

        $this->actingAs($staff)
            ->withSession(['current_store_id' => $store->getKey()])
            ->get($path)
            ->assertForbidden();
    }
});

test('general settings update store defaults and domains', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsIndex::class)
        ->set('storeName', 'Acme Atelier')
        ->set('defaultCurrency', 'GBP')
        ->set('defaultLocale', 'de')
        ->set('timezone', 'Europe/Berlin')
        ->set('announcementEnabled', true)
        ->set('announcementText', 'Spring edits now live')
        ->set('guestCheckoutEnabled', false)
        ->call('save')
        ->assertHasNoErrors()
        ->set('newHostname', 'atelier.test')
        ->set('newType', 'storefront')
        ->call('addDomain')
        ->assertHasNoErrors();

    $settings = StoreSettings::query()->whereKey($store->getKey())->firstOrFail();

    expect($store->refresh()->name)->toBe('Acme Atelier')
        ->and($store->default_currency)->toBe('GBP')
        ->and($store->default_locale)->toBe('de')
        ->and($settings->settings_json['announcement']['text'])->toBe('Spring edits now live')
        ->and($settings->settings_json['checkout']['guest_checkout_enabled'])->toBeFalse()
        ->and(StoreDomain::query()->where('hostname', 'atelier.test')->where('store_id', $store->getKey())->exists())->toBeTrue();
});

test('shipping settings manage zones rates and address tests', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsShipping::class)
        ->set('zoneName', 'Nordics')
        ->set('zoneCountries', 'SE, NO')
        ->call('saveZone')
        ->assertHasNoErrors();

    $zone = ShippingZone::withoutGlobalScopes()
        ->where('store_id', $store->getKey())
        ->where('name', 'Nordics')
        ->firstOrFail();

    Livewire::actingAs($user)
        ->test(AdminSettingsShipping::class)
        ->call('addRate', $zone->getKey())
        ->set('rateName', 'Nordic Standard')
        ->set('rateType', ShippingRateType::Flat->value)
        ->set('rateAmount', '12.50')
        ->call('saveRate')
        ->assertHasNoErrors()
        ->set('testCountry', 'SE')
        ->call('testShippingAddress')
        ->assertSet('testResult.zone', 'Nordics');

    $rate = ShippingRate::withoutGlobalScopes()
        ->where('zone_id', $zone->getKey())
        ->where('name', 'Nordic Standard')
        ->firstOrFail();

    expect($zone->countries_json)->toBe(['SE', 'NO'])
        ->and($rate->type)->toBe(ShippingRateType::Flat)
        ->and($rate->config_json['amount'])->toBe(1250);
});

test('checkout settings save customer and payment policy values', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsCheckout::class)
        ->set('guestCheckoutEnabled', false)
        ->set('customerAccountsRequired', true)
        ->set('phoneNumberRequired', true)
        ->set('billingAddressEnabled', false)
        ->set('orderNotesEnabled', false)
        ->set('termsRequired', true)
        ->set('termsUrl', 'https://shop.test/pages/terms')
        ->set('paymentHoldHours', 36)
        ->set('abandonedCheckoutDays', 21)
        ->set('bankTransferCancelDays', 9)
        ->call('save')
        ->assertHasNoErrors();

    $settings = StoreSettings::query()->whereKey($store->getKey())->firstOrFail()->settings_json;

    expect(data_get($settings, 'checkout.guest_checkout_enabled'))->toBeFalse()
        ->and(data_get($settings, 'checkout.customer_accounts_required'))->toBeTrue()
        ->and(data_get($settings, 'checkout.phone_number_required'))->toBeTrue()
        ->and(data_get($settings, 'checkout.billing_address_enabled'))->toBeFalse()
        ->and(data_get($settings, 'checkout.order_notes_enabled'))->toBeFalse()
        ->and(data_get($settings, 'checkout.terms_required'))->toBeTrue()
        ->and(data_get($settings, 'checkout.terms_url'))->toBe('https://shop.test/pages/terms')
        ->and(data_get($settings, 'checkout.payment_hold_hours'))->toBe(36)
        ->and(data_get($settings, 'checkout.abandoned_checkout_days'))->toBe(21)
        ->and(data_get($settings, 'bank_transfer_cancel_days'))->toBe(9);
});

test('notification settings save sender and event preferences', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsNotifications::class)
        ->set('senderName', 'Acme Ops')
        ->set('senderEmail', 'ops@shop.test')
        ->set('replyToEmail', 'support@shop.test')
        ->set('orderConfirmationEnabled', false)
        ->set('shippingConfirmationEnabled', true)
        ->set('refundConfirmationEnabled', false)
        ->set('adminOrderAlertsEnabled', false)
        ->set('lowStockAlertsEnabled', true)
        ->set('lowStockThreshold', 3)
        ->call('save')
        ->assertHasNoErrors();

    $settings = StoreSettings::query()->whereKey($store->getKey())->firstOrFail()->settings_json;

    expect(data_get($settings, 'notifications.sender_name'))->toBe('Acme Ops')
        ->and(data_get($settings, 'notifications.sender_email'))->toBe('ops@shop.test')
        ->and(data_get($settings, 'notifications.reply_to_email'))->toBe('support@shop.test')
        ->and(data_get($settings, 'notifications.order_confirmation_enabled'))->toBeFalse()
        ->and(data_get($settings, 'notifications.shipping_confirmation_enabled'))->toBeTrue()
        ->and(data_get($settings, 'notifications.refund_confirmation_enabled'))->toBeFalse()
        ->and(data_get($settings, 'notifications.admin_order_alerts_enabled'))->toBeFalse()
        ->and(data_get($settings, 'notifications.low_stock_alerts_enabled'))->toBeTrue()
        ->and(data_get($settings, 'notifications.low_stock_threshold'))->toBe(3);
});

test('tax settings save manual and provider configuration', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsTaxes::class)
        ->set('manualRates', [
            ['country' => 'DE', 'name' => 'VAT', 'rate_percentage' => '19.00'],
            ['country' => 'FR', 'name' => 'TVA', 'rate_percentage' => '20.00'],
        ])
        ->set('pricesIncludeTax', true)
        ->call('save')
        ->assertHasNoErrors();

    $manual = TaxSettings::withoutGlobalScopes()->whereKey($store->getKey())->firstOrFail();

    expect($manual->mode)->toBe(TaxMode::Manual)
        ->and($manual->prices_include_tax)->toBeTrue()
        ->and($manual->config_json['rates'][1]['country'])->toBe('FR')
        ->and($manual->config_json['rates'][1]['rate_bps'])->toBe(2000);

    Livewire::actingAs($user)
        ->test(AdminSettingsTaxes::class)
        ->set('mode', TaxMode::Provider->value)
        ->set('provider', 'stripe_tax')
        ->set('providerApiKey', 'sk_test_tax')
        ->call('save')
        ->assertHasNoErrors();

    $provider = TaxSettings::withoutGlobalScopes()->whereKey($store->getKey())->firstOrFail();

    expect($provider->mode)->toBe(TaxMode::Provider)
        ->and($provider->provider)->toBe('stripe_tax')
        ->and($provider->config_json['provider_api_key'])->toBe('sk_test_tax');
});


Output:
Chunk ID: deab43
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1952
Output:

            <flux:heading size="xl">Store Settings</flux:heading>
            <flux:text class="mt-1">Store defaults, checkout preferences, and domains.</flux:text>

            <flux:button :href="route('admin.settings.index')" wire:navigate variant="primary">General</flux:button>
            <flux:button :href="route('admin.settings.shipping')" wire:navigate variant="filled">Shipping</flux:button>
            <flux:button :href="route('admin.settings.taxes')" wire:navigate variant="filled">Taxes</flux:button>
            <flux:button :href="route('admin.settings.checkout')" wire:navigate variant="filled">Checkout</flux:button>
            <flux:button :href="route('admin.settings.notifications')" wire:navigate variant="filled">Notifications</flux:button>

    @if (session('status'))
        <flux:callout color="green" icon="check-circle">{{ session('status') }}</flux:callout>
    @endif

    <form wire:submit="save" class="space-y-6">

                    <flux:heading size="lg">Store details</flux:heading>
                    <flux:text class="mt-1">Basic storefront identity.</flux:text>

                    <flux:input wire:model="storeName" label="Store name" />
                    <flux:input wire:model="storeHandle" label="Store handle" disabled />
                    <flux:error name="storeName" />

            <flux:separator />

                    <flux:heading size="lg">Defaults</flux:heading>
                    <flux:text class="mt-1">Currency, language, and timezone.</flux:text>

                    <flux:select wire:model="defaultCurrency" label="Default currency">
                        @foreach ($currencyOptions as $currency)
                            <flux:select.option value="{{ $currency }}">{{ $currency }}</flux:select.option>
                        @endforeach
                    </flux:select>

                    <flux:select wire:model="defaultLocale" label="Default locale">
                        @foreach ($localeOptions as $locale => $label)
                            <flux:select.option value="{{ $locale }}">{{ $label }}</flux:select.option>
                        @endforeach
                    </flux:select>

                    <flux:select wire:model="timezone" label="Timezone">
                        @foreach ($timezoneOptions as $timezoneOption)
                            <flux:select.option value="{{ $timezoneOption }}">{{ $timezoneOption }}</flux:select.option>
                        @endforeach
                    </flux:select>

            <flux:separator />

                    <flux:heading size="lg">Checkout</flux:heading>
                    <flux:text class="mt-1">Customer-facing storefront defaults.</flux:text>

                    <flux:switch wire:model="announcementEnabled" label="Announcement bar" align="left" />
                    <flux:input wire:model="announcementText" label="Announcement text" />
                    <flux:checkbox wire:model="guestCheckoutEnabled" label="Guest checkout enabled" />

                <flux:button type="submit" variant="primary" wire:loading.attr="disabled">
                    Save settings
                    Saving...
                </flux:button>

    </form>

                <flux:heading size="lg">Domains</flux:heading>
                <flux:text class="mt-1">Hostnames connected to this store.</flux:text>

                <flux:input wire:model="newHostname" label="Hostname" placeholder="shop.example.com" />
                <flux:select wire:model="newType" label="Type">
                    <flux:select.option value="storefront">Storefront</flux:select.option>
                    <flux:select.option value="admin">Admin</flux:select.option>
                    <flux:select.option value="api">API</flux:select.option>
                </flux:select>

                    <flux:button type="button" wire:click="addDomain" variant="primary" icon="plus">Add</flux:button>

                        Hostname
                        Type
                        Primary
                        TLS
                        Actions

                    @foreach ($domains as $domain)

getKey() }}">
                            {{ $domain->hostname }}
                            <flux:badge>{{ Str::headline($domain->type->value) }}</flux:badge>

                                @if ($domain->is_primary)
                                    <flux:badge color="green">Primary</flux:badge>
                                @else
                                    -
                                @endif

                            {{ Str::headline($domain->tls_mode) }}

                                    @unless ($domain->is_primary)
                                        <flux:button type="button" size="sm" variant="filled" wire:click="setPrimary({{ $domain->getKey() }})">Set primary</flux:button>
                                    @endunless
                                    <flux:button type="button" size="sm" variant="danger" wire:click="removeDomain({{ $domain->getKey() }})">Delete</flux:button>

                    @endforeach


Output:
Chunk ID: 9ea73b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2539
Output:

            <flux:heading size="xl">Shipping</flux:heading>
            <flux:text class="mt-1">Zones, rates, and address matching.</flux:text>

            <flux:button :href="route('admin.settings.index')" wire:navigate variant="filled">General</flux:button>
            <flux:button :href="route('admin.settings.shipping')" wire:navigate variant="primary">Shipping</flux:button>
            <flux:button :href="route('admin.settings.taxes')" wire:navigate variant="filled">Taxes</flux:button>
            <flux:button :href="route('admin.settings.checkout')" wire:navigate variant="filled">Checkout</flux:button>
            <flux:button :href="route('admin.settings.notifications')" wire:navigate variant="filled">Notifications</flux:button>

    @if (session('status'))
        <flux:callout color="green" icon="check-circle">{{ session('status') }}</flux:callout>
    @endif

            @foreach ($zones as $zone)

getKey() }}" class="rounded-lg border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">

                            <flux:heading size="lg">{{ $zone->name }}</flux:heading>
                            <flux:text class="mt-1">{{ implode(', ', $zone->countries_json ?? []) }}</flux:text>

                            <flux:button type="button" wire:click="editZone({{ $zone->getKey() }})" size="sm" variant="filled">Edit</flux:button>
                            <flux:button type="button" wire:click="deleteZone({{ $zone->getKey() }})" wire:confirm="Delete this shipping zone?" size="sm" variant="danger">Delete</flux:button>

                                    Name
                                    Type
                                    Config
                                    Active
                                    Actions

                                @forelse ($zone->rates as $rate)

getKey() }}">
                                        {{ $rate->name }}
                                        <flux:badge>{{ Str::headline($rate->type->value) }}</flux:badge>
                                        {{ $this->rateSummary($rate) }}

                                            <flux:switch wire:click="toggleRateActive({{ $rate->getKey() }})" :checked="$rate->is_active" aria-label="Toggle {{ $rate->name }}" />

                                                <flux:button type="button" wire:click="editRate({{ $rate->getKey() }})" size="sm" variant="filled">Edit</flux:button>
                                                <flux:button type="button" wire:click="deleteRate({{ $rate->getKey() }})" wire:confirm="Delete this shipping rate?" size="sm" variant="danger">Delete</flux:button>

                                @empty

                                        No rates configured.

                                @endforelse

                        <flux:button type="button" wire:click="addRate({{ $zone->getKey() }})" variant="filled" icon="plus">Add rate</flux:button>

            @endforeach

            <form wire:submit="saveZone" class="rounded-lg border border-zinc-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-900">
                <flux:heading size="lg">{{ $editingZoneId ? 'Edit zone' : 'Add zone' }}</flux:heading>

                    <flux:input wire:model="zoneName" label="Zone name" placeholder="Germany" />
                    <flux:error name="zoneName" />

                    <flux:input wire:model="zoneCountries" label="Countries" placeholder="DE, AT, CH" />
                    <flux:error name="zoneCountries" />

                        <flux:button type="button" wire:click="$set('editingZoneId', null)" variant="ghost">Cancel</flux:button>
                        <flux:button type="submit" variant="primary">Save zone</flux:button>

            </form>

            @if ($rateZoneId)
                <form wire:submit="saveRate" class="rounded-lg border border-zinc-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-900">
                    <flux:heading size="lg">{{ $editingRateId ? 'Edit rate' : 'Add rate' }}</flux:heading>

                        <flux:input wire:model="rateName" label="Rate name" placeholder="Standard" />
                        <flux:select wire:model.live="rateType" label="Rate type">
                            <flux:select.option value="flat">Flat</flux:select.option>
                            <flux:select.option value="weight">Weight</flux:select.option>
                            <flux:select.option value="price">Price</flux:select.option>
                            <flux:select.option value="carrier">Carrier</flux:select.option>
                        </flux:select>
                        <flux:input wire:model="rateAmount" type="number" step="0.01" min="0" label="Amount" />

                        @if ($rateType === 'weight')

                                <flux:input wire:model="minimumWeight" type="number" min="0" label="Min weight (g)" />
                                <flux:input wire:model="maximumWeight" type="number" min="1" label="Max weight (g)" />

                        @endif

                        @if ($rateType === 'price')

                                <flux:input wire:model="minimumOrderAmount" type="number" step="0.01" min="0" label="Min order" />
                                <flux:input wire:model="maximumOrderAmount" type="number" step="0.01" min="0" label="Max order" />

                        @endif

                        <flux:switch wire:model="rateActive" label="{{ $rateActive ? 'Active' : 'Inactive' }}" align="left" />

                            <flux:button type="button" wire:click="$set('rateZoneId', null)" variant="ghost">Cancel</flux:button>
                            <flux:button type="submit" variant="primary">Save rate</flux:button>

                </form>
            @endif

                <flux:heading size="lg">Test address</flux:heading>

                    <flux:input wire:model="testCountry" label="Country" placeholder="DE" />
                    <flux:input wire:model="testRegion" label="Region" placeholder="DE-BE" />

                    <flux:button type="button" wire:click="testShippingAddress" variant="filled">Test</flux:button>

                @if ($testResult)

                        @if ($testResult['zone'])

Matched zone: {{ $testResult['zone'] }}

                                @foreach ($testResult['rates'] as $rate)

{{ $rate }}

                                @endforeach

                        @else

No shipping zone matches this address.

                        @endif

                @endif


Output:
Chunk ID: 3799cc
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1070
Output:

            <flux:heading size="xl">Taxes</flux:heading>
            <flux:text class="mt-1">Manual tax rates and provider mode.</flux:text>

            <flux:button :href="route('admin.settings.index')" wire:navigate variant="filled">General</flux:button>
            <flux:button :href="route('admin.settings.shipping')" wire:navigate variant="filled">Shipping</flux:button>
            <flux:button :href="route('admin.settings.taxes')" wire:navigate variant="primary">Taxes</flux:button>
            <flux:button :href="route('admin.settings.checkout')" wire:navigate variant="filled">Checkout</flux:button>
            <flux:button :href="route('admin.settings.notifications')" wire:navigate variant="filled">Notifications</flux:button>

    @if (session('status'))
        <flux:callout color="green" icon="check-circle">{{ session('status') }}</flux:callout>
    @endif

    <form wire:submit="save" class="space-y-6">

            <flux:radio.group wire:model.live="mode" label="Mode" class="grid gap-3 sm:grid-cols-2">
                <flux:radio value="manual" label="Manual tax rates" description="Define tax rates by country." />
                <flux:radio value="provider" label="Tax provider" description="Use an automated tax provider." />
            </flux:radio.group>
            <flux:error name="mode" />

        @if ($mode === 'manual')

                    <flux:heading size="lg">Manual rates</flux:heading>
                    <flux:button type="button" wire:click="addManualRate" variant="filled" icon="plus">Add rate</flux:button>

                    @foreach ($manualRates as $index => $rate)

                            <flux:input wire:model="manualRates.{{ $index }}.country" label="Country" maxlength="2" />
                            <flux:input wire:model="manualRates.{{ $index }}.name" label="Name" />
                            <flux:input wire:model="manualRates.{{ $index }}.rate_percentage" label="Rate %" type="number" step="0.01" min="0" max="100" />
                            <flux:button type="button" wire:click="removeManualRate({{ $index }})" variant="danger" icon="trash" aria-label="Remove tax rate" />

                    @endforeach

        @else

                <flux:heading size="lg">Provider</flux:heading>

                    <flux:select wire:model="provider" label="Provider">
                        <flux:select.option value="none">None</flux:select.option>
                        <flux:select.option value="stripe_tax">Stripe Tax</flux:select.option>
                    </flux:select>
                    <flux:input wire:model="providerApiKey" type="password" label="API key" />

        @endif

            <flux:switch wire:model="pricesIncludeTax" label="Prices include tax" align="left" />

            <flux:button type="submit" variant="primary" wire:loading.attr="disabled">
                Save taxes
                Saving...
            </flux:button>

    </form>


Output:
Chunk ID: c17e5b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2345
Output:
<?php

use App\Enums\ShippingRateType;
use App\Enums\StoreUserRole;
use App\Enums\TaxMode;
use App\Livewire\Admin\Settings\Checkout as AdminSettingsCheckout;
use App\Livewire\Admin\Settings\Index as AdminSettingsIndex;
use App\Livewire\Admin\Settings\Notifications as AdminSettingsNotifications;
use App\Livewire\Admin\Settings\Shipping as AdminSettingsShipping;
use App\Livewire\Admin\Settings\Taxes as AdminSettingsTaxes;
use App\Models\ShippingRate;
use App\Models\ShippingZone;
use App\Models\Store;
use App\Models\StoreDomain;
use App\Models\StoreSettings;
use App\Models\TaxSettings;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    $this->withoutVite();
    $this->seed(DatabaseSeeder::class);
});

function adminSettingsStore(): Store
{
    $store = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    app()->instance('current_store', $store);

    return $store;
}

function adminSettingsUser(): User
{
    return User::query()->where('email', 'admin@acme.test')->firstOrFail();
}

function adminSettingsUserWithRole(Store $store, StoreUserRole $role): User
{
    $user = User::factory()->create();
    $user->stores()->attach($store->getKey(), [
        'role' => $role->value,
        'created_at' => now(),
    ]);

    return $user;
}

test('settings routes render for owners and reject staff', function (): void {
    $store = adminSettingsStore();
    $owner = adminSettingsUser();
    $staff = adminSettingsUserWithRole($store, StoreUserRole::Staff);

    foreach (['/admin/settings', '/admin/settings/shipping', '/admin/settings/taxes', '/admin/settings/checkout', '/admin/settings/notifications'] as $path) {
        $this->actingAs($owner)
            ->withSession(['current_store_id' => $store->getKey()])
            ->get($path)
            ->assertSuccessful();

        $this->actingAs($staff)
            ->withSession(['current_store_id' => $store->getKey()])
            ->get($path)
            ->assertForbidden();
    }
});

test('general settings update store defaults and domains', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsIndex::class)
        ->set('storeName', 'Acme Atelier')
        ->set('defaultCurrency', 'GBP')
        ->set('defaultLocale', 'de')
        ->set('timezone', 'Europe/Berlin')
        ->set('announcementEnabled', true)
        ->set('announcementText', 'Spring edits now live')
        ->set('guestCheckoutEnabled', false)
        ->call('save')
        ->assertHasNoErrors()
        ->set('newHostname', 'atelier.test')
        ->set('newType', 'storefront')
        ->call('addDomain')
        ->assertHasNoErrors();

    $settings = StoreSettings::query()->whereKey($store->getKey())->firstOrFail();

    expect($store->refresh()->name)->toBe('Acme Atelier')
        ->and($store->default_currency)->toBe('GBP')
        ->and($store->default_locale)->toBe('de')
        ->and($settings->settings_json['announcement']['text'])->toBe('Spring edits now live')
        ->and($settings->settings_json['checkout']['guest_checkout_enabled'])->toBeFalse()
        ->and(StoreDomain::query()->where('hostname', 'atelier.test')->where('store_id', $store->getKey())->exists())->toBeTrue();
});

test('shipping settings manage zones rates and address tests', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsShipping::class)
        ->set('zoneName', 'Nordics')
        ->set('zoneCountries', 'SE, NO')
        ->call('saveZone')
        ->assertHasNoErrors();

    $zone = ShippingZone::withoutGlobalScopes()
        ->where('store_id', $store->getKey())
        ->where('name', 'Nordics')
        ->firstOrFail();

    Livewire::actingAs($user)
        ->test(AdminSettingsShipping::class)
        ->call('addRate', $zone->getKey())
        ->set('rateName', 'Nordic Standard')
        ->set('rateType', ShippingRateType::Flat->value)
        ->set('rateAmount', '12.50')
        ->call('saveRate')
        ->assertHasNoErrors()
        ->set('testCountry', 'SE')
        ->call('testShippingAddress')
        ->assertSet('testResult.zone', 'Nordics');

    $rate = ShippingRate::withoutGlobalScopes()
        ->where('zone_id', $zone->getKey())
        ->where('name', 'Nordic Standard')
        ->firstOrFail();

    expect($zone->countries_json)->toBe(['SE', 'NO'])
        ->and($rate->type)->toBe(ShippingRateType::Flat)
        ->and($rate->config_json['amount'])->toBe(1250);
});

test('checkout settings save customer and payment policy values', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsCheckout::class)
        ->set('guestCheckoutEnabled', false)
        ->set('customerAccountsRequired', true)
        ->set('phoneNumberRequired', true)
        ->set('billingAddressEnabled', false)
        ->set('orderNotesEnabled', false)
        ->set('termsRequired', true)
        ->set('termsUrl', 'https://shop.test/pages/terms')
        ->set('paymentHoldHours', 36)
        ->set('abandonedCheckoutDays', 21)
        ->set('bankTransferCancelDays', 9)
        ->call('save')
        ->assertHasNoErrors();

    $settings = StoreSettings::query()->whereKey($store->getKey())->firstOrFail()->settings_json;

    expect(data_get($settings, 'checkout.guest_checkout_enabled'))->toBeFalse()
        ->and(data_get($settings, 'checkout.customer_accounts_required'))->toBeTrue()
        ->and(data_get($settings, 'checkout.phone_number_required'))->toBeTrue()
        ->and(data_get($settings, 'checkout.billing_address_enabled'))->toBeFalse()
        ->and(data_get($settings, 'checkout.order_notes_enabled'))->toBeFalse()
        ->and(data_get($settings, 'checkout.terms_required'))->toBeTrue()
        ->and(data_get($settings, 'checkout.terms_url'))->toBe('https://shop.test/pages/terms')
        ->and(data_get($settings, 'checkout.payment_hold_hours'))->toBe(36)
        ->and(data_get($settings, 'checkout.abandoned_checkout_days'))->toBe(21)
        ->and(data_get($settings, 'bank_transfer_cancel_days'))->toBe(9);
});

test('notification settings save sender and event preferences', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsNotifications::class)
        ->set('senderName', 'Acme Ops')
        ->set('senderEmail', 'ops@shop.test')
        ->set('replyToEmail', 'support@shop.test')
        ->set('orderConfirmationEnabled', false)
        ->set('shippingConfirmationEnabled', true)
        ->set('refundConfirmationEnabled', false)
        ->set('adminOrderAlertsEnabled', false)
        ->set('lowStockAlertsEnabled', true)
        ->set('lowStockThreshold', 3)
        ->call('save')
        ->assertHasNoErrors();

    $settings = StoreSettings::query()->whereKey($store->getKey())->firstOrFail()->settings_json;

    expect(data_get($settings, 'notifications.sender_name'))->toBe('Acme Ops')
        ->and(data_get($settings, 'notifications.sender_email'))->toBe('ops@shop.test')
        ->and(data_get($settings, 'notifications.reply_to_email'))->toBe('support@shop.test')
        ->and(data_get($settings, 'notifications.order_confirmation_enabled'))->toBeFalse()
        ->and(data_get($settings, 'notifications.shipping_confirmation_enabled'))->toBeTrue()
        ->and(data_get($settings, 'notifications.refund_confirmation_enabled'))->toBeFalse()
        ->and(data_get($settings, 'notifications.admin_order_alerts_enabled'))->toBeFalse()
        ->and(data_get($settings, 'notifications.low_stock_alerts_enabled'))->toBeTrue()
        ->and(data_get($settings, 'notifications.low_stock_threshold'))->toBe(3);
});

test('tax settings save manual and provider configuration', function (): void {
    $store = adminSettingsStore();
    $user = adminSettingsUser();

    Livewire::actingAs($user)
        ->test(AdminSettingsTaxes::class)
        ->set('manualRates', [
            ['country' => 'DE', 'name' => 'VAT', 'rate_percentage' => '19.00'],
            ['country' => 'FR', 'name' => 'TVA', 'rate_percentage' => '20.00'],
        ])
        ->set('pricesIncludeTax', true)
        ->call('save')
        ->assertHasNoErrors();

    $manual = TaxSettings::withoutGlobalScopes()->whereKey($store->getKey())->firstOrFail();

    expect($manual->mode)->toBe(TaxMode::Manual)
        ->and($manual->prices_include_tax)->toBeTrue()
        ->and($manual->config_json['rates'][1]['country'])->toBe('FR')
        ->and($manual->config_json['rates'][1]['rate_bps'])->toBe(2000);

    Livewire::actingAs($user)
        ->test(AdminSettingsTaxes::class)
        ->set('mode', TaxMode::Provider->value)
        ->set('provider', 'stripe_tax')
        ->set('providerApiKey', 'sk_test_tax')
        ->call('save')
        ->assertHasNoErrors();

    $provider = TaxSettings::withoutGlobalScopes()->whereKey($store->getKey())->firstOrFail();

    expect($provider->mode)->toBe(TaxMode::Provider)
        ->and($provider->provider)->toBe('stripe_tax')
        ->and($provider->config_json['provider_api_key'])->toBe('sk_test_tax');
});


sed -n '1,320p' app/Livewire/Admin/Settings/Index.php


sed -n '1,360p' app/Livewire/Admin/Settings/Shipping.php


sed -n '1,260p' app/Livewire/Admin/Settings/Taxes.php


sed -n '1,220p' database/seeders/ShippingSeeder.php database/seeders/TaxSettingsSeeder.php


sed -n '1,360p' app/Livewire/Admin/Settings/Shipping.php

<?php

namespace App\Livewire\Admin\Settings;

use App\Enums\ShippingRateType;
use App\Models\ShippingRate;
use App\Models\ShippingZone;
use App\Models\Store;
use App\Services\ShippingCalculator;
use App\Support\Money;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Collection;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Shipping extends Component
{
    use AuthorizesRequests;

    #[Locked]
    public int $storeId;

    public ?int $editingZoneId = null;

    public string $zoneName = '';

    public string $zoneCountries = '';

    public ?int $rateZoneId = null;

    public ?int $editingRateId = null;

    public string $rateName = '';

    public string $rateType = 'flat';

    public string $rateAmount = '0.00';

    public string $minimumWeight = '0';

    public string $maximumWeight = '';

    public string $minimumOrderAmount = '0.00';

    public string $maximumOrderAmount = '';

    public bool $rateActive = true;

    public string $testCountry = 'DE';

    public string $testRegion = '';

    /**
     * @var array<string, mixed>|null
     */
    public ?array $testResult = null;

    public function mount(): void
    {
        $store = $this->store();

        $this->authorize('update', $store);

        $this->storeId = $store->getKey();
    }

    public function editZone(int $zoneId): void
    {
        $zone = $this->zone($zoneId);

        $this->authorize('update', $this->scopedStore());

        $this->editingZoneId = $zone->getKey();
        $this->zoneName = $zone->name;
        $this->zoneCountries = implode(', ', $zone->countries_json ?? []);
    }

    public function saveZone(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'zoneName' => ['required', 'string', 'max:255'],
            'zoneCountries' => ['required', 'string', 'max:255'],
        ], [], [
            'zoneName' => 'zone name',
            'zoneCountries' => 'countries',
        ]);

        $countries = $this->countryCodes($validated['zoneCountries']);

        if ($countries === []) {
            $this->addError('zoneCountries', __('Enter at least one ISO country code.'));

            return;
        }

        $payload = [
            'store_id' => $store->getKey(),
            'name' => $validated['zoneName'],
            'countries_json' => $countries,
            'regions_json' => [],
        ];

        $this->editingZoneId
            ? $this->zone($this->editingZoneId)->update($payload)
            : ShippingZone::withoutGlobalScopes()->create($payload);

        $this->resetZoneForm();

        session()->flash('status', 'Shipping zone saved');
        $this->dispatch('toast', type: 'success', message: __('Shipping zone saved'));
    }

    public function deleteZone(int $zoneId): void
    {
        $this->authorize('update', $this->scopedStore());

        $this->zone($zoneId)->delete();
        $this->resetZoneForm();
        $this->resetRateForm();

        session()->flash('status', 'Shipping zone deleted');
    }

    public function addRate(int $zoneId): void
    {
        $zone = $this->zone($zoneId);

        $this->authorize('update', $this->scopedStore());

        $this->resetRateForm();
        $this->rateZoneId = $zone->getKey();
    }

    public function editRate(int $rateId): void
    {
        $rate = $this->rate($rateId);

        $this->authorize('update', $this->scopedStore());

        $this->editingRateId = $rate->getKey();
        $this->rateZoneId = $rate->zone_id;
        $this->rateName = $rate->name;
        $this->rateType = $rate->type->value;
        $this->rateActive = $rate->is_active;
        $this->fillRateConfig($rate);
    }

    public function saveRate(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'rateZoneId' => ['required', 'integer'],
            'rateName' => ['required', 'string', 'max:255'],
            'rateType' => ['required', Rule::in(array_column(ShippingRateType::cases(), 'value'))],
            'rateAmount' => ['required', 'numeric', 'min:0'],
            'minimumWeight' => ['nullable', 'integer', 'min:0'],
            'maximumWeight' => ['nullable', 'integer', 'min:1'],
            'minimumOrderAmount' => ['nullable', 'numeric', 'min:0'],
            'maximumOrderAmount' => ['nullable', 'numeric', 'min:0'],
            'rateActive' => ['boolean'],
        ], [], [
            'rateZoneId' => 'shipping zone',
            'rateName' => 'rate name',
            'rateType' => 'rate type',
            'rateAmount' => 'rate amount',
        ]);

        $zone = $this->zone((int) $validated['rateZoneId']);
        $payload = [
            'zone_id' => $zone->getKey(),
            'name' => $validated['rateName'],
            'type' => ShippingRateType::from($validated['rateType']…1294 chars truncated…ping->matchingZone($store, $address);

        $this->testResult = [
            'zone' => $zone?->name,
            'rates' => $zone instanceof ShippingZone
                ? ShippingRate::withoutGlobalScopes()
                    ->where('zone_id', $zone->getKey())
                    ->where('is_active', true)
                    ->orderBy('id')
                    ->get()
                    ->map(fn (ShippingRate $rate): string => $rate->name.' - '.$this->rateSummary($rate))
                    ->all()
                : [],
        ];
    }

    /**
     * @return Collection<int, ShippingZone>
     */
    public function zones(): Collection
    {
        return ShippingZone::withoutGlobalScopes()
            ->with(['rates' => fn ($query) => $query->withoutGlobalScopes()->orderBy('id')])
            ->where('store_id', $this->storeId)
            ->orderBy('id')
            ->get();
    }

    public function rateSummary(ShippingRate $rate): string
    {
        if ($rate->type === ShippingRateType::Carrier) {
            return 'Carrier fallback '.Money::format((int) data_get($rate->config_json, 'amount', 0), $this->scopedStore()->default_currency);
        }

        $amount = match ($rate->type) {
            ShippingRateType::Flat => (int) data_get($rate->config_json, 'amount', 0),
            default => (int) data_get($rate->config_json, 'ranges.0.amount', 0),
        };

        return Money::format($amount, $this->scopedStore()->default_currency);
    }

    public function render(): mixed
    {
        return view('livewire.admin.settings.shipping', [
            'zones' => $this->zones(),
        ])->layout('layouts.app', [
            'title' => __('Shipping'),
        ]);
    }

    private function store(): Store
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        return $store;
    }

    private function scopedStore(): Store
    {
        return Store::query()->whereKey($this->storeId)->firstOrFail();
    }

    private function zone(int $zoneId): ShippingZone
    {
        return ShippingZone::withoutGlobalScopes()
            ->where('store_id', $this->storeId)
            ->whereKey($zoneId)
            ->firstOrFail();
    }

    private function rate(int $rateId): ShippingRate
    {
        return ShippingRate::withoutGlobalScopes()
            ->whereKey($rateId)
            ->whereHas('zone', function ($query): void {
                $query->withoutGlobalScopes()->where('store_id', $this->storeId);
            })
            ->firstOrFail();
    }

    /**
     * @return list<string>
     */
    private function countryCodes(string $countries): array
    {
        return collect(explode(',', $countries))
            ->map(fn (string $country): string => strtoupper(trim($country)))
            ->filter(fn (string $country): bool => preg_match('/^[A-Z]{2}$/', $country) === 1)
            ->unique()
            ->values()
            ->all();
    }

    /**
     * @return array<string, mixed>
     */
    private function rateConfig(): array
    {
        $amount = Money::fromDecimalString($this->rateAmount);

        return match ($this->rateType) {
            ShippingRateType::Weight->value => [
                'ranges' => [[
                    'min_g' => (int) ($this->minimumWeight ?: 0),
                    'max_g' => $this->maximumWeight !== '' ? (int) $this->maximumWeight : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Price->value => [
                'ranges' => [[
                    'min_amount' => Money::fromDecimalString($this->minimumOrderAmount),
                    'max_amount' => $this->maximumOrderAmount !== '' ? Money::fromDecimalString($this->maximumOrderAmount) : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Carrier->value => ['amount' => $amount],
            default => ['amount' => $amount],
        };
    }

    private function fillRateConfig(ShippingRate $rate): void
    {
        $amount = match ($rate->type) {
            ShippingRateType::Flat, ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 0),
            default => (int) data_get($rate->config_json, 'ranges.0.amount', 0),
        };

        $this->rateAmount = number_format($amount / 100, 2, '.', '');
        $this->minimumWeight = (string) data_get($rate->config_json, 'ranges.0.min_g', 0);
        $this->maximumWeight = data_get($rate->config_json, 'ranges.0.max_g') !== null
            ? (string) data_get($rate->config_json, 'ranges.0.max_g')
            : '';
        $this->minimumOrderAmount = number_format(((int) data_get($rate->config_json, 'ranges.0.min_amount', 0)) / 100, 2, '.', '');
        $this->maximumOrderAmount = data_get($rate->config_json, 'ranges.0.max_amount') !== null
            ? number_format(((int) data_get($rate->config_json, 'ranges.0.max_amount')) / 100, 2, '.', '')


sed -n '1,320p' app/Livewire/Admin/Settings/Index.php

<?php

namespace App\Livewire\Admin\Settings;

use App\Enums\StoreDomainType;
use App\Models\Store;
use App\Models\StoreDomain;
use App\Models\StoreSettings;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Collection;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Index extends Component
{
    use AuthorizesRequests;

    #[Locked]
    public int $storeId;

    public string $storeName = '';

    public string $storeHandle = '';

    public string $defaultCurrency = 'EUR';

    public string $defaultLocale = 'en';

    public string $timezone = 'UTC';

    public bool $announcementEnabled = false;

    public string $announcementText = '';

    public bool $guestCheckoutEnabled = true;

    public string $newHostname = '';

    public string $newType = 'storefront';

    public function mount(): void
    {
        $store = $this->store();

        $this->authorize('update', $store);

        $settings = $this->storeSettings($store)->settings_json ?? [];

        $this->storeId = $store->getKey();
        $this->storeName = $store->name;
        $this->storeHandle = $store->handle;
        $this->defaultCurrency = $store->default_currency;
        $this->defaultLocale = $store->default_locale;
        $this->timezone = $store->timezone;
        $this->announcementEnabled = (bool) data_get($settings, 'announcement.enabled', false);
        $this->announcementText = (string) data_get($settings, 'announcement.text', '');
        $this->guestCheckoutEnabled = (bool) data_get($settings, 'checkout.guest_checkout_enabled', true);
    }

    public function save(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'storeName' => ['required', 'string', 'max:255'],
            'defaultCurrency' => ['required', Rule::in($this->currencyOptions())],
            'defaultLocale' => ['required', Rule::in(array_keys($this->localeOptions()))],
            'timezone' => ['required', Rule::in(timezone_identifiers_list())],
            'announcementEnabled' => ['boolean'],
            'announcementText' => ['nullable', 'string', 'max:255'],
            'guestCheckoutEnabled' => ['boolean'],
        ]);

        $store->forceFill([
            'name' => $validated['storeName'],
            'default_currency' => $validated['defaultCurrency'],
            'default_locale' => $validated['defaultLocale'],
            'timezone' => $validated['timezone'],
        ])->save();

        $settings = $this->storeSettings($store);
        $settings->forceFill([
            'settings_json' => array_replace_recursive($settings->settings_json ?? [], [
                'announcement' => [
                    'enabled' => $this->announcementEnabled,
                    'text' => $this->announcementText,
                ],
                'checkout' => [
                    'guest_checkout_enabled' => $this->guestCheckoutEnabled,
                ],
            ]),
            'updated_at' => now(),
        ])->save();

        session()->flash('status', 'Settings saved');
        $this->dispatch('toast', type: 'success', message: __('Settings saved'));
    }

    public function addDomain(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'newHostname' => [
                'required',
                'string',
                'max:255',
                'regex:/^[a-z0-9.-]+$/i',
                Rule::unique('store_domains', 'hostname'),
            ],
            'newType' => ['required', Rule::in(array_column(StoreDomainType::cases(), 'value'))],
        ], [], [
            'newHostname' => 'hostname',
            'newType' => 'domain type',
        ]);

        StoreDomain::query()->create([
            'store_id' => $store->getKey(),
            'hostname' => mb_strtolower(trim($validated['newHostname'])),
            'type' => StoreDomainType::from($validated['newType']),
            'is_primary' => $this->domains()->isEmpty(),
            'tls_mode' => 'managed',
        ]);

        $this->reset('newHostname');

        session()->flash('status', 'Domain added');
        $this->dispatch('toast', type: 'success', message: __('Domain added'));
    }

    public function setPrimary(int $domainId): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $domain = $this->domain($domainId);

        StoreDomain::query()
            ->where('store_id', $store->getKey())
            ->update(['is_primary' => false]);

        $domain->forceFill(['is_primary' => true])->save();

        session()->flash('status', 'Primary domain updated');
    }

    public function removeDomain(int $domainId): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $domain = $this->domain($domainId);
        $wasPrimary = $domain->is_primary;

        $domain->delete();

        if ($wasPrimary) {
            StoreDomain::query()
                ->where('store_id', $store->getKey())
                ->orderBy('id')
                ->first()
                ?->forceFill(['is_primary' => true])
                ->save();
        }

        session()->flash('status', 'Domain removed');
    }

    /**
     * @return Collection<int, StoreDomain>
     */
    public function domains(): Collection
    {
        return StoreDomain::query()
            ->where('store_id', $this->storeId)
            ->orderByDesc('is_primary')
            ->orderBy('hostname')
            ->get();
    }

    /**
     * @return array<string, string>
     */
    public function localeOptions(): array
    {
        return [
            'en' => 'English',
            'de' => 'German',
            'fr' => 'French',
        ];
    }

    /**
     * @return list<string>
     */
    public function currencyOptions(): array
    {
        return ['EUR', 'USD', 'GBP', 'CHF'];
    }

    /**
     * @return list<string>
     */
    public function timezoneOptions(): array
    {
        return collect(timezone_identifiers_list())
            ->filter(fn (string $timezone): bool => str_starts_with($timezone, 'Europe/') || str_starts_with($timezone, 'America/'))
            ->values()
            ->all();
    }

    public function render(): mixed
    {
        return view('livewire.admin.settings.index', [
            'domains' => $this->domains(),
            'currencyOptions' => $this->currencyOptions(),
            'localeOptions' => $this->localeOptions(),
            'timezoneOptions' => $this->timezoneOptions(),
        ])->layout('layouts.app', [
            'title' => __('Settings'),
        ]);
    }

    private function store(): Store
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        return $store;
    }

    private function scopedStore(): Store
    {
        return Store::query()->whereKey($this->storeId)->firstOrFail();
    }

    private function storeSettings(Store $store): StoreSettings
    {
        return StoreSettings::query()->firstOrCreate(
            ['store_id' => $store->getKey()],
            ['settings_json' => []],
        );
    }

    private function domain(int $domainId): StoreDomain
    {
        return StoreDomain::query()
            ->where('store_id', $this->storeId)
            ->whereKey($domainId)
            ->firstOrFail();
    }
}


sed -n '1,260p' app/Livewire/Admin/Settings/Taxes.php

<?php

namespace App\Livewire\Admin\Settings;

use App\Enums\TaxMode;
use App\Models\Store;
use App\Models\TaxSettings;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Taxes extends Component
{
    use AuthorizesRequests;

    #[Locked]
    public int $storeId;

    public string $mode = 'manual';

    public bool $pricesIncludeTax = false;

    public string $provider = 'none';

    public string $providerApiKey = '';

    /**
     * @var array<int, array{country: string, name: string, rate_percentage: string}>
     */
    public array $manualRates = [];

    public function mount(): void
    {
        $store = $this->store();

        $this->authorize('update', $store);

        $this->storeId = $store->getKey();
        $this->fillFromSettings($this->taxSettings($store));
    }

    public function addManualRate(): void
    {
        $this->manualRates[] = [
            'country' => '',
            'name' => 'Tax',
            'rate_percentage' => '0.00',
        ];
    }

    public function removeManualRate(int $index): void
    {
        unset($this->manualRates[$index]);

        $this->manualRates = array_values($this->manualRates);
    }

    public function save(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'mode' => ['required', Rule::in(array_column(TaxMode::cases(), 'value'))],
            'pricesIncludeTax' => ['boolean'],
            'provider' => ['required', Rule::in(['none', 'stripe_tax'])],
            'providerApiKey' => ['nullable', 'string', 'max:255'],
            'manualRates' => ['array'],
            'manualRates.*.country' => ['required', 'string', 'size:2'],
            'manualRates.*.name' => ['required', 'string', 'max:50'],
            'manualRates.*.rate_percentage' => ['required', 'numeric', 'min:0', 'max:100'],
        ]);

        TaxSettings::withoutGlobalScopes()->updateOrCreate(
            ['store_id' => $store->getKey()],
            [
                'mode' => TaxMode::from($validated['mode']),
                'provider' => $this->mode === TaxMode::Provider->value ? $this->provider : 'none',
                'prices_include_tax' => $this->pricesIncludeTax,
                'config_json' => [
                    'name' => 'Tax',
                    'default_rate_bps' => $this->firstRateBasisPoints(),
                    'shipping_taxable' => true,
                    'provider_api_key' => $this->mode === TaxMode::Provider->value ? $this->providerApiKey : null,
                    'rates' => collect($this->manualRates)
                        ->map(fn (array $rate): array => [
                            'country' => strtoupper($rate['country']),
                            'rate_bps' => (int) round(((float) $rate['rate_percentage']) * 100),
                            'name' => $rate['name'],
                        ])
                        ->values()
                        ->all(),
                ],
            ],
        );

        session()->flash('status', 'Tax settings saved');
        $this->dispatch('toast', type: 'success', message: __('Tax settings saved'));
    }

    public function render(): mixed
    {
        return view('livewire.admin.settings.taxes')
            ->layout('layouts.app', [
                'title' => __('Taxes'),
            ]);
    }

    private function store(): Store
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        return $store;
    }

    private function scopedStore(): Store
    {
        return Store::query()->whereKey($this->storeId)->firstOrFail();
    }

    private function taxSettings(Store $store): TaxSettings
    {
        return TaxSettings::withoutGlobalScopes()->firstOrCreate(
            ['store_id' => $store->getKey()],
            [
                'mode' => TaxMode::Manual,
                'provider' => 'none',
                'prices_include_tax' => false,
                'config_json' => [
                    'name' => 'Tax',
                    'default_rate_bps' => 0,
                    'shipping_taxable' => true,
                    'rates' => [],
                ],
            ],
        );
    }

    private function fillFromSettings(TaxSettings $settings): void
    {
        $this->mode = $settings->mode->value;
        $this->pricesIncludeTax = $settings->prices_include_tax;
        $this->provider = $settings->provider;
        $this->providerApiKey = (string) data_get($settings->config_json, 'provider_api_key', '');
        $this->manualRates = collect(data_get($settings->config_json, 'rates', []))
            ->map(fn (array $rate): array => [
                'country' => (string) data_get($rate, 'country', ''),
                'name' => (string) data_get($rate, 'name', 'Tax'),
                'rate_percentage' => number_format(((int) data_get($rate, 'rate_bps', 0)) / 100, 2, '.', ''),
            ])
            ->values()
            ->all();

        if ($this->manualRates === []) {
            $this->addManualRate();
        }
    }

    private function firstRateBasisPoints(): int
    {
        $first = $this->manualRates[0]['rate_percentage'] ?? '0';

        return (int) round(((float) $first) * 100);
    }
}


sed -n '1,220p' database/seeders/ShippingSeeder.php database/seeders/TaxSettingsSeeder.php

sed: database/seeders/ShippingSeeder.php: No such file or directory
<?php

namespace Database\Seeders;

use App\Models\Store;
use App\Models\TaxSettings;
use Illuminate\Database\Seeder;

class TaxSettingsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            TaxSettings::withoutGlobalScopes()->updateOrCreate(
                ['store_id' => $store->getKey()],
                [
                    'mode' => 'manual',
                    'provider' => 'none',
                    'prices_include_tax' => true,
                    'config_json' => [
                        'name' => 'VAT',
                        'default_rate_bps' => 1900,
                        'shipping_taxable' => true,
                        'rates' => [
                            ['country' => 'DE', 'rate_bps' => 1900, 'name' => 'VAT'],
                            ['country' => 'AT', 'rate_bps' => 2000, 'name' => 'VAT'],
                            ['country' => 'CH', 'rate_bps' => 770, 'name' => 'VAT'],
                        ],
                    ],
                ],
            );
        });
    }
}


Output:
Chunk ID: 96fcb9
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1885
Output:
<?php

namespace App\Livewire\Admin\Settings;

use App\Enums\StoreDomainType;
use App\Models\Store;
use App\Models\StoreDomain;
use App\Models\StoreSettings;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Collection;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Index extends Component
{
    use AuthorizesRequests;

    #[Locked]
    public int $storeId;

    public string $storeName = '';

    public string $storeHandle = '';

    public string $defaultCurrency = 'EUR';

    public string $defaultLocale = 'en';

    public string $timezone = 'UTC';

    public bool $announcementEnabled = false;

    public string $announcementText = '';

    public bool $guestCheckoutEnabled = true;

    public string $newHostname = '';

    public string $newType = 'storefront';

    public function mount(): void
    {
        $store = $this->store();

        $this->authorize('update', $store);

        $settings = $this->storeSettings($store)->settings_json ?? [];

        $this->storeId = $store->getKey();
        $this->storeName = $store->name;
        $this->storeHandle = $store->handle;
        $this->defaultCurrency = $store->default_currency;
        $this->defaultLocale = $store->default_locale;
        $this->timezone = $store->timezone;
        $this->announcementEnabled = (bool) data_get($settings, 'announcement.enabled', false);
        $this->announcementText = (string) data_get($settings, 'announcement.text', '');
        $this->guestCheckoutEnabled = (bool) data_get($settings, 'checkout.guest_checkout_enabled', true);
    }

    public function save(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'storeName' => ['required', 'string', 'max:255'],
            'defaultCurrency' => ['required', Rule::in($this->currencyOptions())],
            'defaultLocale' => ['required', Rule::in(array_keys($this->localeOptions()))],
            'timezone' => ['required', Rule::in(timezone_identifiers_list())],
            'announcementEnabled' => ['boolean'],
            'announcementText' => ['nullable', 'string', 'max:255'],
            'guestCheckoutEnabled' => ['boolean'],
        ]);

        $store->forceFill([
            'name' => $validated['storeName'],
            'default_currency' => $validated['defaultCurrency'],
            'default_locale' => $validated['defaultLocale'],
            'timezone' => $validated['timezone'],
        ])->save();

        $settings = $this->storeSettings($store);
        $settings->forceFill([
            'settings_json' => array_replace_recursive($settings->settings_json ?? [], [
                'announcement' => [
                    'enabled' => $this->announcementEnabled,
                    'text' => $this->announcementText,
                ],
                'checkout' => [
                    'guest_checkout_enabled' => $this->guestCheckoutEnabled,
                ],
            ]),
            'updated_at' => now(),
        ])->save();

        session()->flash('status', 'Settings saved');
        $this->dispatch('toast', type: 'success', message: __('Settings saved'));
    }

    public function addDomain(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'newHostname' => [
                'required',
                'string',
                'max:255',
                'regex:/^[a-z0-9.-]+$/i',
                Rule::unique('store_domains', 'hostname'),
            ],
            'newType' => ['required', Rule::in(array_column(StoreDomainType::cases(), 'value'))],
        ], [], [
            'newHostname' => 'hostname',
            'newType' => 'domain type',
        ]);

        StoreDomain::query()->create([
            'store_id' => $store->getKey(),
            'hostname' => mb_strtolower(trim($validated['newHostname'])),
            'type' => StoreDomainType::from($validated['newType']),
            'is_primary' => $this->domains()->isEmpty(),
            'tls_mode' => 'managed',
        ]);

        $this->reset('newHostname');

        session()->flash('status', 'Domain added');
        $this->dispatch('toast', type: 'success', message: __('Domain added'));
    }

    public function setPrimary(int $domainId): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $domain = $this->domain($domainId);

        StoreDomain::query()
            ->where('store_id', $store->getKey())
            ->update(['is_primary' => false]);

        $domain->forceFill(['is_primary' => true])->save();

        session()->flash('status', 'Primary domain updated');
    }

    public function removeDomain(int $domainId): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $domain = $this->domain($domainId);
        $wasPrimary = $domain->is_primary;

        $domain->delete();

        if ($wasPrimary) {
            StoreDomain::query()
                ->where('store_id', $store->getKey())
                ->orderBy('id')
                ->first()
                ?->forceFill(['is_primary' => true])
                ->save();
        }

        session()->flash('status', 'Domain removed');
    }

    /**
     * @return Collection<int, StoreDomain>
     */
    public function domains(): Collection
    {
        return StoreDomain::query()
            ->where('store_id', $this->storeId)
            ->orderByDesc('is_primary')
            ->orderBy('hostname')
            ->get();
    }

    /**
     * @return array<string, string>
     */
    public function localeOptions(): array
    {
        return [
            'en' => 'English',
            'de' => 'German',
            'fr' => 'French',
        ];
    }

    /**
     * @return list<string>
     */
    public function currencyOptions(): array
    {
        return ['EUR', 'USD', 'GBP', 'CHF'];
    }

    /**
     * @return list<string>
     */
    public function timezoneOptions(): array
    {
        return collect(timezone_identifiers_list())
            ->filter(fn (string $timezone): bool => str_starts_with($timezone, 'Europe/') || str_starts_with($timezone, 'America/'))
            ->values()
            ->all();
    }

    public function render(): mixed
    {
        return view('livewire.admin.settings.index', [
            'domains' => $this->domains(),
            'currencyOptions' => $this->currencyOptions(),
            'localeOptions' => $this->localeOptions(),
            'timezoneOptions' => $this->timezoneOptions(),
        ])->layout('layouts.app', [
            'title' => __('Settings'),
        ]);
    }

    private function store(): Store
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        return $store;
    }

    private function scopedStore(): Store
    {
        return Store::query()->whereKey($this->storeId)->firstOrFail();
    }

    private function storeSettings(Store $store): StoreSettings
    {
        return StoreSettings::query()->firstOrCreate(
            ['store_id' => $store->getKey()],
            ['settings_json' => []],
        );
    }

    private function domain(int $domainId): StoreDomain
    {
        return StoreDomain::query()
            ->where('store_id', $this->storeId)
            ->whereKey($domainId)
            ->firstOrFail();
    }
}


Output:
Chunk ID: 45397b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2824
Output:
<?php

namespace App\Livewire\Admin\Settings;

use App\Enums\ShippingRateType;
use App\Models\ShippingRate;
use App\Models\ShippingZone;
use App\Models\Store;
use App\Services\ShippingCalculator;
use App\Support\Money;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Collection;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Shipping extends Component
{
    use AuthorizesRequests;

    #[Locked]
    public int $storeId;

    public ?int $editingZoneId = null;

    public string $zoneName = '';

    public string $zoneCountries = '';

    public ?int $rateZoneId = null;

    public ?int $editingRateId = null;

    public string $rateName = '';

    public string $rateType = 'flat';

    public string $rateAmount = '0.00';

    public string $minimumWeight = '0';

    public string $maximumWeight = '';

    public string $minimumOrderAmount = '0.00';

    public string $maximumOrderAmount = '';

    public bool $rateActive = true;

    public string $testCountry = 'DE';

    public string $testRegion = '';

    /**
     * @var array<string, mixed>|null
     */
    public ?array $testResult = null;

    public function mount(): void
    {
        $store = $this->store();

        $this->authorize('update', $store);

        $this->storeId = $store->getKey();
    }

    public function editZone(int $zoneId): void
    {
        $zone = $this->zone($zoneId);

        $this->authorize('update', $this->scopedStore());

        $this->editingZoneId = $zone->getKey();
        $this->zoneName = $zone->name;
        $this->zoneCountries = implode(', ', $zone->countries_json ?? []);
    }

    public function saveZone(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'zoneName' => ['required', 'string', 'max:255'],
            'zoneCountries' => ['required', 'string', 'max:255'],
        ], [], [
            'zoneName' => 'zone name',
            'zoneCountries' => 'countries',
        ]);

        $countries = $this->countryCodes($validated['zoneCountries']);

        if ($countries === []) {
            $this->addError('zoneCountries', __('Enter at least one ISO country code.'));

            return;
        }

        $payload = [
            'store_id' => $store->getKey(),
            'name' => $validated['zoneName'],
            'countries_json' => $countries,
            'regions_json' => [],
        ];

        $this->editingZoneId
            ? $this->zone($this->editingZoneId)->update($payload)
            : ShippingZone::withoutGlobalScopes()->create($payload);

        $this->resetZoneForm();

        session()->flash('status', 'Shipping zone saved');
        $this->dispatch('toast', type: 'success', message: __('Shipping zone saved'));
    }

    public function deleteZone(int $zoneId): void
    {
        $this->authorize('update', $this->scopedStore());

        $this->zone($zoneId)->delete();
        $this->resetZoneForm();
        $this->resetRateForm();

        session()->flash('status', 'Shipping zone deleted');
    }

    public function addRate(int $zoneId): void
    {
        $zone = $this->zone($zoneId);

        $this->authorize('update', $this->scopedStore());

        $this->resetRateForm();
        $this->rateZoneId = $zone->getKey();
    }

    public function editRate(int $rateId): void
    {
        $rate = $this->rate($rateId);

        $this->authorize('update', $this->scopedStore());

        $this->editingRateId = $rate->getKey();
        $this->rateZoneId = $rate->zone_id;
        $this->rateName = $rate->name;
        $this->rateType = $rate->type->value;
        $this->rateActive = $rate->is_active;
        $this->fillRateConfig($rate);
    }

    public function saveRate(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'rateZoneId' => ['required', 'integer'],
            'rateName' => ['required', 'string', 'max:255'],
            'rateType' => ['required', Rule::in(array_column(ShippingRateType::cases(), 'value'))],
            'rateAmount' => ['required', 'numeric', 'min:0'],
            'minimumWeight' => ['nullable', 'integer', 'min:0'],
            'maximumWeight' => ['nullable', 'integer', 'min:1'],
            'minimumOrderAmount' => ['nullable', 'numeric', 'min:0'],
            'maximumOrderAmount' => ['nullable', 'numeric', 'min:0'],
            'rateActive' => ['boolean'],
        ], [], [
            'rateZoneId' => 'shipping zone',
            'rateName' => 'rate name',
            'rateType' => 'rate type',
            'rateAmount' => 'rate amount',
        ]);

        $zone = $this->zone((int) $validated['rateZoneId']);
        $payload = [
            'zone_id' => $zone->getKey(),
            'name' => $validated['rateName'],
            'type' => ShippingRateType::from($validated['rateType']),
            'config_json' => $this->rateConfig(),
            'is_active' => $this->rateActive,
        ];

        $this->editingRateId
            ? $this->rate($this->editingRateId)->update($payload)
            : ShippingRate::withoutGlobalScopes()->create($payload);

        $this->resetRateForm();

        session()->flash('status', 'Shipping rate saved');
        $this->dispatch('toast', type: 'success', message: __('Shipping rate saved'));
    }

    public function deleteRate(int $rateId): void
    {
        $this->authorize('update', $this->scopedStore());

        $this->rate($rateId)->delete();
        $this->resetRateForm();

        session()->flash('status', 'Shipping rate deleted');
    }

    public function toggleRateActive(int $rateId): void
    {
        $this->authorize('update', $this->scopedStore());

        $rate = $this->rate($rateId);
        $rate->forceFill(['is_active' => ! $rate->is_active])->save();
    }

    public function testShippingAddress(ShippingCalculator $shipping): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $address = [
            'country' => strtoupper($this->testCountry),
            'province_code' => strtoupper($this->testRegion),
        ];
        $zone = $shipping->matchingZone($store, $address);

        $this->testResult = [
            'zone' => $zone?->name,
            'rates' => $zone instanceof ShippingZone
                ? ShippingRate::withoutGlobalScopes()
                    ->where('zone_id', $zone->getKey())
                    ->where('is_active', true)
                    ->orderBy('id')
                    ->get()
                    ->map(fn (ShippingRate $rate): string => $rate->name.' - '.$this->rateSummary($rate))
                    ->all()
                : [],
        ];
    }

    /**
     * @return Collection<int, ShippingZone>
     */
    public function zones(): Collection
    {
        return ShippingZone::withoutGlobalScopes()
            ->with(['rates' => fn ($query) => $query->withoutGlobalScopes()->orderBy('id')])
            ->where('store_id', $this->storeId)
            ->orderBy('id')
            ->get();
    }

    public function rateSummary(ShippingRate $rate): string
    {
        if ($rate->type === ShippingRateType::Carrier) {
            return 'Carrier fallback '.Money::format((int) data_get($rate->config_json, 'amount', 0), $this->scopedStore()->default_currency);
        }

        $amount = match ($rate->type) {
            ShippingRateType::Flat => (int) data_get($rate->config_json, 'amount', 0),
            default => (int) data_get($rate->config_json, 'ranges.0.amount', 0),
        };

        return Money::format($amount, $this->scopedStore()->default_currency);
    }

    public function render(): mixed
    {
        return view('livewire.admin.settings.shipping', [
            'zones' => $this->zones(),
        ])->layout('layouts.app', [
            'title' => __('Shipping'),
        ]);
    }

    private function store(): Store
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        return $store;
    }

    private function scopedStore(): Store
    {
        return Store::query()->whereKey($this->storeId)->firstOrFail();
    }

    private function zone(int $zoneId): ShippingZone
    {
        return ShippingZone::withoutGlobalScopes()
            ->where('store_id', $this->storeId)
            ->whereKey($zoneId)
            ->firstOrFail();
    }

    private function rate(int $rateId): ShippingRate
    {
        return ShippingRate::withoutGlobalScopes()
            ->whereKey($rateId)
            ->whereHas('zone', function ($query): void {
                $query->withoutGlobalScopes()->where('store_id', $this->storeId);
            })
            ->firstOrFail();
    }

    /**
     * @return list<string>
     */
    private function countryCodes(string $countries): array
    {
        return collect(explode(',', $countries))
            ->map(fn (string $country): string => strtoupper(trim($country)))
            ->filter(fn (string $country): bool => preg_match('/^[A-Z]{2}$/', $country) === 1)
            ->unique()
            ->values()
            ->all();
    }

    /**
     * @return array<string, mixed>
     */
    private function rateConfig(): array
    {
        $amount = Money::fromDecimalString($this->rateAmount);

        return match ($this->rateType) {
            ShippingRateType::Weight->value => [
                'ranges' => [[
                    'min_g' => (int) ($this->minimumWeight ?: 0),
                    'max_g' => $this->maximumWeight !== '' ? (int) $this->maximumWeight : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Price->value => [
                'ranges' => [[
                    'min_amount' => Money::fromDecimalString($this->minimumOrderAmount),
                    'max_amount' => $this->maximumOrderAmount !== '' ? Money::fromDecimalString($this->maximumOrderAmount) : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Carrier->value => ['amount' => $amount],
            default => ['amount' => $amount],
        };
    }

    private function fillRateConfig(ShippingRate $rate): void
    {
        $amount = match ($rate->type) {
            ShippingRateType::Flat, ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 0),
            default => (int) data_get($rate->config_json, 'ranges.0.amount', 0),
        };

        $this->rateAmount = number_format($amount / 100, 2, '.', '');
        $this->minimumWeight = (string) data_get($rate->config_json, 'ranges.0.min_g', 0);
        $this->maximumWeight = data_get($rate->config_json, 'ranges.0.max_g') !== null
            ? (string) data_get($rate->config_json, 'ranges.0.max_g')
            : '';
        $this->minimumOrderAmount = number_format(((int) data_get($rate->config_json, 'ranges.0.min_amount', 0)) / 100, 2, '.', '');
        $this->maximumOrderAmount = data_get($rate->config_json, 'ranges.0.max_amount') !== null
            ? number_format(((int) data_get($rate->config_json, 'ranges.0.max_amount')) / 100, 2, '.', '')


Output:
Chunk ID: 4c7939
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1347
Output:
<?php

namespace App\Livewire\Admin\Settings;

use App\Enums\TaxMode;
use App\Models\Store;
use App\Models\TaxSettings;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Taxes extends Component
{
    use AuthorizesRequests;

    #[Locked]
    public int $storeId;

    public string $mode = 'manual';

    public bool $pricesIncludeTax = false;

    public string $provider = 'none';

    public string $providerApiKey = '';

    /**
     * @var array<int, array{country: string, name: string, rate_percentage: string}>
     */
    public array $manualRates = [];

    public function mount(): void
    {
        $store = $this->store();

        $this->authorize('update', $store);

        $this->storeId = $store->getKey();
        $this->fillFromSettings($this->taxSettings($store));
    }

    public function addManualRate(): void
    {
        $this->manualRates[] = [
            'country' => '',
            'name' => 'Tax',
            'rate_percentage' => '0.00',
        ];
    }

    public function removeManualRate(int $index): void
    {
        unset($this->manualRates[$index]);

        $this->manualRates = array_values($this->manualRates);
    }

    public function save(): void
    {
        $store = $this->scopedStore();

        $this->authorize('update', $store);

        $validated = $this->validate([
            'mode' => ['required', Rule::in(array_column(TaxMode::cases(), 'value'))],
            'pricesIncludeTax' => ['boolean'],
            'provider' => ['required', Rule::in(['none', 'stripe_tax'])],
            'providerApiKey' => ['nullable', 'string', 'max:255'],
            'manualRates' => ['array'],
            'manualRates.*.country' => ['required', 'string', 'size:2'],
            'manualRates.*.name' => ['required', 'string', 'max:50'],
            'manualRates.*.rate_percentage' => ['required', 'numeric', 'min:0', 'max:100'],
        ]);

        TaxSettings::withoutGlobalScopes()->updateOrCreate(
            ['store_id' => $store->getKey()],
            [
                'mode' => TaxMode::from($validated['mode']),
                'provider' => $this->mode === TaxMode::Provider->value ? $this->provider : 'none',
                'prices_include_tax' => $this->pricesIncludeTax,
                'config_json' => [
                    'name' => 'Tax',
                    'default_rate_bps' => $this->firstRateBasisPoints(),
                    'shipping_taxable' => true,
                    'provider_api_key' => $this->mode === TaxMode::Provider->value ? $this->providerApiKey : null,
                    'rates' => collect($this->manualRates)
                        ->map(fn (array $rate): array => [
                            'country' => strtoupper($rate['country']),
                            'rate_bps' => (int) round(((float) $rate['rate_percentage']) * 100),
                            'name' => $rate['name'],
                        ])
                        ->values()
                        ->all(),
                ],
            ],
        );

        session()->flash('status', 'Tax settings saved');
        $this->dispatch('toast', type: 'success', message: __('Tax settings saved'));
    }

    public function render(): mixed
    {
        return view('livewire.admin.settings.taxes')
            ->layout('layouts.app', [
                'title' => __('Taxes'),
            ]);
    }

    private function store(): Store
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        return $store;
    }

    private function scopedStore(): Store
    {
        return Store::query()->whereKey($this->storeId)->firstOrFail();
    }

    private function taxSettings(Store $store): TaxSettings
    {
        return TaxSettings::withoutGlobalScopes()->firstOrCreate(
            ['store_id' => $store->getKey()],
            [
                'mode' => TaxMode::Manual,
                'provider' => 'none',
                'prices_include_tax' => false,
                'config_json' => [
                    'name' => 'Tax',
                    'default_rate_bps' => 0,
                    'shipping_taxable' => true,
                    'rates' => [],
                ],
            ],
        );
    }

    private function fillFromSettings(TaxSettings $settings): void
    {
        $this->mode = $settings->mode->value;
        $this->pricesIncludeTax = $settings->prices_include_tax;
        $this->provider = $settings->provider;
        $this->providerApiKey = (string) data_get($settings->config_json, 'provider_api_key', '');
        $this->manualRates = collect(data_get($settings->config_json, 'rates', []))
            ->map(fn (array $rate): array => [
                'country' => (string) data_get($rate, 'country', ''),
                'name' => (string) data_get($rate, 'name', 'Tax'),
                'rate_percentage' => number_format(((int) data_get($rate, 'rate_bps', 0)) / 100, 2, '.', ''),
            ])
            ->values()
            ->all();

        if ($this->manualRates === []) {
            $this->addManualRate();
        }
    }

    private function firstRateBasisPoints(): int
    {
        $first = $this->manualRates[0]['rate_percentage'] ?? '0';

        return (int) round(((float) $first) * 100);
    }
}


Output:
Chunk ID: 4cac7e
Wall time: 0.0000 seconds
Process exited with code 1
Original token count: 307
Output:
sed: database/seeders/ShippingSeeder.php: No such file or directory
<?php

namespace Database\Seeders;

use App\Models\Store;
use App\Models\TaxSettings;
use Illuminate\Database\Seeder;

class TaxSettingsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            TaxSettings::withoutGlobalScopes()->updateOrCreate(
                ['store_id' => $store->getKey()],
                [
                    'mode' => 'manual',
                    'provider' => 'none',
                    'prices_include_tax' => true,
                    'config_json' => [
                        'name' => 'VAT',
                        'default_rate_bps' => 1900,
                        'shipping_taxable' => true,
                        'rates' => [
                            ['country' => 'DE', 'rate_bps' => 1900, 'name' => 'VAT'],
                            ['country' => 'AT', 'rate_bps' => 2000, 'name' => 'VAT'],
                            ['country' => 'CH', 'rate_bps' => 770, 'name' => 'VAT'],
                        ],
                    ],
                ],
            );
        });
    }
}


sed -n '320,520p' app/Livewire/Admin/Settings/Shipping.php


sed -n '320,520p' app/Livewire/Admin/Settings/Shipping.php

     * @return array<string, mixed>
     */
    private function rateConfig(): array
    {
        $amount = Money::fromDecimalString($this->rateAmount);

        return match ($this->rateType) {
            ShippingRateType::Weight->value => [
                'ranges' => [[
                    'min_g' => (int) ($this->minimumWeight ?: 0),
                    'max_g' => $this->maximumWeight !== '' ? (int) $this->maximumWeight : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Price->value => [
                'ranges' => [[
                    'min_amount' => Money::fromDecimalString($this->minimumOrderAmount),
                    'max_amount' => $this->maximumOrderAmount !== '' ? Money::fromDecimalString($this->maximumOrderAmount) : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Carrier->value => ['amount' => $amount],
            default => ['amount' => $amount],
        };
    }

    private function fillRateConfig(ShippingRate $rate): void
    {
        $amount = match ($rate->type) {
            ShippingRateType::Flat, ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 0),
            default => (int) data_get($rate->config_json, 'ranges.0.amount', 0),
        };

        $this->rateAmount = number_format($amount / 100, 2, '.', '');
        $this->minimumWeight = (string) data_get($rate->config_json, 'ranges.0.min_g', 0);
        $this->maximumWeight = data_get($rate->config_json, 'ranges.0.max_g') !== null
            ? (string) data_get($rate->config_json, 'ranges.0.max_g')
            : '';
        $this->minimumOrderAmount = number_format(((int) data_get($rate->config_json, 'ranges.0.min_amount', 0)) / 100, 2, '.', '');
        $this->maximumOrderAmount = data_get($rate->config_json, 'ranges.0.max_amount') !== null
            ? number_format(((int) data_get($rate->config_json, 'ranges.0.max_amount')) / 100, 2, '.', '')
            : '';
    }

    private function resetZoneForm(): void
    {
        $this->editingZoneId = null;
        $this->zoneName = '';
        $this->zoneCountries = '';
    }

    private function resetRateForm(): void
    {
        $this->editingRateId = null;
        $this->rateZoneId = null;
        $this->rateName = '';
        $this->rateType = 'flat';
        $this->rateAmount = '0.00';
        $this->minimumWeight = '0';
        $this->maximumWeight = '';
        $this->minimumOrderAmount = '0.00';
        $this->maximumOrderAmount = '';
        $this->rateActive = true;
    }
}


Output:
Chunk ID: c5f274
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 653
Output:
     * @return array<string, mixed>
     */
    private function rateConfig(): array
    {
        $amount = Money::fromDecimalString($this->rateAmount);

        return match ($this->rateType) {
            ShippingRateType::Weight->value => [
                'ranges' => [[
                    'min_g' => (int) ($this->minimumWeight ?: 0),
                    'max_g' => $this->maximumWeight !== '' ? (int) $this->maximumWeight : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Price->value => [
                'ranges' => [[
                    'min_amount' => Money::fromDecimalString($this->minimumOrderAmount),
                    'max_amount' => $this->maximumOrderAmount !== '' ? Money::fromDecimalString($this->maximumOrderAmount) : null,
                    'amount' => $amount,
                ]],
            ],
            ShippingRateType::Carrier->value => ['amount' => $amount],
            default => ['amount' => $amount],
        };
    }

    private function fillRateConfig(ShippingRate $rate): void
    {
        $amount = match ($rate->type) {
            ShippingRateType::Flat, ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 0),
            default => (int) data_get($rate->config_json, 'ranges.0.amount', 0),
        };

        $this->rateAmount = number_format($amount / 100, 2, '.', '');
        $this->minimumWeight = (string) data_get($rate->config_json, 'ranges.0.min_g', 0);
        $this->maximumWeight = data_get($rate->config_json, 'ranges.0.max_g') !== null
            ? (string) data_get($rate->config_json, 'ranges.0.max_g')
            : '';
        $this->minimumOrderAmount = number_format(((int) data_get($rate->config_json, 'ranges.0.min_amount', 0)) / 100, 2, '.', '');
        $this->maximumOrderAmount = data_get($rate->config_json, 'ranges.0.max_amount') !== null
            ? number_format(((int) data_get($rate->config_json, 'ranges.0.max_amount')) / 100, 2, '.', '')
            : '';
    }

    private function resetZoneForm(): void
    {
        $this->editingZoneId = null;
        $this->zoneName = '';
        $this->zoneCountries = '';
    }

    private function resetRateForm(): void
    {
        $this->editingRateId = null;
        $this->rateZoneId = null;
        $this->rateName = '';
        $this->rateType = 'flat';
        $this->rateAmount = '0.00';
        $this->minimumWeight = '0';
        $this->maximumWeight = '';
        $this->minimumOrderAmount = '0.00';
        $this->maximumOrderAmount = '';
        $this->rateActive = true;
    }
}


rg --files database/seeders | rg "Shipping|shipping" && rg -n "Domestic|Standard Shipping|ShippingRate" database/seeders


rg --files database/seeders | rg "Shipping|shipping" && rg -n "Domestic|Standard Shipping|ShippingRate" database/seeders

Question: Shipping|shipping

rg -n 'Domestic|Standard Shipping|ShippingRate' database/seeders

Question: Domestic|Standard Shipping|ShippingRate

database/seeders/ShippingZoneSeeder.php
database/seeders/ShippingRateSeeder.php
database/seeders/DatabaseSeeder.php:33:            ShippingRateSeeder::class,
database/seeders/ShippingZoneSeeder.php:24:                    'name' => 'Domestic',
database/seeders/ShippingRateSeeder.php:5:use App\Models\ShippingRate;
database/seeders/ShippingRateSeeder.php:9:class ShippingRateSeeder extends Seeder
database/seeders/ShippingRateSeeder.php:18:                ShippingRate::withoutGlobalScopes()->updateOrCreate(
database/seeders/ShippingRateSeeder.php:39:            'Domestic' => [
database/seeders/ShippingRateSeeder.php:40:                ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 499]],


Output:
Chunk ID: ceaaca
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 182
Output:
database/seeders/ShippingZoneSeeder.php
database/seeders/ShippingRateSeeder.php
database/seeders/DatabaseSeeder.php:33:            ShippingRateSeeder::class,
database/seeders/ShippingZoneSeeder.php:24:                    'name' => 'Domestic',
database/seeders/ShippingRateSeeder.php:5:use App\Models\ShippingRate;
database/seeders/ShippingRateSeeder.php:9:class ShippingRateSeeder extends Seeder
database/seeders/ShippingRateSeeder.php:18:                ShippingRate::withoutGlobalScopes()->updateOrCreate(
database/seeders/ShippingRateSeeder.php:39:            'Domestic' => [
database/seeders/ShippingRateSeeder.php:40:                ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 499]],


sed -n '1,140p' database/seeders/ShippingZoneSeeder.php


sed -n '1,140p' database/seeders/ShippingRateSeeder.php


sed -n '1,140p' database/seeders/ShippingRateSeeder.php

<?php

namespace Database\Seeders;

use App\Models\ShippingRate;
use App\Models\ShippingZone;
use Illuminate\Database\Seeder;

class ShippingRateSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        ShippingZone::withoutGlobalScopes()->get()->each(function (ShippingZone $zone): void {
            foreach ($this->ratesForZone($zone) as $rate) {
                ShippingRate::withoutGlobalScopes()->updateOrCreate(
                    [
                        'zone_id' => $zone->getKey(),
                        'name' => $rate['name'],
                    ],
                    [
                        'type' => $rate['type'],
                        'config_json' => $rate['config_json'],
                        'is_active' => true,
                    ],
                );
            }
        });
    }

    /**
     * @return array<int, array{name: string, type: string, config_json: array<string, mixed>}>
     */
    private function ratesForZone(ShippingZone $zone): array
    {
        return match ($zone->name) {
            'Domestic' => [
                ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 499]],
                ['name' => 'Express Shipping', 'type' => 'flat', 'config_json' => ['amount' => 999]],
                [
                    'name' => 'Free Shipping Over 75',
                    'type' => 'price',
                    'config_json' => [
                        'ranges' => [
                            ['min_amount' => 0, 'max_amount' => 7499, 'amount' => 499],
                            ['min_amount' => 7500, 'amount' => 0],
                        ],
                    ],
                ],
            ],
            'International' => [
                ['name' => 'International Shipping', 'type' => 'flat', 'config_json' => ['amount' => 1499]],
            ],
            default => [],
        };
    }
}


sed -n '1,140p' database/seeders/ShippingZoneSeeder.php

<?php

namespace Database\Seeders;

use App\Models\ShippingZone;
use App\Models\Store;
use Illuminate\Database\Seeder;

class ShippingZoneSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            ShippingZone::withoutGlobalScopes()
                ->where('store_id', $store->getKey())
                ->delete();

            ShippingZone::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'name' => 'Domestic',
                ],
                [
                    'countries_json' => ['DE'],
                    'regions_json' => [],
                ],
            );

            ShippingZone::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'name' => 'International',
                ],
                [
                    'countries_json' => ['AT', 'CH', 'US', 'GB', 'CA', 'AU'],
                    'regions_json' => [],
                ],
            );
        });
    }
}


Output:
Chunk ID: cdb795
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 295
Output:
<?php

namespace Database\Seeders;

use App\Models\ShippingZone;
use App\Models\Store;
use Illuminate\Database\Seeder;

class ShippingZoneSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            ShippingZone::withoutGlobalScopes()
                ->where('store_id', $store->getKey())
                ->delete();

            ShippingZone::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'name' => 'Domestic',
                ],
                [
                    'countries_json' => ['DE'],
                    'regions_json' => [],
                ],
            );

            ShippingZone::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'name' => 'International',
                ],
                [
                    'countries_json' => ['AT', 'CH', 'US', 'GB', 'CA', 'AU'],
                    'regions_json' => [],
                ],
            );
        });
    }
}


Output:
Chunk ID: 881aa9
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 490
Output:
<?php

namespace Database\Seeders;

use App\Models\ShippingRate;
use App\Models\ShippingZone;
use Illuminate\Database\Seeder;

class ShippingRateSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        ShippingZone::withoutGlobalScopes()->get()->each(function (ShippingZone $zone): void {
            foreach ($this->ratesForZone($zone) as $rate) {
                ShippingRate::withoutGlobalScopes()->updateOrCreate(
                    [
                        'zone_id' => $zone->getKey(),
                        'name' => $rate['name'],
                    ],
                    [
                        'type' => $rate['type'],
                        'config_json' => $rate['config_json'],
                        'is_active' => true,
                    ],
                );
            }
        });
    }

    /**
     * @return array<int, array{name: string, type: string, config_json: array<string, mixed>}>
     */
    private function ratesForZone(ShippingZone $zone): array
    {
        return match ($zone->name) {
            'Domestic' => [
                ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 499]],
                ['name' => 'Express Shipping', 'type' => 'flat', 'config_json' => ['amount' => 999]],
                [
                    'name' => 'Free Shipping Over 75',
                    'type' => 'price',
                    'config_json' => [
                        'ranges' => [
                            ['min_amount' => 0, 'max_amount' => 7499, 'amount' => 499],
                            ['min_amount' => 7500, 'amount' => 0],
                        ],
                    ],
                ],
            ],
            'International' => [
                ['name' => 'International Shipping', 'type' => 'flat', 'config_json' => ['amount' => 1499]],
            ],
            default => [],
        };
    }
}


php artisan make:test --pest Browser/Admin/SettingsTest --no-interaction


Output:
Chunk ID: 1f3a7b
Wall time: 0.1540 seconds
Process exited with code 0
Original token count: 22
Output:

   INFO  Test [tests/Feature/Browser/Admin/SettingsTest.php] created successfully.


php artisan make:test --pest Browser/Admin/SettingsTest --no-interaction

INFO  Test [tests/Feature/Browser/Admin/SettingsTest.php] created successfully.


Output:
Chunk ID: cba07b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 0
Output:


I’m adding minimal `data-test` hooks to the settings save/rate controls and writing Suite 6 with direct authenticated visits. The assertions will also hit the database for persistence, so the browser checks are not just visual.