command: sed -n '145,290p' app/Http/Controllers/Api/StorefrontCheckoutController.php && sed -n '1,220p' app/Services/PricingEngine.php && sed -n '1,220p' app/Services/TaxCalculator.php && sed -n '1,220p' app/Services/ShippingCalculator.php

ShippingCalculator.php

stdout:
        }

        return response()->json($this->payload($checkout));
    }

    private function checkout(int $checkoutId): Checkout
    {
        $checkout = Checkout::query()->with(['cart.lines.variant.product', 'shippingRate'])->findOrFail($checkoutId);
        $customerId = request()->user('customer')?->getKey();

        if ($customerId !== null) {
            abort_unless((int) $checkout->customer_id === (int) $customerId, 404);
        } else {
            $sessionCartIds = array_filter([request()->session()->get('cart_id'), request()->session()->get('cart_id_'.app('current_store')->getKey())]);
            abort_unless(in_array($checkout->cart_id, $sessionCartIds, true), 404);
        }

        return $checkout;
    }

    /** @return array<string, mixed> */
    private function payload(Checkout $checkout): array
    {
        $totals = $checkout->totals_json ?? $this->pricing->calculate($checkout)->toArray();
        $rates = $checkout->shipping_address_json === null ? collect() : $this->shipping->getAvailableRates(app('current_store'), $checkout->shipping_address_json);

        return ['id' => $checkout->id, 'store_id' => $checkout->store_id, 'cart_id' => $checkout->cart_id, 'customer_id' => $checkout->customer_id, 'status' => $checkout->status, 'email' => $checkout->email, 'payment_method' => $checkout->payment_method, 'shipping_address_json' => $checkout->shipping_address_json, 'billing_address_json' => $checkout->billing_address_json, 'shipping_method_id' => $checkout->shipping_rate_id, 'discount_code' => $checkout->discount_code, 'lines' => $checkout->cart->lines->map(fn ($line): array => ['variant_id' => $line->variant_id, 'product_title' => $line->variant->product->title, 'variant_title' => $line->variant->title, 'sku' => $line->variant->sku, 'quantity' => $line->quantity, 'unit_price_amount' => $line->unit_price_amount, 'line_total_amount' => $line->line_total_amount])->all(), 'totals' => $totals, 'available_shipping_methods' => $rates->map(fn ($rate): array => ['id' => $rate->id, 'name' => $rate->name, 'type' => $rate->type, 'price_amount' => $rate->price_amount, 'currency' => $rate->currency, 'estimated_days_min' => $rate->estimated_days_min, 'estimated_days_max' => $rate->estimated_days_max])->all(), 'expires_at' => $checkout->expires_at, 'created_at' => $checkout->created_at];
    }
}
<?php

namespace App\Services;

use App\Models\Checkout;
use App\Models\Discount;
use App\Models\TaxSettings;
use App\ValueObjects\PricingResult;

class PricingEngine
{
    public function __construct(private readonly DiscountService $discounts, private readonly ShippingCalculator $shipping, private readonly TaxCalculator $taxes) {}

    public function calculate(Checkout $checkout): PricingResult
    {
        $checkout->load(['cart.lines.variant.product.collections', 'shippingRate']);
        $cart = $checkout->cart;
        $lines = $cart->lines;
        $subtotal = (int) $lines->sum(fn ($line): int => $line->unit_price_amount * $line->quantity);
        $discountAmount = 0;
        $freeShipping = false;

        foreach ($lines as $line) {
            $line->updateQuietly(['line_discount_amount' => 0, 'line_total_amount' => $line->line_subtotal_amount]);
        }

        if ($checkout->discount_code !== null) {
            $discount = Discount::withoutGlobalScopes()->where('store_id', $checkout->store_id)->whereRaw('lower(code) = ?', [strtolower($checkout->discount_code)])->first();

            if ($discount?->isAvailable()) {
                $result = $this->discounts->calculate($discount, $subtotal, $lines->map(fn ($line): array => [
                    'line_id' => $line->id,
                    'amount' => $line->unit_price_amount * $line->quantity,
                    'product_id' => $line->variant->product_id,
                    'collection_ids' => $line->variant->product->collections->modelKeys(),
                ])->all());
                $discountAmount = $result->amount;
                $freeShipping = $result->freeShipping;

                foreach ($lines as $line) {
                    $lineDiscount = $result->allocations[$line->id] ?? 0;
                    $line->updateQuietly(['line_discount_amount' => $lineDiscount, 'line_total_amount' => max(0, $line->line_subtotal_amount - $lineDiscount)]);
                }
            }
        }

        $shippingAmount = $checkout->shippingRate !== null && ! $freeShipping ? $this->shipping->calculate($checkout->shippingRate, $cart) : 0;
        $taxSettings = TaxSettings::withoutGlobalScopes()->firstOrCreate(['store_id' => $checkout->store_id], ['default_rate_basis_points' => 0]);
        $taxLines = [];

        foreach ($lines as $line) {
            $lineAmount = max(0, $line->line_subtotal_amount - $line->line_discount_amount);
            $taxLines = [...$taxLines, ...$this->taxes->calculate($lineAmount, $taxSettings, $checkout->shipping_address_json ?? [])->lines];
        }

        if ($shippingAmount > 0) {
            $taxLines = [...$taxLines, ...$this->taxes->calculate($shippingAmount, $taxSettings, $checkout->shipping_address_json ?? [])->lines];
        }

        $taxTotal = (int) collect($taxLines)->sum('amount');
        $discountedSubtotal = max(0, $subtotal - $discountAmount);
        $pricesIncludeTax = (bool) $taxSettings->prices_include_tax || $taxSettings->mode === 'inclusive';
        $total = max(0, $discountedSubtotal + $shippingAmount + ($pricesIncludeTax ? 0 : $taxTotal));
        $result = new PricingResult($subtotal, $discountAmount, $shippingAmount, $taxLines, $taxTotal, $total, $cart->currency);
        $checkout->update(['totals_json' => $result->toArray()]);

        return $result;
    }
}
<?php

