command: sed -n '1,260p' tests/Feature/Admin/CustomerManagementTest.php

aggregated_output:
<?php

use App\Livewire\Admin\Customers\Index as AdminCustomersIndex;
use App\Livewire\Admin\Customers\Show as AdminCustomerShow;
use App\Models\Customer;
use App\Models\CustomerAddress;
use App\Models\Order;
use App\Models\Store;
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 adminCustomerManagementStore(): Store
{
    $store = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    app()->instance('current_store', $store);

    return $store;
}

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

function adminCustomerManagementCustomer(Store $store, string $email, string $name): Customer
{
    return Customer::factory()->create([
        'store_id' => $store->getKey(),
        'email' => $email,
        'name' => $name,
    ]);
}

function adminCustomerManagementOrder(Store $store, Customer $customer, string $orderNumber, int $totalAmount): Order
{
    return Order::factory()->paid()->create([
        'store_id' => $store->getKey(),
        'customer_id' => $customer->getKey(),
        'order_number' => $orderNumber,
        'subtotal_amount' => $totalAmount,
        'shipping_amount' => 0,
        'tax_amount' => 0,
        'total_amount' => $totalAmount,
        'email' => $customer->email,
    ]);
}

test('admin customer routes require authentication and render store scoped customers', function (): void {
    $store = adminCustomerManagementStore();
    $customer = adminCustomerManagementCustomer($store, 'jane@example.test', 'Jane Example');
    adminCustomerManagementOrder($store, $customer, '#8101', 7000);
    CustomerAddress::factory()->default()->create([
        'customer_id' => $customer->getKey(),
        'label' => 'Home',
    ]);

    $otherStore = Store::factory()->create();
    $otherCustomer = adminCustomerManagementCustomer($otherStore, 'other@example.test', 'Other Customer');

    $this->get('/admin/customers')->assertRedirect('/admin/login');

    $this->actingAs(adminCustomerManagementUser())
        ->withSession(['current_store_id' => $store->getKey()])
        ->get('/admin/customers')
        ->assertSuccessful()
        ->assertSee('Jane Example')
        ->assertSee('70.00 EUR')
        ->assertDontSee('Other Customer');

    $this->actingAs(adminCustomerManagementUser())
        ->withSession(['current_store_id' => $store->getKey()])
        ->get('/admin/customers/'.$customer->getKey())
        ->assertSuccessful()
        ->assertSee('Jane Example')
        ->assertSee('#8101')
        ->assertSee('Home');

    $this->actingAs(adminCustomerManagementUser())
        ->withSession(['current_store_id' => $store->getKey()])
        ->get('/admin/customers/'.$otherCustomer->getKey())
        ->assertNotFound();
});

test('admin customer index filters by name and email', function (): void {
    $store = adminCustomerManagementStore();
    $user = adminCustomerManagementUser();
    adminCustomerManagementCustomer($store, 'jane@example.test', 'Jane Example');
    adminCustomerManagementCustomer($store, 'bravo@example.test', 'Bravo Example');

    Livewire::actingAs($user)
        ->test(AdminCustomersIndex::class)
        ->assertSee('Jane Example')
        ->assertSee('Bravo Example')
        ->set('search', 'jane')
        ->assertSee('Jane Example')
        ->assertDontSee('Bravo Example')
        ->set('search', 'bravo@example.test')
        ->assertSee('Bravo Example')
        ->assertDontSee('Jane Example');
});

test('admin customer detail manages addresses', function (): void {
    $store = adminCustomerManagementStore();
    $user = adminCustomerManagementUser();
    $customer = adminCustomerManagementCustomer($store, 'jane@example.test', 'Jane Example');
    $home = CustomerAddress::factory()->default()->create([
        'customer_id' => $customer->getKey(),
        'label' => 'Home',
    ]);

    Livewire::actingAs($user)
        ->test(AdminCustomerShow::class, ['customer' => $customer])
        ->call('openAddressForm')
        ->set('addressLabel', 'Office')
        ->set('addressJson.address1', 'Business Street 5')
        ->set('addressJson.city', 'Berlin')
        ->set('addressJson.postal_code', '10115')
        ->set('addressJson.country', 'DE')
        ->call('saveAddress')
        ->assertHasNoErrors();

    $office = CustomerAddress::query()
        ->where('customer_id', $customer->getKey())
        ->where('label', 'Office')
        ->firstOrFail();

    expect($office->is_default)->toBeFalse();

    Livewire::actingAs($user)
        ->test(AdminCustomerShow::class, ['customer' => $customer])
        ->call('setDefaultAddress', $office->getKey())
        ->assertHasNoErrors()
        ->call('openAddressForm', $office->getKey())
        ->set('addressLabel', 'HQ')
        ->call('saveAddress')
        ->assertHasNoErrors()
        ->call('deleteAddress', $home->getKey())
        ->assertHasNoErrors();

    expect($office->refresh()->is_default)->toBeTrue()
        ->and($office->label)->toBe('HQ')
        ->and(CustomerAddress::query()->whereKey($home->getKey())->exists())->toBeFalse();
});

test('admin customer detail rejects customers from another store', function (): void {
    $store = adminCustomerManagementStore();
    $otherStore = Store::factory()->create();
    $otherCustomer = adminCustomerManagementCustomer($otherStore, 'other@example.test', 'Other Customer');

    app()->instance('current_store', $store);

    Livewire::actingAs(adminCustomerManagementUser())
        ->test(AdminCustomerShow::class, ['customer' => $otherCustomer])
        ->assertStatus(404);
});


output:
Chunk ID: f90ec2
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 575
Output:
test('can process a refund', function (): void {
    adminOrderOpenOrder($this, '#1001')
        ->click('button[data-test="refund-modal-button"]')
        ->wait(1)
        ->fill('input[wire\\:model="refundAmount"]', '10.00')
        ->fill('textarea[wire\\:model="refundReason"]', 'Customer requested partial refund')
        ->click('button[data-test="refund-submit-button"]')
        ->wait(1)
        ->assertSee('Refund processed')
        ->assertSee('Partially Refunded')
        ->assertNoJavaScriptErrors();
});

test('shows customer information in order detail', function (): void {
    adminOrderOpenOrder($this, '#1001')
        ->assertSee('customer@acme.test')
        ->assertNoJavaScriptErrors();
});

test('can confirm bank transfer payment', function (): void {
    adminOrderOpenOrder($this, '#1005')
        ->assertSee('Pending')
        ->assertPresent('button[data-test="confirm-payment-button"]')
        ->click('button[data-test="confirm-payment-button"]')
        ->wait(1)
        ->assertSee('Payment confirmed')
        ->assertSee('Paid')
        ->assertDontSee('Confirm payment')
        ->assertNoJavaScriptErrors();
});

test('shows fulfillment guard for unpaid order', function (): void {
    adminOrderOpenOrder($this, '#1005')
        ->assertSee('Cannot create fulfillment')
        ->assertSee('Payment must be confirmed before items can be fulfilled')
        ->assertScript('document.querySelector("button[data-test=\"fulfillment-modal-button\"]") === null')
        ->assertNoJavaScriptErrors();
});

test('can mark fulfillment as shipped', function (): void {
    $page = adminOrderCreateFulfillmentInBrowser(adminOrderOpenOrder($this, '#1001'));

    $page->click('button[data-test="mark-fulfillment-shipped-button"]')
        ->wait(1)
        ->assertSee('Shipped')
        ->assertNoJavaScriptErrors();
});

test('can mark fulfillment as delivered', function (): void {
    $page = adminOrderCreateFulfillmentInBrowser(adminOrderOpenOrder($this, '#1001'));

    $page->click('button[data-test="mark-fulfillment-shipped-button"]')
        ->wait(1)
        ->click('button[data-test="mark-fulfillment-delivered-button"]')
        ->wait(1)
        ->assertSee('Delivered')
        ->assertSee('Fulfilled')
        ->assertNoJavaScriptErrors();
});


output:
Chunk ID: 2429a3
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1224
Output:
<?php

use App\Enums\FinancialStatus;
use App\Enums\FulfillmentStatus;
use App\Enums\OrderStatus;
use App\Enums\PaymentMethod;
use App\Enums\PaymentStatus;
use App\Models\Customer;
use App\Models\Order;
use App\Models\OrderLine;
use App\Models\Payment;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\Store;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Pest\Browser\Playwright\Playwright;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    Playwright::setHost('shop.test');

    $this->seed(DatabaseSeeder::class);
});

afterEach(function (): void {
    Playwright::setHost(null);
});

function adminCustomerBrowserHost(): array
{
    return ['host' => 'shop.test'];
}

function adminCustomerBrowserAuthenticate(mixed $testCase): Store
{
    $store = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    $user = User::query()->where('email', 'admin@acme.test')->firstOrFail();

    $testCase->actingAs($user);
    $testCase->withSession(['current_store_id' => $store->getKey()]);

    return $store;
}

function adminCustomerBrowserCustomer(Store $store): Customer
{
    return Customer::withoutGlobalScopes()
        ->where('store_id', $store->getKey())
        ->where('email', 'customer@acme.test')
        ->firstOrFail();
}

function adminCustomerBrowserCreateOrder(Store $store, Customer $customer): Order
{
    $product = Product::withoutGlobalScopes()
        ->where('store_id', $store->getKey())
        ->where('handle', 'classic-cotton-t-shirt')
        ->firstOrFail();
    $variant = ProductVariant::withoutGlobalScopes()
        ->where('product_id', $product->getKey())
        ->firstOrFail();
    $shipping = 499;
    $total = $variant->price_amount + $shipping;

    $order = Order::factory()->create([
        'store_id' => $store->getKey(),
        'customer_id' => $customer->getKey(),
        'order_number' => '#1001',
        'payment_method' => PaymentMethod::CreditCard,
        'status' => OrderStatus::Paid,
        'financial_status' => FinancialStatus::Paid,
        'fulfillment_status' => FulfillmentStatus::Unfulfilled,
        'currency' => $store->default_currency,
        'subtotal_amount' => $variant->price_amount,
        'discount_amount' => 0,
        'shipping_amount' => $shipping,
        'tax_amount' => 0,
        'total_amount' => $total,
        'email' => $customer->email,
        'placed_at' => now()->subDay(),
    ]);

    OrderLine::factory()->create([
        'order_id' => $order->getKey(),
        'product_id' => $product->getKey(),
        'variant_id' => $variant->getKey(),
        'title_snapshot' => 'Classic Cotton T-Shirt',
        'sku_snapshot' => $variant->sku,
        'quantity' => 1,
        'unit_price_amount' => $variant->price_amount,
        'total_amount' => $variant->price_amount,
    ]);

    Payment::factory()->create([
        'order_id' => $order->getKey(),
        'method' => PaymentMethod::CreditCard,
        'status' => PaymentStatus::Captured,
        'amount' => $total,
        'currency' => $store->default_currency,
    ]);

    return $order->refresh();
}