namespace App\Services;

use App\Contracts\TaxProvider;
use App\Models\TaxSettings;
use App\ValueObjects\TaxCalculationRequest;
use App\ValueObjects\TaxResult;

class TaxCalculator
{
    public function __construct(private readonly ?TaxProvider $provider = null) {}

    public function calculate(int $amount, TaxSettings $settings, array $address): TaxResult
    {
        return ($this->provider ?? new \App\Services\Tax\ManualTaxProvider)->calculate(new TaxCalculationRequest([['amount' => $amount]], 0, $address, $settings));
    }

    public function extractInclusive(int $grossAmount, int $rateBasisPoints): int
    {
        return $rateBasisPoints > 0 ? intdiv($grossAmount * $rateBasisPoints, 10000 + $rateBasisPoints) : 0;
    }

    public function addExclusive(int $netAmount, int $rateBasisPoints): int
    {
        return intdiv($netAmount * $rateBasisPoints, 10000);
    }
}
<?php

namespace App\Services;

use App\Models\Cart;
use App\Models\ShippingRate;
use App\Models\Store;
use Illuminate\Support\Collection;

class ShippingCalculator
{
    public function getAvailableRates(Store $store, array $address): Collection
    {
        $country = strtoupper((string) ($address['country_code'] ?? ''));
        $region = strtoupper((string) ($address['province_code'] ?? ''));

        $rates = ShippingRate::query()->where('is_active', true)->whereHas('zone', fn ($query) => $query->where('store_id', $store->getKey()))->with('zone')->get();

        $matching = $rates->filter(function (ShippingRate $rate) use ($country, $region): bool {
            $countries = array_map('strtoupper', $rate->zone->countries_json ?? []);
            $regions = array_map('strtoupper', $rate->zone->regions_json ?? []);

            return ($region !== '' && in_array($region, $regions, true))
                || ($country !== '' && in_array($country, $countries, true))
                || ($countries === [] && $regions === []);
        });

        $specificity = $matching->groupBy(function (ShippingRate $rate) use ($country, $region): int {
            $countries = array_map('strtoupper', $rate->zone->countries_json ?? []);
            $regions = array_map('strtoupper', $rate->zone->regions_json ?? []);

            return ($region !== '' && in_array($region, $regions, true)) ? 2 : (($country !== '' && in_array($country, $countries, true)) ? 1 : 0);
        });

        return $specificity->sortKeysDesc()->first() ?? collect();
    }

    public function calculate(ShippingRate $rate, Cart $cart): int
    {
        $lines = $cart->load('lines.variant')->lines;
        $lines = $lines->filter(fn ($line): bool => (bool) $line->variant->requires_shipping);
        $weight = (int) $lines->sum(fn ($line): int => $line->quantity * ($line->variant->weight_g ?? $line->variant->weight_grams));
        $subtotal = (int) $lines->sum('line_total_amount');
        $config = $rate->config_json ?? [];

        return match ($rate->type) {
            'weight' => $this->rangeAmount($config['ranges'] ?? [], $weight, $rate->price_amount),
            'price' => $this->rangeAmount($config['ranges'] ?? [], $subtotal, $rate->price_amount),
            default => $rate->price_amount,
        };
    }

    private function rangeAmount(array $ranges, int $value, int $fallback): int
    {
        foreach ($ranges as $range) {
            if ($value >= (int) ($range['min_g'] ?? $range['min_amount'] ?? 0) && $value <= (int) ($range['max_g'] ?? $range['max_amount'] ?? PHP_INT_MAX)) {
                return (int) ($range['amount'] ?? $fallback);
            }
        }

        return 0;
    }
}


text:
Script completed
Wall time 0.1 seconds
Output:

text:
        }

        return response()->json($this->payload($checkout));
    }

    private function checkout(int $checkoutId): Checkout
    {
        $checkout = Checkout::query()->with(['cart.lines.variant.product', 'shippingRate'])->findOrFail($checkoutId);
        $customerId = request()->user('customer')?->getKey();

        if ($customerId !== null) {
            abort_unless((int) $checkout->customer_id === (int) $customerId, 404);
        } else {
            $sessionCartIds = array_filter([request()->session()->get('cart_id'), request()->session()->get('cart_id_'.app('current_store')->getKey())]);
            abort_unless(in_array($checkout->cart_id, $sessionCartIds, true), 404);
        }

        return $checkout;
    }

    /** @return array<string, mixed> */
    private function payload(Checkout $checkout): array
    {
        $totals = $checkout->totals_json ?? $this->pricing->calculate($checkout)->toArray();
        $rates = $checkout->shipping_address_json === null ? collect() : $this->shipping->getAvailableRates(app('current_store'), $checkout->shipping_address_json);

        return ['id' => $checkout->id, 'store_id' => $checkout->store_id, 'cart_id' => $checkout->cart_id, 'customer_id' => $checkout->customer_id, 'status' => $checkout->status, 'email' => $checkout->email, 'payment_method' => $checkout->payment_method, 'shipping_address_json' => $checkout->shipping_address_json, 'billing_address_json' => $checkout->billing_address_json, 'shipping_method_id' => $checkout->shipping_rate_id, 'discount_code' => $checkout->discount_code, 'lines' => $checkout->cart->lines->map(fn ($line): array => ['variant_id' => $line->variant_id, 'product_title' => $line->variant->product->title, 'variant_title' => $line->variant->title, 'sku' => $line->variant->sku, 'quantity' => $line->quantity, 'unit_price_amount' => $line->unit_price_amount, 'line_total_amount' => $line->line_total_amount])->all(), 'totals' => $totals, 'available_shipping_methods' => $rates->map(fn ($rate): array => ['id' => $rate->id, 'name' => $rate->name, 'type' => $rate->type, 'price_amount' => $rate->price_amount, 'currency' => $rate->currency, 'estimated_days_min' => $rate->estimated_days_min, 'estimated_days_max' => $rate->estimated_days_max])->all(), 'expires_at' => $checkout->expires_at, 'created_at' => $checkout->created_at];
    }
}
<?php

namespace App\Services;

use App\Models\Checkout;
use App\Models\Discount;
use App\Models\TaxSettings;
use App\ValueObjects\PricingResult;

class PricingEngine
{
    public function __construct(private readonly DiscountService $discounts, private readonly ShippingCalculator $shipping, private readonly TaxCalculator $taxes) {}

    public function calculate(Checkout $checkout): PricingResult
    {
        $checkout->load(['cart.lines.variant.product.collections', 'shippingRate']);
        $cart = $checkout->cart;
        $lines = $cart->lines;
        $subtotal = (int) $lines->sum(fn ($line): int => $line->unit_price_amount * $line->quantity);
        $discountAmount = 0;
        $freeShipping = false;

        foreach ($lines as $line) {
            $line->updateQuietly(['line_discount_amount' => 0, 'line_total_amount' => $line->line_subtotal_amount]);
        }

        if ($checkout->discount_code !== null) {
            $discount = Discount::withoutGlobalScopes()->where('store_id', $checkout->store_id)->whereRaw('lower(code) = ?', [strtolower($checkout->discount_code)])->first();

            if ($discount?->isAvailable()) {
                $result = $this->discounts->calculate($discount, $subtotal, $lines->map(fn ($line): array => [
                    'line_id' => $line->id,
                    'amount' => $line->unit_price_amount * $line->quantity,
                    'product_id' => $line->variant->product_id,
                    'collection_ids' => $line->variant->product->collections->modelKeys(),
                ])->all());
                $discountAmount = $result->amount;
                $freeShipping = $result->freeShipping;

                foreach ($lines as $line) {
                    $lineDiscount = $result->allocations[$line->id] ?? 0;
                    $line->updateQuietly(['line_discount_amount' => $lineDiscount, 'line_total_amount' => max(0, $line->line_subtotal_amount - $lineDiscount)]);
                }
            }
        }

        $shippingAmount = $checkout->shippingRate !== null && ! $freeShipping ? $this->shipping->calculate($checkout->shippingRate, $cart) : 0;
        $taxSettings = TaxSettings::withoutGlobalScopes()->firstOrCreate(['store_id' => $checkout->store_id], ['default_rate_basis_points' => 0]);
        $taxLines = [];

        foreach ($lines as $line) {
            $lineAmount = max(0, $line->line_subtotal_amount - $line->line_discount_amount);
            $taxLines = [...$taxLines, ...$this->taxes->calculate($lineAmount, $taxSettings, $checkout->shipping_address_json ?? [])->lines];
        }

        if ($shippingAmount > 0) {
            $taxLines = [...$taxLines, ...$this->taxes->calculate($shippingAmount, $taxSettings, $checkout->shipping_address_json ?? [])->lines];
        }

        $taxTotal = (int) collect($taxLines)->sum('amount');
        $discountedSubtotal = max(0, $subtotal - $discountAmount);
        $pricesIncludeTax = (bool) $taxSettings->prices_include_tax || $taxSettings->mode === 'inclusive';
        $total = max(0, $discountedSubtotal + $shippingAmount + ($pricesIncludeTax ? 0 : $taxTotal));
        $result = new PricingResult($subtotal, $discountAmount, $shippingAmount, $taxLines, $taxTotal, $total, $cart->currency);
        $checkout->update(['totals_json' => $result->toArray()]);

        return $result;
    }
}
<?php