function adminCustomerBrowserOpenCustomers(mixed $testCase): mixed
{
    adminCustomerBrowserAuthenticate($testCase);

    return visit('/admin/customers', adminCustomerBrowserHost())
        ->wait(1)
        ->assertPathIs('/admin/customers')
        ->assertSee('Customers')
        ->assertNoJavaScriptErrors();
}

function adminCustomerBrowserOpenCustomer(mixed $testCase): mixed
{
    return adminCustomerBrowserOpenCustomers($testCase)
        ->assertSee('John Doe')
        ->click('a:has-text("John Doe")')
        ->wait(1)
        ->assertPathContains('/admin/customers/')
        ->assertNoJavaScriptErrors();
}

test('shows the customer list', function (): void {
    adminCustomerBrowserOpenCustomers($this)
        ->assertSee('customer@acme.test')
        ->assertSee('John Doe')
        ->assertNoJavaScriptErrors();
});

test('shows customer detail with order history', function (): void {
    $store = adminCustomerBrowserAuthenticate($this);
    $customer = adminCustomerBrowserCustomer($store);
    adminCustomerBrowserCreateOrder($store, $customer);

    visit('/admin/customers', adminCustomerBrowserHost())
        ->wait(1)
        ->assertPathIs('/admin/customers')
        ->click('a:has-text("John Doe")')
        ->wait(1)
        ->assertPathContains('/admin/customers/')
        ->assertSee('John Doe')
        ->assertSee('customer@acme.test')
        ->assertSee('Order history')
        ->assertSee('#1001')
        ->assertNoJavaScriptErrors();
});

test('shows customer addresses', function (): void {
    adminCustomerBrowserOpenCustomer($this)
        ->assertSee('John Doe')
        ->assertSee('Addresses')
        ->assertSee('Home')
        ->assertNoJavaScriptErrors();
});


output:
Chunk ID: 16dd88
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1257
Output:
<?php

use App\Models\Customer;
use App\Models\Order;
use App\Models\Store;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Pest\Browser\Playwright\Playwright;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    Playwright::setHost('acme-fashion.test');

    $this->seed(DatabaseSeeder::class);
});

afterEach(function (): void {
    Playwright::setHost(null);
});

function tenantIsolationHost(): array
{
    return ['host' => 'acme-fashion.test'];
}

function tenantIsolationStore(string $handle): Store
{
    return Store::query()->where('handle', $handle)->firstOrFail();
}

function tenantIsolationFashionCustomer(): Customer
{
    $store = tenantIsolationStore('acme-fashion');

    return Customer::withoutGlobalScopes()
        ->where('store_id', $store->getKey())
        ->where('email', 'customer@acme.test')
        ->firstOrFail();
}

function tenantIsolationCreateOrders(): void
{
    $fashionStore = tenantIsolationStore('acme-fashion');
    $electronicsStore = tenantIsolationStore('acme-electronics');
    $fashionCustomer = tenantIsolationFashionCustomer();
    $electronicsCustomer = Customer::factory()->create([
        'store_id' => $electronicsStore->getKey(),
        'email' => 'customer@acme.test',
        'password' => 'password',
        'name' => 'Electronics Customer',
    ]);

    foreach (['#1001', '#1002', '#1004'] as $orderNumber) {
        Order::factory()
            ->forCustomer($fashionCustomer)
            ->paid()
            ->create([
                'store_id' => $fashionStore->getKey(),
                'customer_id' => $fashionCustomer->getKey(),
                'order_number' => $orderNumber,
                'email' => $fashionCustomer->email,
            ]);
    }

    Order::factory()
        ->forCustomer($electronicsCustomer)
        ->paid()
        ->create([
            'store_id' => $electronicsStore->getKey(),
            'customer_id' => $electronicsCustomer->getKey(),
            'order_number' => '#2001',
            'email' => $electronicsCustomer->email,
        ]);
}

function tenantIsolationAdminLogin(): mixed
{
    return visit('/admin/login', tenantIsolationHost())
        ->fill('input[type=email]', 'admin@acme.test')
        ->fill('input[type=password]', 'password')
        ->click('@admin-login-button')
        ->wait(1)
        ->assertPathIs('/admin')
        ->assertSee('Dashboard')
        ->assertNoJavaScriptErrors();
}

function tenantIsolationCustomerLogin(): mixed
{
    return visit('/account/login', tenantIsolationHost())
        ->fill('input[type=email]', 'customer@acme.test')
        ->fill('input[type=password]', 'password')
        ->click('@customer-login-button')
        ->wait(1)
        ->assertPathIs('/account')
        ->assertSee('My Account')
        ->assertSee('John Doe')
        ->assertNoJavaScriptErrors();
}

test('storefront only shows current store products', function (): void {
    visit('/', tenantIsolationHost())
        ->assertSee('Acme Fashion')
        ->assertSee('Classic Cotton T-Shirt')
        ->assertDontSee('Pro Laptop 15')
        ->assertDontSee('Wireless Headphones')
        ->assertNoJavaScriptErrors();
});

test('storefront collections only contain current store products', function (): void {
    visit('/collections/t-shirts', tenantIsolationHost())
        ->assertSee('T-Shirts')
        ->assertSee('Classic Cotton T-Shirt')
        ->assertDontSee('Pro Laptop 15')
        ->assertDontSee('Wireless Headphones')
        ->assertNoJavaScriptErrors();
});

test('admin cannot see other store products or orders', function (): void {
    tenantIsolationCreateOrders();

    tenantIsolationAdminLogin()
        ->click('a[href$="/admin/products"]')
        ->wait(1)
        ->assertPathIs('/admin/products')
        ->assertSee('Products')
        ->assertDontSee('Pro Laptop 15')
        ->assertDontSee('Wireless Headphones')
        ->click('a[href$="/admin/orders"]')
        ->wait(1)
        ->assertPathIs('/admin/orders')
        ->assertSee('#1001')
        ->assertDontSee('#2001')
        ->assertNoJavaScriptErrors();
});

test('search only returns current store products', function (): void {
    visit('/search?q=cotton', tenantIsolationHost())
        ->assertSee('Classic Cotton T-Shirt')
        ->assertDontSee('Pro Laptop 15')
        ->assertNoJavaScriptErrors();

    visit('/search?q=laptop', tenantIsolationHost())
        ->assertDontSee('Pro Laptop 15')
        ->assertSee('No products found')
        ->assertNoJavaScriptErrors();
});

test('customer accounts are scoped to their store', function (): void {
    tenantIsolationCreateOrders();

    tenantIsolationCustomerLogin()
        ->click('nav[aria-label="Account navigation"] a[href$="/account/orders"]')
        ->wait(1)
        ->assertPathIs('/account/orders')
        ->assertSee('#1001')
        ->assertSee('#1002')
        ->assertSee('#1004')
        ->assertDontSee('#2001')
        ->assertNoJavaScriptErrors();
});


output:
Chunk ID: 2a34a2
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1975
Output:
<?php

use App\Enums\FinancialStatus;
use App\Enums\FulfillmentShipmentStatus;
use App\Enums\FulfillmentStatus;
use App\Enums\OrderStatus;
use App\Enums\PaymentMethod;
use App\Enums\PaymentStatus;
use App\Livewire\Admin\Orders\Index as AdminOrdersIndex;
use App\Livewire\Admin\Orders\Show as AdminOrderShow;
use App\Models\Fulfillment;
use App\Models\InventoryItem;
use App\Models\Order;
use App\Models\OrderLine;
use App\Models\Payment;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\Store;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

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

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

    return $store;
}

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

/**
 * @return array{0: Order, 1: OrderLine, 2: ProductVariant}
 */
function adminOrderManagementOrder(Store $store, array $orderAttributes = [], int $quantity = 2, int $unitPrice = 2500): array
{
    $product = Product::factory()
        ->withDefaultVariant($unitPrice)
        ->create(['store_id' => $store->getKey()]);
    $variant = ProductVariant::withoutGlobalScopes()
        ->where('product_id', $product->getKey())
        ->firstOrFail();

    InventoryItem::withoutGlobalScopes()
        ->where('variant_id', $variant->getKey())
        ->update([
            'quantity_on_hand' => 10,
            'quantity_reserved' => 0,
        ]);

    $total = $quantity * $unitPrice;
    $order = Order::factory()->paid()->create(array_merge([
        'store_id' => $store->getKey(),
        'subtotal_amount' => $total,
        'shipping_amount' => 0,
        'tax_amount' => 0,
        'total_amount' => $total,
        'email' => 'buyer@example.test',
    ], $orderAttributes));
    $line = OrderLine::factory()->create([
        'order_id' => $order->getKey(),
        'product_id' => $product->getKey(),
        'variant_id' => $variant->getKey(),
        'title_snapshot' => 'Admin Test Product',
        'sku_snapshot' => 'ADMIN-TEST-001',
        'quantity' => $quantity,
        'unit_price_amount' => $unitPrice,
        'total_amount' => $total,
    ]);

    Payment::factory()->create([
        'order_id' => $order->getKey(),
        'method' => $order->payment_method,
        'status' => $order->financial_status === FinancialStatus::Pending ? PaymentStatus::Pending : PaymentStatus::Captured,
        'amount' => $total,
        'currency' => $order->currency,
    ]);

    return [$order, $line, $variant];
}

test('admin order routes require authentication and render scoped orders', function () {
    $store = adminOrderManagementStore();
    [$order] = adminOrderManagementOrder($store, [
        'order_number' => '#7001',
        'email' => 'alpha@example.test',
    ]);

    $otherStore = Store::factory()->create();
    adminOrderManagementOrder($otherStore, [
        'order_number' => '#9001',
        'email' => 'other@example.test',
    ]);

    $this->get('/admin/orders')->assertRedirect('/admin/login');

    $user = adminOrderManagementUser();

    $this->actingAs($user)
        ->get('/admin/orders')
        ->assertSuccessful()
        ->assertSee('#7001')
        ->assertDontSee('#9001');

    $this->actingAs($user)
        ->get('/admin/orders/'.$order->getKey())
        ->assertSuccessful()
        ->assertSee('#7001')
        ->assertSee('Admin Test Product');
});

test('admin order index filters by search and status dimensions', function () {
    $store = adminOrderManagementStore();
    $user = adminOrderManagementUser();
    adminOrderManagementOrder($store, [
        'order_number' => '#7001',
        'email' => 'alpha@example.test',
    ]);
    adminOrderManagementOrder($store, [
        'order_number' => '#7002',
        'email' => 'bravo@example.test',
        'status' => OrderStatus::Pending,
        'financial_status' => FinancialStatus::Pending,
        'payment_method' => PaymentMethod::BankTransfer,
    ]);

    Livewire::actingAs($user)
        ->test(AdminOrdersIndex::class)
        ->assertSee('#7001')
        ->assertSee('#7002')
        ->set('search', 'alpha')
        ->assertSee('#7001')
        ->assertDontSee('#7002')
        ->set('search', '')
        ->set('financialStatusFilter', 'pending')
        ->assertSee('#7002')
        ->assertDontSee('#7001');
});