namespace App\Services;

use App\Contracts\TaxProvider;
use App\Models\TaxSettings;
use App\ValueObjects\TaxCalculationRequest;
use App\ValueObjects\TaxResult;

class TaxCalculator
{
    public function __construct(private readonly ?TaxProvider $provider = null) {}

    public function calculate(int $amount, TaxSettings $settings, array $address): TaxResult
    {
        return ($this->provider ?? new \App\Services\Tax\ManualTaxProvider)->calculate(new TaxCalculationRequest([['amount' => $amount]], 0, $address, $settings));
    }

    public function extractInclusive(int $grossAmount, int $rateBasisPoints): int
    {
        return $rateBasisPoints > 0 ? intdiv($grossAmount * $rateBasisPoints, 10000 + $rateBasisPoints) : 0;
    }

    public function addExclusive(int $netAmount, int $rateBasisPoints): int
    {
        return intdiv($netAmount * $rateBasisPoints, 10000);
    }
}
<?php

namespace App\Services;

use App\Models\Cart;
use App\Models\ShippingRate;
use App\Models\Store;
use Illuminate\Support\Collection;

class ShippingCalculator
{
    public function getAvailableRates(Store $store, array $address): Collection
    {
        $country = strtoupper((string) ($address['country_code'] ?? ''));
        $region = strtoupper((string) ($address['province_code'] ?? ''));

        $rates = ShippingRate::query()->where('is_active', true)->whereHas('zone', fn ($query) => $query->where('store_id', $store->getKey()))->with('zone')->get();

        $matching = $rates->filter(function (ShippingRate $rate) use ($country, $region): bool {
            $countries = array_map('strtoupper', $rate->zone->countries_json ?? []);
            $regions = array_map('strtoupper', $rate->zone->regions_json ?? []);

            return ($region !== '' && in_array($region, $regions, true))
                || ($country !== '' && in_array($country, $countries, true))
                || ($countries === [] && $regions === []);
        });

        $specificity = $matching->groupBy(function (ShippingRate $rate) use ($country, $region): int {
            $countries = array_map('strtoupper', $rate->zone->countries_json ?? []);
            $regions = array_map('strtoupper', $rate->zone->regions_json ?? []);

            return ($region !== '' && in_array($region, $regions, true)) ? 2 : (($country !== '' && in_array($country, $countries, true)) ? 1 : 0);
        });

        return $specificity->sortKeysDesc()->first() ?? collect();
    }

    public function calculate(ShippingRate $rate, Cart $cart): int
    {
        $lines = $cart->load('lines.variant')->lines;
        $lines = $lines->filter(fn ($line): bool => (bool) $line->variant->requires_shipping);
        $weight = (int) $lines->sum(fn ($line): int => $line->quantity * ($line->variant->weight_g ?? $line->variant->weight_grams));
        $subtotal = (int) $lines->sum('line_total_amount');
        $config = $rate->config_json ?? [];

        return match ($rate->type) {
            'weight' => $this->rangeAmount($config['ranges'] ?? [], $weight, $rate->price_amount),
            'price' => $this->rangeAmount($config['ranges'] ?? [], $subtotal, $rate->price_amount),
            default => $rate->price_amount,
        };
    }

    private function rangeAmount(array $ranges, int $value, int $fallback): int
    {
        foreach ($ranges as $range) {
            if ($value >= (int) ($range['min_g'] ?? $range['min_amount'] ?? 0) && $value <= (int) ($range['max_g'] ?? $range['max_amount'] ?? PHP_INT_MAX)) {
                return (int) ($range['amount'] ?? $fallback);
            }
        }

        return 0;
    }
}


exec

status: completed

input:
const r = await tools.exec_command({
  cmd: "sed -n '1,240p' app/Models/Discount.php && sed -n '1,260p' app/Services/DiscountService.php && sed -n '1,260p' app/Models/TaxSettings.php && sed -n '1,240p' app/Services/Tax/ManualTaxProvider.php && sed -n '1,220p' app/Models/ThemeSetting.php && sed -n '1,180p' app/Models/ThemeFile.php",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms: 1000,
  max_output_tokens: 26000
});
text(r.output);


type: CommandExecution

command: sed -n '1,240p' app/Models/Discount.php && sed -n '1,260p' app/Services/DiscountService.php && sed -n '1,260p' app/Models/TaxSettings.php && sed -n '1,240p' app/Services/Tax/ManualTaxProvider.php && sed -n '1,220p' app/Models/ThemeSetting.php && sed -n '1,180p' app/Models/ThemeFile.php

Discount.php

type: read

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

ThemeFile.php

status: completed

stdout:
<?php

namespace App\Models;

use App\Enums\DiscountType;
use App\Enums\DiscountValueType;
use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Model;

class Discount extends Model
{
    use BelongsToStore;

    protected $fillable = ['store_id', 'code', 'type', 'value_type', 'value_amount', 'status', 'usage_limit', 'usage_count', 'starts_at', 'ends_at', 'rules_json'];

    protected function casts(): array
    {
        return ['type' => DiscountType::class, 'value_type' => DiscountValueType::class, 'starts_at' => 'datetime', 'ends_at' => 'datetime', 'rules_json' => 'array'];
    }

    public function isAvailable(): bool
    {
        return $this->status === 'active'
            && ($this->starts_at === null || $this->starts_at->isPast())
            && ($this->ends_at === null || $this->ends_at->isFuture())
            && ($this->usage_limit === null || $this->usage_count < $this->usage_limit);
    }
}
<?php

namespace App\Services;

use App\Enums\DiscountValueType;
use App\Exceptions\InvalidDiscountException;
use App\Models\Cart;
use App\Models\Discount;
use App\Models\Store;
use App\ValueObjects\DiscountResult;
use Illuminate\Support\Collection;

class DiscountService
{
    public function validate(string $code, Store $store, Cart $cart): Discount
    {
        $discount = Discount::withoutGlobalScopes()->where('store_id', $store->getKey())->whereRaw('lower(code) = ?', [strtolower($code)])->first();

        if ($discount === null) {
            throw new InvalidDiscountException('This discount code does not exist.', 'discount_not_found');
        }

        if ($discount->status !== 'active' || ($discount->ends_at !== null && $discount->ends_at->isPast())) {
            throw new InvalidDiscountException('This discount code has expired.', 'discount_expired');
        }

        if ($discount->starts_at !== null && $discount->starts_at->isFuture()) {
            throw new InvalidDiscountException('This discount code is not active yet.', 'discount_not_yet_active');
        }

        if ($discount->usage_limit !== null && $discount->usage_count >= $discount->usage_limit) {
            throw new InvalidDiscountException('This discount code has reached its usage limit.', 'discount_usage_limit_reached');
        }

        $cart->loadMissing('lines.variant.product.collections');
        $minimum = (int) ($discount->rules_json['min_purchase_amount'] ?? $discount->rules_json['minimum_purchase_amount'] ?? 0);

        if ((int) $cart->lines->sum('line_subtotal_amount') < $minimum) {
            throw new InvalidDiscountException('This discount requires a higher subtotal.', 'discount_min_purchase_not_met');
        }

        if ($this->qualifyingLines($discount, $cart->lines)->isEmpty()) {
            throw new InvalidDiscountException('This discount does not apply to the items in your cart.', 'discount_not_applicable');
        }

        return $discount;
    }

    /** @param array<int, array{line_id?: int, amount?: int, product_id?: int, collection_ids?: array<int, int>, quantity?: int}> $lines */
    public function calculate(Discount $discount, int $subtotal, array $lines): DiscountResult
    {
        $qualifyingLines = array_values(array_filter($lines, fn (array $line): bool => $this->lineQualifies($discount, $line)));
        $eligibleSubtotal = array_sum(array_map(fn (array $line): int => (int) ($line['amount'] ?? 0), $qualifyingLines));
        $amount = match ($discount->value_type) {
            DiscountValueType::Percent => intdiv($eligibleSubtotal * $discount->value_amount, 100),
            DiscountValueType::Fixed => min($discount->value_amount, $eligibleSubtotal),
            DiscountValueType::FreeShipping => 0,
        };
        $allocations = [];

        if ($amount > 0 && $eligibleSubtotal > 0) {
            $lastIndex = count($qualifyingLines) - 1;
            $allocated = 0;

            foreach ($qualifyingLines as $index => $line) {
                $lineAmount = (int) ($line['amount'] ?? 0);
                $allocation = $index === $lastIndex
                    ? $amount - $allocated
                    : min($amount - $allocated, intdiv((2 * $amount * $lineAmount) + $eligibleSubtotal, 2 * $eligibleSubtotal));
                $allocations[(int) ($line['line_id'] ?? 0)] = $allocation;
                $allocated += $allocation;
            }
        }

        return new DiscountResult($amount, $allocations, $discount->value_type === DiscountValueType::FreeShipping);
    }