test('admin order detail confirms bank transfer payments', function () {
    $store = adminOrderManagementStore();
    $user = adminOrderManagementUser();
    [$order, $line, $variant] = adminOrderManagementOrder($store, [
        'payment_method' => PaymentMethod::BankTransfer,
        'status' => OrderStatus::Pending,
        'financial_status' => FinancialStatus::Pending,
    ]);

    InventoryItem::withoutGlobalScopes()
        ->where('variant_id', $variant->getKey())
        ->update(['quantity_reserved' => $line->quantity]);

    Livewire::actingAs($user)
        ->test(AdminOrderShow::class, ['order' => $order])
        ->call('confirmBankTransferPayment')
        ->assertHasNoErrors();

    $inventory = InventoryItem::withoutGlobalScopes()->where('variant_id', $variant->getKey())->firstOrFail();

    expect($order->refresh()->status)->toBe(OrderStatus::Paid)
        ->and($order->financial_status)->toBe(FinancialStatus::Paid)
        ->and($order->payments()->first()?->status)->toBe(PaymentStatus::Captured)
        ->and($inventory->quantity_on_hand)->toBe(8)
        ->and($inventory->quantity_reserved)->toBe(0);
});

test('admin order detail processes refunds and fulfillment transitions', function () {
    $store = adminOrderManagementStore();
    $user = adminOrderManagementUser();
    [$order, $line] = adminOrderManagementOrder($store, quantity: 2, unitPrice: 2500);

    Livewire::actingAs($user)
        ->test(AdminOrderShow::class, ['order' => $order])
        ->set('refundAmount', '10.00')
        ->set('refundReason', 'Customer return')
        ->call('processRefund')
        ->assertHasNoErrors();

    expect($order->refresh()->financial_status)->toBe(FinancialStatus::PartiallyRefunded)
        ->and($order->refunds()->first()?->amount)->toBe(1000);

    Livewire::actingAs($user)
        ->test(AdminOrderShow::class, ['order' => $order->refresh()])
        ->set("fulfillmentLineQuantities.{$line->getKey()}", 1)
        ->set('trackingCompany', 'DHL')
        ->set('trackingNumber', 'DHL123')
        ->call('createFulfillment')
        ->assertHasNoErrors();

    $fulfillment = Fulfillment::query()->where('order_id', $order->getKey())->firstOrFail();

    expect($order->refresh()->fulfillment_status)->toBe(FulfillmentStatus::Partial)
        ->and($fulfillment->lines()->first()?->quantity)->toBe(1);

    Livewire::actingAs($user)
        ->test(AdminOrderShow::class, ['order' => $order->refresh()])
        ->call('markFulfillmentShipped', $fulfillment->getKey())
        ->call('markFulfillmentDelivered', $fulfillment->getKey())
        ->assertHasNoErrors();

    expect($fulfillment->refresh()->status)->toBe(FulfillmentShipmentStatus::Delivered)
        ->and($fulfillment->delivered_at)->not->toBeNull();
});

test('admin order detail rejects orders from another store', function () {
    $store = adminOrderManagementStore();
    $otherStore = Store::factory()->create();
    $user = adminOrderManagementUser();
    [$otherOrder] = adminOrderManagementOrder($otherStore, [
        'order_number' => '#9001',
    ]);

    app()->instance('current_store', $store);

    Livewire::actingAs($user)
        ->test(AdminOrderShow::class, ['order' => $otherOrder])
        ->assertStatus(404);
});


output:
Chunk ID: eb7a49
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1460
Output:
<?php

use App\Livewire\Admin\Customers\Index as AdminCustomersIndex;
use App\Livewire\Admin\Customers\Show as AdminCustomerShow;
use App\Models\Customer;
use App\Models\CustomerAddress;
use App\Models\Order;
use App\Models\Store;
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 adminCustomerManagementStore(): Store
{
    $store = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    app()->instance('current_store', $store);

    return $store;
}

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

function adminCustomerManagementCustomer(Store $store, string $email, string $name): Customer
{
    return Customer::factory()->create([
        'store_id' => $store->getKey(),
        'email' => $email,
        'name' => $name,
    ]);
}

function adminCustomerManagementOrder(Store $store, Customer $customer, string $orderNumber, int $totalAmount): Order
{
    return Order::factory()->paid()->create([
        'store_id' => $store->getKey(),
        'customer_id' => $customer->getKey(),
        'order_number' => $orderNumber,
        'subtotal_amount' => $totalAmount,
        'shipping_amount' => 0,
        'tax_amount' => 0,
        'total_amount' => $totalAmount,
        'email' => $customer->email,
    ]);
}

test('admin customer routes require authentication and render store scoped customers', function (): void {
    $store = adminCustomerManagementStore();
    $customer = adminCustomerManagementCustomer($store, 'jane@example.test', 'Jane Example');
    adminCustomerManagementOrder($store, $customer, '#8101', 7000);
    CustomerAddress::factory()->default()->create([
        'customer_id' => $customer->getKey(),
        'label' => 'Home',
    ]);

    $otherStore = Store::factory()->create();
    $otherCustomer = adminCustomerManagementCustomer($otherStore, 'other@example.test', 'Other Customer');

    $this->get('/admin/customers')->assertRedirect('/admin/login');

    $this->actingAs(adminCustomerManagementUser())
        ->withSession(['current_store_id' => $store->getKey()])
        ->get('/admin/customers')
        ->assertSuccessful()
        ->assertSee('Jane Example')
        ->assertSee('70.00 EUR')
        ->assertDontSee('Other Customer');

    $this->actingAs(adminCustomerManagementUser())
        ->withSession(['current_store_id' => $store->getKey()])
        ->get('/admin/customers/'.$customer->getKey())
        ->assertSuccessful()
        ->assertSee('Jane Example')
        ->assertSee('#8101')
        ->assertSee('Home');

    $this->actingAs(adminCustomerManagementUser())
        ->withSession(['current_store_id' => $store->getKey()])
        ->get('/admin/customers/'.$otherCustomer->getKey())
        ->assertNotFound();
});

test('admin customer index filters by name and email', function (): void {
    $store = adminCustomerManagementStore();
    $user = adminCustomerManagementUser();
    adminCustomerManagementCustomer($store, 'jane@example.test', 'Jane Example');
    adminCustomerManagementCustomer($store, 'bravo@example.test', 'Bravo Example');

    Livewire::actingAs($user)
        ->test(AdminCustomersIndex::class)
        ->assertSee('Jane Example')
        ->assertSee('Bravo Example')
        ->set('search', 'jane')
        ->assertSee('Jane Example')
        ->assertDontSee('Bravo Example')
        ->set('search', 'bravo@example.test')
        ->assertSee('Bravo Example')
        ->assertDontSee('Jane Example');
});

test('admin customer detail manages addresses', function (): void {
    $store = adminCustomerManagementStore();
    $user = adminCustomerManagementUser();
    $customer = adminCustomerManagementCustomer($store, 'jane@example.test', 'Jane Example');
    $home = CustomerAddress::factory()->default()->create([
        'customer_id' => $customer->getKey(),
        'label' => 'Home',
    ]);

    Livewire::actingAs($user)
        ->test(AdminCustomerShow::class, ['customer' => $customer])
        ->call('openAddressForm')
        ->set('addressLabel', 'Office')
        ->set('addressJson.address1', 'Business Street 5')
        ->set('addressJson.city', 'Berlin')
        ->set('addressJson.postal_code', '10115')
        ->set('addressJson.country', 'DE')
        ->call('saveAddress')
        ->assertHasNoErrors();

    $office = CustomerAddress::query()
        ->where('customer_id', $customer->getKey())
        ->where('label', 'Office')
        ->firstOrFail();

    expect($office->is_default)->toBeFalse();

    Livewire::actingAs($user)
        ->test(AdminCustomerShow::class, ['customer' => $customer])
        ->call('setDefaultAddress', $office->getKey())
        ->assertHasNoErrors()
        ->call('openAddressForm', $office->getKey())
        ->set('addressLabel', 'HQ')
        ->call('saveAddress')
        ->assertHasNoErrors()
        ->call('deleteAddress', $home->getKey())
        ->assertHasNoErrors();

    expect($office->refresh()->is_default)->toBeTrue()
        ->and($office->label)->toBe('HQ')
        ->and(CustomerAddress::query()->whereKey($home->getKey())->exists())->toBeFalse();
});

test('admin customer detail rejects customers from another store', function (): void {
    $store = adminCustomerManagementStore();
    $otherStore = Store::factory()->create();
    $otherCustomer = adminCustomerManagementCustomer($otherStore, 'other@example.test', 'Other Customer');

    app()->instance('current_store', $store);

    Livewire::actingAs(adminCustomerManagementUser())
        ->test(AdminCustomerShow::class, ['customer' => $otherCustomer])
        ->assertStatus(404);
});


cmd: sed -n '1,260p' database/seeders/DiscountSeeder.php


cmd: sed -n '1,220p' database/seeders/StoreSeeder.php


cmd: sed -n '1,240p' database/seeders/AnalyticsSeeder.php


cmd: sed -n '1,180p' app/Models/ProductVariant.php


cmd: sed -n '1,180p' app/Models/ProductOptionValue.php


command: sed -n '1,220p' database/seeders/StoreSeeder.php

StoreSeeder.php

type: read

aggregated_output:
<?php

namespace Database\Seeders;

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

class StoreSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $organization = Organization::query()->where('billing_email', 'billing@acme.test')->firstOrFail();

        foreach ([
            ['handle' => 'acme-fashion', 'name' => 'Acme Fashion'],
            ['handle' => 'acme-electronics', 'name' => 'Acme Electronics'],
        ] as $store) {
            Store::query()->updateOrCreate(
                ['handle' => $store['handle']],
                [
                    'organization_id' => $organization->getKey(),
                    'name' => $store['name'],
                    'status' => 'active',
                    'default_currency' => 'EUR',
                    'default_locale' => 'en',
                    'timezone' => 'Europe/Berlin',
                ],
            );
        }
    }
}

status: completed


command: sed -n '1,260p' database/seeders/DiscountSeeder.php

aggregated_output:
<?php

namespace Database\Seeders;

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

class DiscountSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            foreach ($this->discounts() as $discount) {
                Discount::withoutGlobalScopes()->updateOrCreate(
                    [
                        'store_id' => $store->getKey(),
                        'code' => $discount['code'],
                    ],
                    [
                        'type' => 'code',
                        'value_type' => $discount['value_type'],
                        'value_amount' => $discount['value_amount'],
                        'starts_at' => $discount['starts_at'] ?? now()->subDay(),
                        'ends_at' => $discount['ends_at'] ?? now()->addMonth(),
                        'usage_limit' => $discount['usage_limit'] ?? null,
                        'usage_count' => $discount['usage_count'] ?? 0,
                        'rules_json' => $discount['rules_json'],
                        'status' => $discount['status'] ?? 'active',
                    ],
                );
            }
        });
    }

    /**
     * @return array<int, array{code: string, value_type: string, value_amount: int, rules_json: array<string, mixed>, starts_at?: \Illuminate\Support\Carbon, ends_at?: \Illuminate\Support\Carbon, usage_limit?: int|null, usage_count?: int, status?: string}>
     */
    private function discounts(): array
    {
        return [
            ['code' => 'WELCOME10', 'value_type' => 'percent', 'value_amount' => 10, 'usage_count' => 3, 'rules_json' => ['min_purchase_amount' => 2000]],
            ['code' => 'FLAT5', 'value_type' => 'fixed', 'value_amount' => 500, 'rules_json' => []],
            ['code' => 'SAVE10', 'value_type' => 'percent', 'value_amount' => 10, 'rules_json' => ['customer_eligibility' => 'all']],
            ['code' => '5OFF', 'value_type' => 'fixed', 'value_amount' => 500, 'rules_json' => ['min_purchase_amount' => 2500, 'customer_eligibility' => 'all']],
            ['code' => 'FREESHIP', 'value_type' => 'free_shipping', 'value_amount' => 0, 'rules_json' => ['customer_eligibility' => 'all']],
            ['code' => 'EXPIRED20', 'value_type' => 'percent', 'value_amount' => 20, 'ends_at' => now()->subDay(), 'rules_json' => [], 'status' => 'expired'],
            ['code' => 'MAXED', 'value_type' => 'percent', 'value_amount' => 10, 'usage_limit' => 5, 'usage_count' => 5, 'rules_json' => []],
        ];
    }
}


output:
Chunk ID: fa7340
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 662
Output:
<?php

namespace Database\Seeders;

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

class DiscountSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            foreach ($this->discounts() as $discount) {
                Discount::withoutGlobalScopes()->updateOrCreate(
                    [
                        'store_id' => $store->getKey(),
                        'code' => $discount['code'],
                    ],
                    [
                        'type' => 'code',
                        'value_type' => $discount['value_type'],
                        'value_amount' => $discount['value_amount'],
                        'starts_at' => $discount['starts_at'] ?? now()->subDay(),
                        'ends_at' => $discount['ends_at'] ?? now()->addMonth(),
                        'usage_limit' => $discount['usage_limit'] ?? null,
                        'usage_count' => $discount['usage_count'] ?? 0,
                        'rules_json' => $discount['rules_json'],
                        'status' => $discount['status'] ?? 'active',
                    ],
                );
            }
        });
    }

    /**
     * @return array<int, array{code: string, value_type: string, value_amount: int, rules_json: array<string, mixed>, starts_at?: \Illuminate\Support\Carbon, ends_at?: \Illuminate\Support\Carbon, usage_limit?: int|null, usage_count?: int, status?: string}>
     */
    private function discounts(): array
    {
        return [
            ['code' => 'WELCOME10', 'value_type' => 'percent', 'value_amount' => 10, 'usage_count' => 3, 'rules_json' => ['min_purchase_amount' => 2000]],
            ['code' => 'FLAT5', 'value_type' => 'fixed', 'value_amount' => 500, 'rules_json' => []],
            ['code' => 'SAVE10', 'value_type' => 'percent', 'value_amount' => 10, 'rules_json' => ['customer_eligibility' => 'all']],
            ['code' => '5OFF', 'value_type' => 'fixed', 'value_amount' => 500, 'rules_json' => ['min_purchase_amount' => 2500, 'customer_eligibility' => 'all']],
            ['code' => 'FREESHIP', 'value_type' => 'free_shipping', 'value_amount' => 0, 'rules_json' => ['customer_eligibility' => 'all']],
            ['code' => 'EXPIRED20', 'value_type' => 'percent', 'value_amount' => 20, 'ends_at' => now()->subDay(), 'rules_json' => [], 'status' => 'expired'],
            ['code' => 'MAXED', 'value_type' => 'percent', 'value_amount' => 10, 'usage_limit' => 5, 'usage_count' => 5, 'rules_json' => []],
        ];
    }
}


output:
Chunk ID: 2505f5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 249
Output:
<?php

namespace Database\Seeders;

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

class StoreSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $organization = Organization::query()->where('billing_email', 'billing@acme.test')->firstOrFail();

        foreach ([
            ['handle' => 'acme-fashion', 'name' => 'Acme Fashion'],
            ['handle' => 'acme-electronics', 'name' => 'Acme Electronics'],
        ] as $store) {
            Store::query()->updateOrCreate(
                ['handle' => $store['handle']],
                [
                    'organization_id' => $organization->getKey(),
                    'name' => $store['name'],
                    'status' => 'active',
                    'default_currency' => 'EUR',
                    'default_locale' => 'en',
                    'timezone' => 'Europe/Berlin',
                ],
            );
        }
    }
}


command: sed -n '1,240p' database/seeders/AnalyticsSeeder.php

aggregated_output:
<?php

namespace Database\Seeders;

use App\Enums\AnalyticsEventType;
use App\Models\AnalyticsEvent;
use App\Models\Customer;
use App\Models\Store;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class AnalyticsSeeder extends Seeder
{
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            $this->seedDailyMetrics($store);

            if ($store->handle === 'acme-fashion') {
                $this->seedEventStream($store);
            }
        });
    }

    private function seedDailyMetrics(Store $store): void
    {
        foreach (range(0, 29) as $daysAgo) {
            $date = now()->subDays($daysAgo)->toDateString();
            $orders = max(0, 10 - ($daysAgo % 6));
            $revenue = $orders * (5200 + (($daysAgo % 5) * 800));

            DB::table('analytics_daily')->updateOrInsert(
                [
                    'store_id' => $store->getKey(),
                    'date' => $date,
                ],
                [
                    'orders_count' => $orders,
                    'revenue_amount' => $revenue,
                    'aov_amount' => $orders > 0 ? intdiv($revenue, $orders) : 0,
                    'visits_count' => 160 + (($daysAgo * 7) % 80),
                    'add_to_cart_count' => 42 + ($daysAgo % 13),
                    'checkout_started_count' => 18 + ($daysAgo % 8),
                    'checkout_completed_count' => $orders,
                ],
            );
        }
    }

    private function seedEventStream(Store $store): void
    {
        $customer = Customer::withoutGlobalScopes()
            ->where('store_id', $store->getKey())
            ->first();

        $types = [
            AnalyticsEventType::PageView,
            AnalyticsEventType::PageView,
            AnalyticsEventType::PageView,
            AnalyticsEventType::ProductView,
            AnalyticsEventType::ProductView,
            AnalyticsEventType::AddToCart,
            AnalyticsEventType::CheckoutStarted,
            AnalyticsEventType::CheckoutCompleted,
            AnalyticsEventType::Search,
            AnalyticsEventType::RemoveFromCart,
        ];

        $referrers = [
            'https://google.com/search?q=acme+fashion',
            'https://instagram.com/acme-fashion',
            'direct',
            'https://newsletter.example/acme',
        ];

        foreach (range(1, 210) as $index) {
            $type = $types[$index % count($types)];
            $occurredAt = now()
                ->subDays($index % 7)
                ->setTime($index % 24, ($index * 7) % 60, 0);

            AnalyticsEvent::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'client_event_id' => "seed_evt_{$store->handle}_{$index}",
                ],
                [
                    'type' => $type,
                    'session_id' => 'seed_sess_'.($index % 48),
                    'customer_id' => $index % 5 === 0 ? $customer?->getKey() : null,
                    'properties_json' => $this->propertiesFor($type, $index, $referrers[$index % count($referrers)]),
                    'occurred_at' => $occurredAt,
                    'created_at' => $occurredAt,
                ],
            );
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function propertiesFor(AnalyticsEventType $type, int $index, string $referrer): array
    {
        $base = [
            'channel' => $index % 4 === 0 ? 'api' : 'storefront',
            'device' => ['desktop', 'mobile', 'tablet'][$index % 3],
            'referrer' => $referrer,
        ];

        return match ($type) {
            AnalyticsEventType::PageView => [
                ...$base,
                'url' => $index % 2 === 0 ? '/' : '/collections/t-shirts',
            ],
            AnalyticsEventType::ProductView => [
                ...$base,
                'product_id' => ($index % 20) + 1,
                'product_title' => 'Seeded product '.$index,
                'url' => '/products/classic-cotton-t-shirt',
            ],
            AnalyticsEventType::AddToCart, AnalyticsEventType::RemoveFromCart => [
                ...$base,
                'product_id' => ($index % 20) + 1,
                'variant_id' => ($index % 120) + 1,
                'quantity' => 1 + ($index % 3),
                'price_amount' => 2499 + (($index % 5) * 500),
            ],
            AnalyticsEventType::CheckoutStarted => [
                ...$base,
                'cart_id' => $index,
                'subtotal_amount' => 4500 + (($index % 9) * 500),
            ],
            AnalyticsEventType::CheckoutCompleted => [
                ...$base,
                'order_id' => $index,
                'order_number' => 'SEED-'.$index,
                'total_amount' => 6500 + (($index % 9) * 750),
            ],
            AnalyticsEventType::Search => [
                ...$base,
                'query' => ['cotton', 'hoodie', 'jeans', 'sneakers'][$index % 4],
                'results_count' => 3 + ($index % 8),
            ],
        };
    }
}


output:
Chunk ID: cecc9b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1293
Output:
<?php

namespace Database\Seeders;