    /** @param \Illuminate\Support\Collection<int, \App\Models\CartLine> $lines */
    private function qualifyingLines(Discount $discount, Collection $lines): Collection
    {
        return $lines->filter(function ($line) use ($discount): bool {
            return $this->lineQualifies($discount, [
                'product_id' => $line->variant?->product_id,
                'collection_ids' => $line->variant?->product?->collections?->modelKeys() ?? [],
            ]);
        });
    }

    /** @param array{product_id?: int|null, collection_ids?: array<int, int>} $line */
    private function lineQualifies(Discount $discount, array $line): bool
    {
        $rules = $discount->rules_json ?? [];
        $productIds = array_map('intval', array_filter($rules['applicable_product_ids'] ?? []));
        $collectionIds = array_map('intval', array_filter($rules['applicable_collection_ids'] ?? []));

        if ($productIds === [] && $collectionIds === []) {
            return true;
        }

        return in_array((int) ($line['product_id'] ?? 0), $productIds, true)
            || array_intersect($collectionIds, array_map('intval', $line['collection_ids'] ?? [])) !== [];
    }
}
<?php

namespace App\Models;

use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class TaxSettings extends Model
{
    use BelongsToStore;

    protected $primaryKey = 'store_id';

    public $incrementing = false;

    protected $fillable = ['store_id', 'mode', 'provider', 'prices_include_tax', 'config_json', 'default_rate_basis_points', 'rates_json', 'provider_config_json'];

    protected function casts(): array
    {
        return ['rates_json' => 'array', 'provider_config_json' => 'array', 'config_json' => 'array', 'prices_include_tax' => 'boolean'];
    }

    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }
}
<?php

namespace App\Services\Tax;

use App\Contracts\TaxProvider;
use App\ValueObjects\TaxCalculationRequest;
use App\ValueObjects\TaxLine;
use App\ValueObjects\TaxResult;

class ManualTaxProvider implements TaxProvider
{
    public function calculate(TaxCalculationRequest $request): TaxResult
    {
        $country = strtoupper((string) ($request->address['country_code'] ?? ''));
        $rate = (int) ($request->settings->rates_json[$country] ?? $request->settings->default_rate_basis_points);
        $taxLines = [];

        foreach ($request->lineItems as $line) {
            $amount = (int) ($line['amount'] ?? $line['line_total_amount'] ?? 0);
            $tax = $request->settings->prices_include_tax || $request->settings->mode === 'inclusive'
                ? $this->extractInclusive($amount, $rate)
                : $this->roundTax($amount, $rate);

            if ($tax > 0) {
                $taxLines[] = new TaxLine('Sales tax', $rate, $tax);
            }
        }

        if ($request->shippingAmount > 0) {
            $tax = $request->settings->prices_include_tax || $request->settings->mode === 'inclusive'
                ? $this->extractInclusive($request->shippingAmount, $rate)
                : $this->roundTax($request->shippingAmount, $rate);

            if ($tax > 0) {
                $taxLines[] = new TaxLine('Shipping tax', $rate, $tax);
            }
        }

        return new TaxResult(array_sum(array_map(fn (TaxLine $line): int => $line->amount, $taxLines)), $taxLines);
    }

    private function roundTax(int $amount, int $rate): int
    {
        return (int) floor(($amount * $rate / 10000) + 0.5);
    }

    private function extractInclusive(int $amount, int $rate): int
    {
        return $rate > 0 ? $amount - intdiv($amount * 10000, 10000 + $rate) : 0;
    }
}
<?php

namespace App\Models;

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

class ThemeSetting extends Model
{
    /** @use HasFactory<\Database\Factories\ThemeSettingFactory> */
    use HasFactory;

    protected $primaryKey = 'theme_id';

    public $incrementing = false;

    public const CREATED_AT = null;

    protected $keyType = 'int';

    protected $attributes = ['settings_json' => '{}'];

    protected $fillable = ['theme_id', 'settings_json'];

    protected function casts(): array
    {
        return ['settings_json' => 'array'];
    }

    public function theme(): BelongsTo
    {
        return $this->belongsTo(Theme::class);
    }
}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class ThemeFile extends Model
{
    protected $fillable = ['theme_id', 'path', 'content', 'storage_key', 'sha256', 'byte_size'];

    public function theme(): BelongsTo
    {
        return $this->belongsTo(Theme::class);
    }
}


text:
Script completed
Wall time 0.1 seconds
Output:

text:
<?php

namespace App\Models;

use App\Enums\DiscountType;
use App\Enums\DiscountValueType;
use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Model;

class Discount extends Model
{
    use BelongsToStore;

    protected $fillable = ['store_id', 'code', 'type', 'value_type', 'value_amount', 'status', 'usage_limit', 'usage_count', 'starts_at', 'ends_at', 'rules_json'];

    protected function casts(): array
    {
        return ['type' => DiscountType::class, 'value_type' => DiscountValueType::class, 'starts_at' => 'datetime', 'ends_at' => 'datetime', 'rules_json' => 'array'];
    }

    public function isAvailable(): bool
    {
        return $this->status === 'active'
            && ($this->starts_at === null || $this->starts_at->isPast())
            && ($this->ends_at === null || $this->ends_at->isFuture())
            && ($this->usage_limit === null || $this->usage_count < $this->usage_limit);
    }
}
<?php

namespace App\Services;

use App\Enums\DiscountValueType;
use App\Exceptions\InvalidDiscountException;
use App\Models\Cart;
use App\Models\Discount;
use App\Models\Store;
use App\ValueObjects\DiscountResult;
use Illuminate\Support\Collection;

class DiscountService
{
    public function validate(string $code, Store $store, Cart $cart): Discount
    {
        $discount = Discount::withoutGlobalScopes()->where('store_id', $store->getKey())->whereRaw('lower(code) = ?', [strtolower($code)])->first();

        if ($discount === null) {
            throw new InvalidDiscountException('This discount code does not exist.', 'discount_not_found');
        }

        if ($discount->status !== 'active' || ($discount->ends_at !== null && $discount->ends_at->isPast())) {
            throw new InvalidDiscountException('This discount code has expired.', 'discount_expired');
        }

        if ($discount->starts_at !== null && $discount->starts_at->isFuture()) {
            throw new InvalidDiscountException('This discount code is not active yet.', 'discount_not_yet_active');
        }

        if ($discount->usage_limit !== null && $discount->usage_count >= $discount->usage_limit) {
            throw new InvalidDiscountException('This discount code has reached its usage limit.', 'discount_usage_limit_reached');
        }

        $cart->loadMissing('lines.variant.product.collections');
        $minimum = (int) ($discount->rules_json['min_purchase_amount'] ?? $discount->rules_json['minimum_purchase_amount'] ?? 0);

        if ((int) $cart->lines->sum('line_subtotal_amount') < $minimum) {
            throw new InvalidDiscountException('This discount requires a higher subtotal.', 'discount_min_purchase_not_met');
        }

        if ($this->qualifyingLines($discount, $cart->lines)->isEmpty()) {
            throw new InvalidDiscountException('This discount does not apply to the items in your cart.', 'discount_not_applicable');
        }

        return $discount;
    }

    /** @param array<int, array{line_id?: int, amount?: int, product_id?: int, collection_ids?: array<int, int>, quantity?: int}> $lines */
    public function calculate(Discount $discount, int $subtotal, array $lines): DiscountResult
    {
        $qualifyingLines = array_values(array_filter($lines, fn (array $line): bool => $this->lineQualifies($discount, $line)));
        $eligibleSubtotal = array_sum(array_map(fn (array $line): int => (int) ($line['amount'] ?? 0), $qualifyingLines));
        $amount = match ($discount->value_type) {
            DiscountValueType::Percent => intdiv($eligibleSubtotal * $discount->value_amount, 100),
            DiscountValueType::Fixed => min($discount->value_amount, $eligibleSubtotal),
            DiscountValueType::FreeShipping => 0,
        };
        $allocations = [];

        if ($amount > 0 && $eligibleSubtotal > 0) {
            $lastIndex = count($qualifyingLines) - 1;
            $allocated = 0;

            foreach ($qualifyingLines as $index => $line) {
                $lineAmount = (int) ($line['amount'] ?? 0);
                $allocation = $index === $lastIndex
                    ? $amount - $allocated
                    : min($amount - $allocated, intdiv((2 * $amount * $lineAmount) + $eligibleSubtotal, 2 * $eligibleSubtotal));
                $allocations[(int) ($line['line_id'] ?? 0)] = $allocation;
                $allocated += $allocation;
            }
        }

        return new DiscountResult($amount, $allocations, $discount->value_type === DiscountValueType::FreeShipping);
    }