use App\Enums\AnalyticsEventType;
use App\Models\AnalyticsEvent;
use App\Models\Customer;
use App\Models\Store;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class AnalyticsSeeder extends Seeder
{
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            $this->seedDailyMetrics($store);

            if ($store->handle === 'acme-fashion') {
                $this->seedEventStream($store);
            }
        });
    }

    private function seedDailyMetrics(Store $store): void
    {
        foreach (range(0, 29) as $daysAgo) {
            $date = now()->subDays($daysAgo)->toDateString();
            $orders = max(0, 10 - ($daysAgo % 6));
            $revenue = $orders * (5200 + (($daysAgo % 5) * 800));

            DB::table('analytics_daily')->updateOrInsert(
                [
                    'store_id' => $store->getKey(),
                    'date' => $date,
                ],
                [
                    'orders_count' => $orders,
                    'revenue_amount' => $revenue,
                    'aov_amount' => $orders > 0 ? intdiv($revenue, $orders) : 0,
                    'visits_count' => 160 + (($daysAgo * 7) % 80),
                    'add_to_cart_count' => 42 + ($daysAgo % 13),
                    'checkout_started_count' => 18 + ($daysAgo % 8),
                    'checkout_completed_count' => $orders,
                ],
            );
        }
    }

    private function seedEventStream(Store $store): void
    {
        $customer = Customer::withoutGlobalScopes()
            ->where('store_id', $store->getKey())
            ->first();

        $types = [
            AnalyticsEventType::PageView,
            AnalyticsEventType::PageView,
            AnalyticsEventType::PageView,
            AnalyticsEventType::ProductView,
            AnalyticsEventType::ProductView,
            AnalyticsEventType::AddToCart,
            AnalyticsEventType::CheckoutStarted,
            AnalyticsEventType::CheckoutCompleted,
            AnalyticsEventType::Search,
            AnalyticsEventType::RemoveFromCart,
        ];

        $referrers = [
            'https://google.com/search?q=acme+fashion',
            'https://instagram.com/acme-fashion',
            'direct',
            'https://newsletter.example/acme',
        ];

        foreach (range(1, 210) as $index) {
            $type = $types[$index % count($types)];
            $occurredAt = now()
                ->subDays($index % 7)
                ->setTime($index % 24, ($index * 7) % 60, 0);

            AnalyticsEvent::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'client_event_id' => "seed_evt_{$store->handle}_{$index}",
                ],
                [
                    'type' => $type,
                    'session_id' => 'seed_sess_'.($index % 48),
                    'customer_id' => $index % 5 === 0 ? $customer?->getKey() : null,
                    'properties_json' => $this->propertiesFor($type, $index, $referrers[$index % count($referrers)]),
                    'occurred_at' => $occurredAt,
                    'created_at' => $occurredAt,
                ],
            );
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function propertiesFor(AnalyticsEventType $type, int $index, string $referrer): array
    {
        $base = [
            'channel' => $index % 4 === 0 ? 'api' : 'storefront',
            'device' => ['desktop', 'mobile', 'tablet'][$index % 3],
            'referrer' => $referrer,
        ];

        return match ($type) {
            AnalyticsEventType::PageView => [
                ...$base,
                'url' => $index % 2 === 0 ? '/' : '/collections/t-shirts',
            ],
            AnalyticsEventType::ProductView => [
                ...$base,
                'product_id' => ($index % 20) + 1,
                'product_title' => 'Seeded product '.$index,
                'url' => '/products/classic-cotton-t-shirt',
            ],
            AnalyticsEventType::AddToCart, AnalyticsEventType::RemoveFromCart => [
                ...$base,
                'product_id' => ($index % 20) + 1,
                'variant_id' => ($index % 120) + 1,
                'quantity' => 1 + ($index % 3),
                'price_amount' => 2499 + (($index % 5) * 500),
            ],
            AnalyticsEventType::CheckoutStarted => [
                ...$base,
                'cart_id' => $index,
                'subtotal_amount' => 4500 + (($index % 9) * 500),
            ],
            AnalyticsEventType::CheckoutCompleted => [
                ...$base,
                'order_id' => $index,
                'order_number' => 'SEED-'.$index,
                'total_amount' => 6500 + (($index % 9) * 750),
            ],
            AnalyticsEventType::Search => [
                ...$base,
                'query' => ['cotton', 'hoodie', 'jeans', 'sneakers'][$index % 4],
                'results_count' => 3 + ($index % 8),
            ],
        };
    }
}


command: sed -n '1,180p' app/Models/ProductVariant.php

aggregated_output:
<?php

namespace App\Models;

use App\Enums\InventoryPolicy;
use App\Enums\VariantStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use RuntimeException;

class ProductVariant extends Model
{
    /** @use HasFactory<\Database\Factories\ProductVariantFactory> */
    use HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'product_id',
        'sku',
        'barcode',
        'price_amount',
        'compare_at_amount',
        'currency',
        'weight_g',
        'requires_shipping',
        'is_default',
        'position',
        'status',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'price_amount' => 0,
        'currency' => 'USD',
        'requires_shipping' => true,
        'is_default' => false,
        'position' => 0,
        'status' => 'active',
    ];

    protected static function booted(): void
    {
        static::addGlobalScope('current_store', function (Builder $builder): void {
            if (! app()->bound('current_store')) {
                return;
            }

            $store = app('current_store');

            if (! $store instanceof Store) {
                return;
            }

            $builder->whereHas('product', function (Builder $query) use ($store): void {
                $query
                    ->withoutGlobalScopes()
                    ->where('store_id', $store->getKey());
            });
        });

        static::saving(function (ProductVariant $variant): void {
            $variant->assertSkuIsUniqueForStore();
        });

        static::created(function (ProductVariant $variant): void {
            if ($variant->inventoryItem()->exists()) {
                return;
            }

            $storeId = Product::withoutGlobalScopes()
                ->whereKey($variant->product_id)
                ->value('store_id');

            if (! $storeId) {
                return;
            }

            InventoryItem::withoutGlobalScopes()->create([
                'store_id' => $storeId,
                'variant_id' => $variant->getKey(),
                'quantity_on_hand' => 0,
                'quantity_reserved' => 0,
                'policy' => InventoryPolicy::Deny,
            ]);
        });
    }

    private function assertSkuIsUniqueForStore(): void
    {
        $sku = trim((string) $this->sku);

        if ($sku === '') {
            return;
        }

        $storeId = Product::withoutGlobalScopes()
            ->whereKey($this->product_id)
            ->value('store_id');

        if ($storeId === null) {
            return;
        }

        $query = self::withoutGlobalScopes()
            ->where('sku', $sku)
            ->whereHas('product', function (Builder $query) use ($storeId): void {
                $query
                    ->withoutGlobalScopes()
                    ->where('store_id', $storeId);
            });

        if ($this->exists) {
            $query->whereKeyNot($this->getKey());
        }

        if ($query->exists()) {
            throw new RuntimeException("The SKU [{$sku}] is already used in this store.");
        }
    }

    /**
     * @return BelongsTo<Product, $this>
     */
    public function product(): BelongsTo
    {
        return $this->belongsTo(Product::class);
    }

    /**
     * @return HasOne<InventoryItem, $this>
     */
    public function inventoryItem(): HasOne
    {
        return $this->hasOne(InventoryItem::class, 'variant_id');
    }

    /**
     * @return BelongsToMany<ProductOptionValue, $this>
     */
    public function optionValues(): BelongsToMany
    {
        return $this->belongsToMany(ProductOptionValue::class, 'variant_option_values', 'variant_id', 'product_option_value_id');
    }

    public function isPurchasable(): bool
    {
        return $this->status === VariantStatus::Active;
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'price_amount' => 'integer',
            'compare_at_amount' => 'integer',
            'weight_g' => 'integer',
            'requires_shipping' => 'bool',
            'is_default' => 'bool',
            'position' => 'integer',
            'status' => VariantStatus::class,
        ];
    }
}


output:
Chunk ID: 31bcef
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1140
Output:
<?php

namespace App\Models;

use App\Enums\InventoryPolicy;
use App\Enums\VariantStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use RuntimeException;

class ProductVariant extends Model
{
    /** @use HasFactory<\Database\Factories\ProductVariantFactory> */
    use HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'product_id',
        'sku',
        'barcode',
        'price_amount',
        'compare_at_amount',
        'currency',
        'weight_g',
        'requires_shipping',
        'is_default',
        'position',
        'status',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'price_amount' => 0,
        'currency' => 'USD',
        'requires_shipping' => true,
        'is_default' => false,
        'position' => 0,
        'status' => 'active',
    ];

    protected static function booted(): void
    {
        static::addGlobalScope('current_store', function (Builder $builder): void {
            if (! app()->bound('current_store')) {
                return;
            }

            $store = app('current_store');

            if (! $store instanceof Store) {
                return;
            }

            $builder->whereHas('product', function (Builder $query) use ($store): void {
                $query
                    ->withoutGlobalScopes()
                    ->where('store_id', $store->getKey());
            });
        });

        static::saving(function (ProductVariant $variant): void {
            $variant->assertSkuIsUniqueForStore();
        });

        static::created(function (ProductVariant $variant): void {
            if ($variant->inventoryItem()->exists()) {
                return;
            }

            $storeId = Product::withoutGlobalScopes()
                ->whereKey($variant->product_id)
                ->value('store_id');

            if (! $storeId) {
                return;
            }

            InventoryItem::withoutGlobalScopes()->create([
                'store_id' => $storeId,
                'variant_id' => $variant->getKey(),
                'quantity_on_hand' => 0,
                'quantity_reserved' => 0,
                'policy' => InventoryPolicy::Deny,
            ]);
        });
    }

    private function assertSkuIsUniqueForStore(): void
    {
        $sku = trim((string) $this->sku);

        if ($sku === '') {
            return;
        }

        $storeId = Product::withoutGlobalScopes()
            ->whereKey($this->product_id)
            ->value('store_id');

        if ($storeId === null) {
            return;
        }

        $query = self::withoutGlobalScopes()
            ->where('sku', $sku)
            ->whereHas('product', function (Builder $query) use ($storeId): void {
                $query
                    ->withoutGlobalScopes()
                    ->where('store_id', $storeId);
            });

        if ($this->exists) {
            $query->whereKeyNot($this->getKey());
        }

        if ($query->exists()) {
            throw new RuntimeException("The SKU [{$sku}] is already used in this store.");
        }
    }

    /**
     * @return BelongsTo<Product, $this>
     */
    public function product(): BelongsTo
    {
        return $this->belongsTo(Product::class);
    }

    /**
     * @return HasOne<InventoryItem, $this>
     */
    public function inventoryItem(): HasOne
    {
        return $this->hasOne(InventoryItem::class, 'variant_id');
    }

    /**
     * @return BelongsToMany<ProductOptionValue, $this>
     */
    public function optionValues(): BelongsToMany
    {
        return $this->belongsToMany(ProductOptionValue::class, 'variant_option_values', 'variant_id', 'product_option_value_id');
    }

    public function isPurchasable(): bool
    {
        return $this->status === VariantStatus::Active;
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'price_amount' => 'integer',
            'compare_at_amount' => 'integer',
            'weight_g' => 'integer',
            'requires_shipping' => 'bool',
            'is_default' => 'bool',
            'position' => 'integer',
            'status' => VariantStatus::class,
        ];
    }
}


command: sed -n '1,180p' app/Models/ProductOptionValue.php