    /** @param \Illuminate\Support\Collection<int, \App\Models\CartLine> $lines */
    private function qualifyingLines(Discount $discount, Collection $lines): Collection
    {
        return $lines->filter(function ($line) use ($discount): bool {
            return $this->lineQualifies($discount, [
                'product_id' => $line->variant?->product_id,
                'collection_ids' => $line->variant?->product?->collections?->modelKeys() ?? [],
            ]);
        });
    }

    /** @param array{product_id?: int|null, collection_ids?: array<int, int>} $line */
    private function lineQualifies(Discount $discount, array $line): bool
    {
        $rules = $discount->rules_json ?? [];
        $productIds = array_map('intval', array_filter($rules['applicable_product_ids'] ?? []));
        $collectionIds = array_map('intval', array_filter($rules['applicable_collection_ids'] ?? []));

        if ($productIds === [] && $collectionIds === []) {
            return true;
        }

        return in_array((int) ($line['product_id'] ?? 0), $productIds, true)
            || array_intersect($collectionIds, array_map('intval', $line['collection_ids'] ?? [])) !== [];
    }
}
<?php

namespace App\Models;

use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class TaxSettings extends Model
{
    use BelongsToStore;

    protected $primaryKey = 'store_id';

    public $incrementing = false;

    protected $fillable = ['store_id', 'mode', 'provider', 'prices_include_tax', 'config_json', 'default_rate_basis_points', 'rates_json', 'provider_config_json'];

    protected function casts(): array
    {
        return ['rates_json' => 'array', 'provider_config_json' => 'array', 'config_json' => 'array', 'prices_include_tax' => 'boolean'];
    }

    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }
}
<?php

namespace App\Services\Tax;

use App\Contracts\TaxProvider;
use App\ValueObjects\TaxCalculationRequest;
use App\ValueObjects\TaxLine;
use App\ValueObjects\TaxResult;

class ManualTaxProvider implements TaxProvider
{
    public function calculate(TaxCalculationRequest $request): TaxResult
    {
        $country = strtoupper((string) ($request->address['country_code'] ?? ''));
        $rate = (int) ($request->settings->rates_json[$country] ?? $request->settings->default_rate_basis_points);
        $taxLines = [];

        foreach ($request->lineItems as $line) {
            $amount = (int) ($line['amount'] ?? $line['line_total_amount'] ?? 0);
            $tax = $request->settings->prices_include_tax || $request->settings->mode === 'inclusive'
                ? $this->extractInclusive($amount, $rate)
                : $this->roundTax($amount, $rate);

            if ($tax > 0) {
                $taxLines[] = new TaxLine('Sales tax', $rate, $tax);
            }
        }

        if ($request->shippingAmount > 0) {
            $tax = $request->settings->prices_include_tax || $request->settings->mode === 'inclusive'
                ? $this->extractInclusive($request->shippingAmount, $rate)
                : $this->roundTax($request->shippingAmount, $rate);

            if ($tax > 0) {
                $taxLines[] = new TaxLine('Shipping tax', $rate, $tax);
            }
        }

        return new TaxResult(array_sum(array_map(fn (TaxLine $line): int => $line->amount, $taxLines)), $taxLines);
    }

    private function roundTax(int $amount, int $rate): int
    {
        return (int) floor(($amount * $rate / 10000) + 0.5);
    }

    private function extractInclusive(int $amount, int $rate): int
    {
        return $rate > 0 ? $amount - intdiv($amount * 10000, 10000 + $rate) : 0;
    }
}
<?php

namespace App\Models;

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

class ThemeSetting extends Model
{
    /** @use HasFactory<\Database\Factories\ThemeSettingFactory> */
    use HasFactory;

    protected $primaryKey = 'theme_id';

    public $incrementing = false;

    public const CREATED_AT = null;

    protected $keyType = 'int';

    protected $attributes = ['settings_json' => '{}'];

    protected $fillable = ['theme_id', 'settings_json'];

    protected function casts(): array
    {
        return ['settings_json' => 'array'];
    }

    public function theme(): BelongsTo
    {
        return $this->belongsTo(Theme::class);
    }
}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class ThemeFile extends Model
{
    protected $fillable = ['theme_id', 'path', 'content', 'storage_key', 'sha256', 'byte_size'];

    public function theme(): BelongsTo
    {
        return $this->belongsTo(Theme::class);
    }
}


exec

status: completed

input:
const r = await tools.exec_command({
  cmd: "cat app/Enums/DiscountType.php app/Enums/DiscountValueType.php; sed -n '1,180p' database/migrations/*create_discounts_table.php 2>/dev/null; rg -n \"TaxSettings|provider|shipping|expires|abandoned_cart|bank_transfer\" app database specs/05-BUSINESS-LOGIC.md | sed -n '1,240p'",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms: 1000,
  max_output_tokens: 22000
});
text(r.output);