aggregated_output:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class ProductOptionValue extends Model
{
    /** @use HasFactory<\Database\Factories\ProductOptionValueFactory> */
    use HasFactory;

    public $timestamps = false;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'product_option_id',
        'value',
        'position',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'position' => 0,
    ];

    protected static function booted(): void
    {
        static::addGlobalScope('current_store', function (Builder $builder): void {
            if (! app()->bound('current_store')) {
                return;
            }

            $store = app('current_store');

            if (! $store instanceof Store) {
                return;
            }

            $builder->whereHas('option.product', function (Builder $query) use ($store): void {
                $query
                    ->withoutGlobalScopes()
                    ->where('store_id', $store->getKey());
            });
        });
    }

    /**
     * @return BelongsTo<ProductOption, $this>
     */
    public function option(): BelongsTo
    {
        return $this->belongsTo(ProductOption::class, 'product_option_id');
    }

    /**
     * @return BelongsToMany<ProductVariant, $this>
     */
    public function variants(): BelongsToMany
    {
        return $this->belongsToMany(ProductVariant::class, 'variant_option_values', 'product_option_value_id', 'variant_id');
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'position' => 'integer',
        ];
    }
}


output:
Chunk ID: 119821
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 483
Output:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class ProductOptionValue extends Model
{
    /** @use HasFactory<\Database\Factories\ProductOptionValueFactory> */
    use HasFactory;

    public $timestamps = false;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'product_option_id',
        'value',
        'position',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'position' => 0,
    ];

    protected static function booted(): void
    {
        static::addGlobalScope('current_store', function (Builder $builder): void {
            if (! app()->bound('current_store')) {
                return;
            }

            $store = app('current_store');

            if (! $store instanceof Store) {
                return;
            }

            $builder->whereHas('option.product', function (Builder $query) use ($store): void {
                $query
                    ->withoutGlobalScopes()
                    ->where('store_id', $store->getKey());
            });
        });
    }

    /**
     * @return BelongsTo<ProductOption, $this>
     */
    public function option(): BelongsTo
    {
        return $this->belongsTo(ProductOption::class, 'product_option_id');
    }

    /**
     * @return BelongsToMany<ProductVariant, $this>
     */
    public function variants(): BelongsToMany
    {
        return $this->belongsToMany(ProductVariant::class, 'variant_option_values', 'product_option_value_id', 'variant_id');
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'position' => 'integer',
        ];
    }
}


command:
/bin/zsh
-lc
sed -n '1,180p' app/Models/Discount.php

Discount.php

cmd: sed -n '1,180p' app/Models/Discount.php

aggregated_output:
<?php

namespace App\Models;

use App\Enums\DiscountStatus;
use App\Enums\DiscountType;
use App\Enums\DiscountValueType;
use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Discount extends Model
{
    /** @use HasFactory<\Database\Factories\DiscountFactory> */
    use BelongsToStore, HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'store_id',
        'type',
        'code',
        'value_type',
        'value_amount',
        'starts_at',
        'ends_at',
        'usage_limit',
        'usage_count',
        'rules_json',
        'status',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'type' => 'code',
        'value_amount' => 0,
        'usage_count' => 0,
        'rules_json' => '{}',
        'status' => 'active',
    ];

    /**
     * @return BelongsTo<Store, $this>
     */
    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'type' => DiscountType::class,
            'value_type' => DiscountValueType::class,
            'value_amount' => 'integer',
            'starts_at' => 'datetime',
            'ends_at' => 'datetime',
            'usage_limit' => 'integer',
            'usage_count' => 'integer',
            'rules_json' => 'array',
            'status' => DiscountStatus::class,
        ];
    }
}


output:
Chunk ID: 9f495a
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 410
Output:
<?php

namespace App\Models;

use App\Enums\DiscountStatus;
use App\Enums\DiscountType;
use App\Enums\DiscountValueType;
use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Discount extends Model
{
    /** @use HasFactory<\Database\Factories\DiscountFactory> */
    use BelongsToStore, HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'store_id',
        'type',
        'code',
        'value_type',
        'value_amount',
        'starts_at',
        'ends_at',
        'usage_limit',
        'usage_count',
        'rules_json',
        'status',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'type' => 'code',
        'value_amount' => 0,
        'usage_count' => 0,
        'rules_json' => '{}',
        'status' => 'active',
    ];

    /**
     * @return BelongsTo<Store, $this>
     */
    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'type' => DiscountType::class,
            'value_type' => DiscountValueType::class,
            'value_amount' => 'integer',
            'starts_at' => 'datetime',
            'ends_at' => 'datetime',
            'usage_limit' => 'integer',
            'usage_count' => 'integer',
            'rules_json' => 'array',
            'status' => DiscountStatus::class,
        ];
    }
}


cmd: rg -n "CustomerSeeder|customer@acme|jane@example|CustomerAddressSeeder|Order #1001|Acme Electronics - 3 orders" specs/07-SEEDERS-AND-TEST-DATA.md


cmd: sed -n '1380,1530p' specs/07-SEEDERS-AND-TEST-DATA.md


cmd: sed -n '1,220p' database/seeders/OrderSeeder.php


cmd: sed -n '1,220p' database/seeders/PaymentSeeder.php


cmd: sed -n '1,220p' database/seeders/FulfillmentSeeder.php


cmd: sed -n '1,220p' database/seeders/RefundSeeder.php


command: sed -n '1,220p' database/seeders/OrderSeeder.php

aggregated_output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class OrderSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


command: sed -n '1,220p' database/seeders/PaymentSeeder.php

aggregated_output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class PaymentSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


command: rg -n "CustomerSeeder|customer@acme|jane@example|CustomerAddressSeeder|Order #1001|Acme Electronics - 3 orders" specs/07-SEEDERS-AND-TEST-DATA.md

type: search

cmd: rg -n 'CustomerSeeder|customer@acme|jane@example|CustomerAddressSeeder|Order #1001|Acme Electronics - 3 orders' specs/07-SEEDERS-AND-TEST-DATA.md

query: CustomerSeeder|customer@acme|jane@example|CustomerAddressSeeder|Order #1001|Acme Electronics - 3 orders

aggregated_output:
26:| 12 | CustomerSeeder | Stores | Creates customer accounts with addresses |
49:    B --> L[CustomerSeeder]
1456:### 3.12 CustomerSeeder
1462:| 1 | customer@acme.test | John Doe | password | 1 |
1463:| 2 | jane@example.com | Jane Smith | password | 0 |
1473:**Customer 1 (customer@acme.test) addresses:**
1480:**Customer 2 (jane@example.com) addresses:**
1507:**Order #1001 - Awaiting fulfillment (Credit Card)**
1511:| customer | customer@acme.test (John Doe) |
1534:| customer | customer@acme.test (John Doe) |
1558:| customer | jane@example.com (Jane Smith) |
1582:| customer | customer@acme.test (John Doe) |
1605:| customer | jane@example.com (Jane Smith) |
1721:| customer | customer@acme.test (John Doe) |
1831:| customer | customer@acme.test (John Doe) |
1875:**Acme Electronics - 3 orders (minimal, for tenant isolation):**
2157:  Email:    customer@acme.test
2163:  Email:    jane@example.com


command: sed -n '1380,1530p' specs/07-SEEDERS-AND-TEST-DATA.md

aggregated_output:
Options: Size (S, M, L), Color (Camel, Charcoal)

Variants (6 total): price_amount = 49999 (499.99 EUR), weight_g = 1200, inventory = 3 each (deny)

**Test purpose:** Tests high-value price display (499.99 EUR).

---

#### Product Summary Table (Acme Fashion)

| # | Title | Handle | Status | Price (cents) | Compare At | Variants | Inventory | Special |
|---|-------|--------|--------|---------------|-----------|----------|-----------|---------|
| 1 | Classic Cotton T-Shirt | classic-cotton-t-shirt | active | 2499 | - | 12 | 15 ea | - |
| 2 | Premium Slim Fit Jeans | premium-slim-fit-jeans | active | 7999 | 9999 | 10 | 8 ea | On sale |
| 3 | Organic Hoodie | organic-hoodie | active | 5999 | - | 4 | 20 ea | - |
| 4 | Leather Belt | leather-belt | active | 3499 | - | 4 | 25 ea | - |
| 5 | Running Sneakers | running-sneakers | active | 11999 | - | 14 | 5 ea | Low stock |
| 6 | Graphic Print Tee | graphic-print-tee | active | 2999 | - | 4 | 18 ea | - |
| 7 | V-Neck Linen Tee | v-neck-linen-tee | active | 3499 | - | 9 | 12 ea | - |
| 8 | Striped Polo Shirt | striped-polo-shirt | active | 2799 | 3999 | 4 | 10 ea | On sale |
| 9 | Cargo Pants | cargo-pants | active | 5499 | - | 12 | 14 ea | - |
| 10 | Chino Shorts | chino-shorts | active | 3999 | - | 8 | 16 ea | - |
| 11 | Wide Leg Trousers | wide-leg-trousers | active | 4999 | 6999 | 3 | 7 ea | On sale |
| 12 | Wool Scarf | wool-scarf | active | 2999 | - | 3 | 30 ea | - |
| 13 | Canvas Tote Bag | canvas-tote-bag | active | 1999 | - | 2 | 40 ea | - |
| 14 | Bucket Hat | bucket-hat | active | 2499 | - | 6 | 22 ea | - |
| 15 | Unreleased Winter Jacket | unreleased-winter-jacket | draft | 14999 | - | 4 | 0 ea | Draft |
| 16 | Discontinued Raincoat | discontinued-raincoat | archived | 8999 | - | 2 | 3 ea | Archived |
| 17 | Limited Edition Sneakers | limited-edition-sneakers | active | 15999 | - | 3 | 0 ea | Sold out (deny) |
| 18 | Backorder Denim Jacket | backorder-denim-jacket | active | 9999 | - | 4 | 0 ea | Backorder (continue) |
| 19 | Gift Card | gift-card | active | 2500/5000/10000 | - | 3 | 9999 ea | Digital |
| 20 | Cashmere Overcoat | cashmere-overcoat | active | 49999 | - | 6 | 3 ea | Expensive |

**Total variants across Acme Fashion:** 107

---

#### Acme Electronics - 5 Products (minimal seed for tenant isolation)

These products exist for tenant isolation testing. Store 2 data must never appear in Store 1 queries.

| # | Title | Handle | Vendor | Type | Options | Variants | Price (cents) | Weight | Inventory |
|---|-------|--------|--------|------|---------|----------|---------------|--------|-----------|
| E1 | Pro Laptop 15 | pro-laptop-15 | TechCorp | Laptops | Storage: 256GB, 512GB, 1TB | 3 | 99999, 119999, 149999 | 1800g | 10 ea |
| E2 | Wireless Headphones | wireless-headphones | AudioMax | Audio | Color: Black, Silver | 2 | 14999 | 250g | 25 ea |
| E3 | USB-C Cable 2m | usb-c-cable-2m | CablePro | Cables | (none - single default variant) | 1 | 1299 | 50g | 200 |
| E4 | Mechanical Keyboard | mechanical-keyboard | KeyTech | Peripherals | Switch Type: Red, Blue, Brown | 3 | 12999 | 1100g | 15 ea |
| E5 | Monitor Stand | monitor-stand | DeskGear | Accessories | (none - single default variant) | 1 | 4999 | 2500g | 30 |

All active, published now, tags vary. Inventory policy = "deny" for all.

**Note on product media:** No `ProductMedia` records are seeded. The storefront must gracefully handle products without images by displaying a placeholder (e.g., a neutral SVG placeholder with the store's brand color). E2E tests should not assert on specific image URLs.

---

### 3.11 DiscountSeeder

**Acme Fashion - 5 discount codes:**

| Code | Type | Value Type | value_amount | starts_at | ends_at | usage_limit | usage_count | rules_json | status |
|------|------|-----------|-------------|-----------|---------|-------------|-------------|-----------|--------|
| WELCOME10 | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | null | 3 | min_purchase_amount: 2000 (=20.00 EUR) | active |
| FLAT5 | code | fixed | 500 (=5.00 EUR) | 2025-01-01 | 2027-12-31 | null | 0 | (empty) | active |
| FREESHIP | code | free_shipping | 0 | 2025-01-01 | 2027-12-31 | null | 1 | (empty) | active |
| EXPIRED20 | code | percent | 20 (=20%) | 2024-01-01 | 2024-12-31 | null | 0 | (empty) | expired |
| MAXED | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | 5 | 5 | (empty) | active |

**Behavioral expectations:**
- WELCOME10: Applies 10% off to orders with subtotal >= 20.00 EUR. Already used 3 times (has capacity remaining since no usage_limit).
- FLAT5: Applies flat 5.00 EUR discount. Never used.
- FREESHIP: Removes shipping cost. Used once.
- EXPIRED20: Expired - checkout must reject this code with "discount expired" error.
- MAXED: Usage limit of 5 reached - checkout must reject this code with "discount limit reached" error.

---

### 3.12 CustomerSeeder

**Acme Fashion - 10 customers:**

| # | Email | Name | Password (plaintext) | marketing_opt_in |
|---|-------|------|----------------------|------------------|
| 1 | customer@acme.test | John Doe | password | 1 |
| 2 | jane@example.com | Jane Smith | password | 0 |
| 3 | michael@example.com | Michael Brown | password | 1 |
| 4 | sarah@example.com | Sarah Wilson | password | 0 |
| 5 | david@example.com | David Lee | password | 1 |
| 6 | emma@example.com | Emma Garcia | password | 0 |
| 7 | james@example.com | James Taylor | password | 0 |
| 8 | lisa@example.com | Lisa Anderson | password | 1 |
| 9 | robert@example.com | Robert Martinez | password | 0 |
| 10 | anna@example.com | Anna Thomas | password | 1 |

**Customer 1 (customer@acme.test) addresses:**

| Label | is_default | Address Details |
|-------|-----------|-----------------|
| Home | 1 | John Doe, Hauptstrasse 1, 10115 Berlin, DE, phone: +49 30 12345678 |
| Work | 0 | John Doe, Acme Corp, Friedrichstrasse 100, 3rd Floor, 10117 Berlin, DE, phone: +49 30 87654321 |

**Customer 2 (jane@example.com) addresses:**

| Label | is_default | Address Details |
|-------|-----------|-----------------|
| Home | 1 | Jane Smith, Schillerstrasse 45, 80336 Munich, Bavaria (BY), DE |

**Customers 3-10:** Each gets one default address with Faker-generated German addresses.

**Acme Electronics - 2 customers (tenant isolation testing):**

| # | Email | Name | Password |
|---|-------|------|----------|
| 1 | techfan@example.com | Tech Fan | password |
| 2 | gadgetlover@example.com | Gadget Lover | password |

Each has one default address with Faker-generated data.

---

### 3.13 OrderSeeder

**Acme Fashion - 15 orders:**

All orders use the customer's default shipping address (unless noted). Order numbers follow the "#NNNN" format starting from #1001.

---

**Order #1001 - Awaiting fulfillment (Credit Card)**

| Field | Value |
|-------|-------|
| customer | customer@acme.test (John Doe) |
| payment_method | credit_card |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 2 days ago |

Lines:

| Product | Variant (option values) | Qty | Unit Price | Line Total |
|---------|------------------------|-----|-----------|-----------|
| Classic Cotton T-Shirt | S / White | 2 | 2499 | 4998 |

Totals: subtotal 4998, discount 0, shipping 499, tax 798, total **5497**

Payment: mock, method "credit_card", provider_payment_id "mock_test_order1001", status "captured", amount 5497

Fulfillments: None.

---

**Order #1002 - Fully delivered (Credit Card)**


output:
Chunk ID: b7e578
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 227
Output:
26:| 12 | CustomerSeeder | Stores | Creates customer accounts with addresses |
49:    B --> L[CustomerSeeder]
1456:### 3.12 CustomerSeeder
1462:| 1 | customer@acme.test | John Doe | password | 1 |
1463:| 2 | jane@example.com | Jane Smith | password | 0 |
1473:**Customer 1 (customer@acme.test) addresses:**
1480:**Customer 2 (jane@example.com) addresses:**
1507:**Order #1001 - Awaiting fulfillment (Credit Card)**
1511:| customer | customer@acme.test (John Doe) |
1534:| customer | customer@acme.test (John Doe) |
1558:| customer | jane@example.com (Jane Smith) |
1582:| customer | customer@acme.test (John Doe) |
1605:| customer | jane@example.com (Jane Smith) |
1721:| customer | customer@acme.test (John Doe) |
1831:| customer | customer@acme.test (John Doe) |
1875:**Acme Electronics - 3 orders (minimal, for tenant isolation):**
2157:  Email:    customer@acme.test
2163:  Email:    jane@example.com


output:
Chunk ID: 13f9cd
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1857
Output:
Options: Size (S, M, L), Color (Camel, Charcoal)

Variants (6 total): price_amount = 49999 (499.99 EUR), weight_g = 1200, inventory = 3 each (deny)

**Test purpose:** Tests high-value price display (499.99 EUR).

---

#### Product Summary Table (Acme Fashion)

| # | Title | Handle | Status | Price (cents) | Compare At | Variants | Inventory | Special |
|---|-------|--------|--------|---------------|-----------|----------|-----------|---------|
| 1 | Classic Cotton T-Shirt | classic-cotton-t-shirt | active | 2499 | - | 12 | 15 ea | - |
| 2 | Premium Slim Fit Jeans | premium-slim-fit-jeans | active | 7999 | 9999 | 10 | 8 ea | On sale |
| 3 | Organic Hoodie | organic-hoodie | active | 5999 | - | 4 | 20 ea | - |
| 4 | Leather Belt | leather-belt | active | 3499 | - | 4 | 25 ea | - |
| 5 | Running Sneakers | running-sneakers | active | 11999 | - | 14 | 5 ea | Low stock |
| 6 | Graphic Print Tee | graphic-print-tee | active | 2999 | - | 4 | 18 ea | - |
| 7 | V-Neck Linen Tee | v-neck-linen-tee | active | 3499 | - | 9 | 12 ea | - |
| 8 | Striped Polo Shirt | striped-polo-shirt | active | 2799 | 3999 | 4 | 10 ea | On sale |
| 9 | Cargo Pants | cargo-pants | active | 5499 | - | 12 | 14 ea | - |
| 10 | Chino Shorts | chino-shorts | active | 3999 | - | 8 | 16 ea | - |
| 11 | Wide Leg Trousers | wide-leg-trousers | active | 4999 | 6999 | 3 | 7 ea | On sale |
| 12 | Wool Scarf | wool-scarf | active | 2999 | - | 3 | 30 ea | - |
| 13 | Canvas Tote Bag | canvas-tote-bag | active | 1999 | - | 2 | 40 ea | - |
| 14 | Bucket Hat | bucket-hat | active | 2499 | - | 6 | 22 ea | - |
| 15 | Unreleased Winter Jacket | unreleased-winter-jacket | draft | 14999 | - | 4 | 0 ea | Draft |
| 16 | Discontinued Raincoat | discontinued-raincoat | archived | 8999 | - | 2 | 3 ea | Archived |
| 17 | Limited Edition Sneakers | limited-edition-sneakers | active | 15999 | - | 3 | 0 ea | Sold out (deny) |
| 18 | Backorder Denim Jacket | backorder-denim-jacket | active | 9999 | - | 4 | 0 ea | Backorder (continue) |
| 19 | Gift Card | gift-card | active | 2500/5000/10000 | - | 3 | 9999 ea | Digital |
| 20 | Cashmere Overcoat | cashmere-overcoat | active | 49999 | - | 6 | 3 ea | Expensive |

**Total variants across Acme Fashion:** 107

---

#### Acme Electronics - 5 Products (minimal seed for tenant isolation)

These products exist for tenant isolation testing. Store 2 data must never appear in Store 1 queries.

| # | Title | Handle | Vendor | Type | Options | Variants | Price (cents) | Weight | Inventory |
|---|-------|--------|--------|------|---------|----------|---------------|--------|-----------|
| E1 | Pro Laptop 15 | pro-laptop-15 | TechCorp | Laptops | Storage: 256GB, 512GB, 1TB | 3 | 99999, 119999, 149999 | 1800g | 10 ea |
| E2 | Wireless Headphones | wireless-headphones | AudioMax | Audio | Color: Black, Silver | 2 | 14999 | 250g | 25 ea |
| E3 | USB-C Cable 2m | usb-c-cable-2m | CablePro | Cables | (none - single default variant) | 1 | 1299 | 50g | 200 |
| E4 | Mechanical Keyboard | mechanical-keyboard | KeyTech | Peripherals | Switch Type: Red, Blue, Brown | 3 | 12999 | 1100g | 15 ea |
| E5 | Monitor Stand | monitor-stand | DeskGear | Accessories | (none - single default variant) | 1 | 4999 | 2500g | 30 |

All active, published now, tags vary. Inventory policy = "deny" for all.

**Note on product media:** No `ProductMedia` records are seeded. The storefront must gracefully handle products without images by displaying a placeholder (e.g., a neutral SVG placeholder with the store's brand color). E2E tests should not assert on specific image URLs.

---

### 3.11 DiscountSeeder

**Acme Fashion - 5 discount codes:**

| Code | Type | Value Type | value_amount | starts_at | ends_at | usage_limit | usage_count | rules_json | status |
|------|------|-----------|-------------|-----------|---------|-------------|-------------|-----------|--------|
| WELCOME10 | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | null | 3 | min_purchase_amount: 2000 (=20.00 EUR) | active |
| FLAT5 | code | fixed | 500 (=5.00 EUR) | 2025-01-01 | 2027-12-31 | null | 0 | (empty) | active |
| FREESHIP | code | free_shipping | 0 | 2025-01-01 | 2027-12-31 | null | 1 | (empty) | active |
| EXPIRED20 | code | percent | 20 (=20%) | 2024-01-01 | 2024-12-31 | null | 0 | (empty) | expired |
| MAXED | code | percent | 10 (=10%) | 2025-01-01 | 2027-12-31 | 5 | 5 | (empty) | active |

**Behavioral expectations:**
- WELCOME10: Applies 10% off to orders with subtotal >= 20.00 EUR. Already used 3 times (has capacity remaining since no usage_limit).
- FLAT5: Applies flat 5.00 EUR discount. Never used.
- FREESHIP: Removes shipping cost. Used once.
- EXPIRED20: Expired - checkout must reject this code with "discount expired" error.
- MAXED: Usage limit of 5 reached - checkout must reject this code with "discount limit reached" error.

---

### 3.12 CustomerSeeder

**Acme Fashion - 10 customers:**

| # | Email | Name | Password (plaintext) | marketing_opt_in |
|---|-------|------|----------------------|------------------|
| 1 | customer@acme.test | John Doe | password | 1 |
| 2 | jane@example.com | Jane Smith | password | 0 |
| 3 | michael@example.com | Michael Brown | password | 1 |
| 4 | sarah@example.com | Sarah Wilson | password | 0 |
| 5 | david@example.com | David Lee | password | 1 |
| 6 | emma@example.com | Emma Garcia | password | 0 |
| 7 | james@example.com | James Taylor | password | 0 |
| 8 | lisa@example.com | Lisa Anderson | password | 1 |
| 9 | robert@example.com | Robert Martinez | password | 0 |
| 10 | anna@example.com | Anna Thomas | password | 1 |

**Customer 1 (customer@acme.test) addresses:**

| Label | is_default | Address Details |
|-------|-----------|-----------------|
| Home | 1 | John Doe, Hauptstrasse 1, 10115 Berlin, DE, phone: +49 30 12345678 |
| Work | 0 | John Doe, Acme Corp, Friedrichstrasse 100, 3rd Floor, 10117 Berlin, DE, phone: +49 30 87654321 |

**Customer 2 (jane@example.com) addresses:**

| Label | is_default | Address Details |
|-------|-----------|-----------------|
| Home | 1 | Jane Smith, Schillerstrasse 45, 80336 Munich, Bavaria (BY), DE |

**Customers 3-10:** Each gets one default address with Faker-generated German addresses.

**Acme Electronics - 2 customers (tenant isolation testing):**

| # | Email | Name | Password |
|---|-------|------|----------|
| 1 | techfan@example.com | Tech Fan | password |
| 2 | gadgetlover@example.com | Gadget Lover | password |

Each has one default address with Faker-generated data.

---

### 3.13 OrderSeeder

**Acme Fashion - 15 orders:**

All orders use the customer's default shipping address (unless noted). Order numbers follow the "#NNNN" format starting from #1001.

---

**Order #1001 - Awaiting fulfillment (Credit Card)**

| Field | Value |
|-------|-------|
| customer | customer@acme.test (John Doe) |
| payment_method | credit_card |
| status / financial / fulfillment | paid / paid / unfulfilled |
| placed_at | 2 days ago |

Lines:

| Product | Variant (option values) | Qty | Unit Price | Line Total |
|---------|------------------------|-----|-----------|-----------|
| Classic Cotton T-Shirt | S / White | 2 | 2499 | 4998 |

Totals: subtotal 4998, discount 0, shipping 499, tax 798, total **5497**

Payment: mock, method "credit_card", provider_payment_id "mock_test_order1001", status "captured", amount 5497

Fulfillments: None.

---

**Order #1002 - Fully delivered (Credit Card)**


output:
Chunk ID: ae2e9e
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 52
Output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class OrderSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


output:
Chunk ID: 29a94d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 53
Output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class PaymentSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


command: sed -n '1,220p' database/seeders/FulfillmentSeeder.php

aggregated_output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class FulfillmentSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


command: sed -n '1,220p' database/seeders/RefundSeeder.php

aggregated_output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class RefundSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


output:
Chunk ID: 7007d6
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 54
Output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class FulfillmentSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


output:
Chunk ID: 110488
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 53
Output:
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class RefundSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        //
    }
}


cmd: sed -n '1,260p' app/Livewire/Storefront/Account/Orders/Index.php


cmd: sed -n '1,260p' app/Livewire/Admin/Orders/Index.php


cmd: sed -n '1,320p' app/Livewire/Admin/Orders/Show.php


cmd: sed -n '1,220p' app/Livewire/Admin/Customers/Show.php


cmd: rg -n "orderBy|latest|paginate|placed_at" app/Livewire/Storefront/Account/Orders app/Livewire/Admin/Orders app/Livewire/Admin/Customers -S


command: sed -n '1,260p' app/Livewire/Storefront/Account/Orders/Index.php

aggregated_output:
<?php

namespace App\Livewire\Storefront\Account\Orders;

use App\Models\Customer;
use App\Models\Order;
use App\Models\Store;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Index extends Component
{
    #[Locked]
    public int $storeId;

    #[Locked]
    public bool $isDashboard = false;

    public function mount(): void
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        $this->storeId = $store->getKey();
        $this->isDashboard = request()->routeIs('account.dashboard');
    }

    public function render(): mixed
    {
        return view('livewire.storefront.account.orders.index', [
            'customer' => $this->customer(),
            'isDashboard' => $this->isDashboard,
            'orders' => $this->orders($this->isDashboard),
        ])->layout('layouts.storefront', [
            'title' => 'Account',
        ]);
    }

    /**
     * @return Collection<int, Order>
     */
    private function orders(bool $isDashboard): Collection
    {
        return Order::withoutGlobalScopes()
            ->where('store_id', $this->storeId)
            ->where('customer_id', $this->customer()->getKey())
            ->latest('placed_at')
            ->latest('id')
            ->limit($isDashboard ? 5 : 20)
            ->get();
    }

    private function customer(): Customer
    {
        $customer = Auth::guard('customer')->user();

        abort_unless($customer instanceof Customer, 403);

        return Customer::withoutGlobalScopes()
            ->where('store_id', $this->storeId)
            ->whereKey($customer->getKey())
            ->firstOrFail();
    }
}


output:
Chunk ID: 9f2540
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 434
Output:
<?php

namespace App\Livewire\Storefront\Account\Orders;

use App\Models\Customer;
use App\Models\Order;
use App\Models\Store;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Locked;
use Livewire\Component;

class Index extends Component
{
    #[Locked]
    public int $storeId;

    #[Locked]
    public bool $isDashboard = false;

    public function mount(): void
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

        $this->storeId = $store->getKey();
        $this->isDashboard = request()->routeIs('account.dashboard');
    }

    public function render(): mixed
    {
        return view('livewire.storefront.account.orders.index', [
            'customer' => $this->customer(),
            'isDashboard' => $this->isDashboard,
            'orders' => $this->orders($this->isDashboard),
        ])->layout('layouts.storefront', [
            'title' => 'Account',
        ]);
    }

    /**
     * @return Collection<int, Order>
     */
    private function orders(bool $isDashboard): Collection
    {
        return Order::withoutGlobalScopes()
            ->where('store_id', $this->storeId)
            ->where('customer_id', $this->customer()->getKey())
            ->latest('placed_at')
            ->latest('id')
            ->limit($isDashboard ? 5 : 20)
            ->get();
    }

    private function customer(): Customer
    {
        $customer = Auth::guard('customer')->user();

        abort_unless($customer instanceof Customer, 403);

        return Customer::withoutGlobalScopes()
            ->where('store_id', $this->storeId)
            ->whereKey($customer->getKey())
            ->firstOrFail();
    }
}


command: sed -n '1,260p' app/Livewire/Admin/Orders/Index.php

aggregated_output:
<?php

namespace App\Livewire\Admin\Orders;

use App\Enums\FinancialStatus;
use App\Enums\FulfillmentStatus;
use App\Enums\OrderStatus;
use App\Models\Order;
use App\Models\Store;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Livewire\Attributes\Locked;
use Livewire\Component;
use Livewire\WithPagination;
use Throwable;

class Index extends Component
{
    use WithPagination;

    #[Locked]
    public int $storeId;

    public string $search = '';

    public string $statusFilter = 'all';

    public string $financialStatusFilter = 'all';

    public string $fulfillmentStatusFilter = 'all';

    public string $dateFrom = '';

    public string $dateTo = '';

    public function mount(): void
    {
        $store = app('current_store');

        abort_unless($store instanceof Store, 404);

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

    public function updatedSearch(): void
    {
        $this->resetPage();
    }

    public function updatedStatusFilter(): void
    {
        $this->resetPage();
    }

    public function updatedFinancialStatusFilter(): void
    {
        $this->resetPage();
    }

    public function updatedFulfillmentStatusFilter(): void
    {
        $this->resetPage();
    }

    public function updatedDateFrom(): void
    {
        $this->resetPage();
    }

    public function updatedDateTo(): void
    {
        $this->resetPage();
    }

    public function orders(): LengthAwarePaginator
    {
        $dateFrom = $this->parsedDate($this->dateFrom);
        $dateTo = $this->parsedDate($this->dateTo, endOfDay: true);

        return Order::withoutGlobalScopes()
            ->with('customer')
            ->withCount('lines')
            ->where('store_id', $this->storeId)
            ->when($this->search !== '', function (Builder $query): void {
                $search = '%'.$this->search.'%';

                $query->where(function (Builder $query) use ($search): void {
                    $query
                        ->where('order_number', 'like', $search)
                        ->orWhere('email', 'like', $search)
                        ->orWhereHas('customer', fn (Builder $query) => $query->where('name', 'like', $search));
                });
            })
            ->when($this->statusFilter !== 'all', fn (Builder $query) => $query->where('status', $this->statusFilter))
            ->when($this->financialStatusFilter !== 'all', fn (Builder $query) => $query->where('financial_status', $this->financialStatusFilter))
            ->when($this->fulfillmentStatusFilter !== 'all', fn (Builder $query) => $query->where('fulfillment_status', $this->fulfillmentStatusFilter))
            ->when($dateFrom instanceof Carbon, fn (Builder $query) => $query->where('placed_at', '>=', $dateFrom))
            ->when($dateTo instanceof Carbon, fn (Builder $query) => $query->where('placed_at', '<=', $dateTo))
            ->latest('placed_at')
            ->latest('id')
            ->paginate(20);
    }

    public function render(): mixed
    {
        return view('livewire.admin.orders.index', [
            'orders' => $this->orders(),
            'statuses' => OrderStatus::cases(),
            'financialStatuses' => FinancialStatus::cases(),
            'fulfillmentStatuses' => FulfillmentStatus::cases(),
        ])->layout('layouts.app', [
            'title' => __('Orders'),
        ]);
    }

    private function parsedDate(string $value, bool $endOfDay = false): ?Carbon
    {
        if ($value === '') {
            return null;
        }

        try {
            $date = Carbon::parse($value);

            return $endOfDay ? $date->endOfDay() : $date->startOfDay();
        } catch (Throwable) {
            return null;
        }
    }
}