Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.78% covered (warning)
77.78%
7 / 9
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
CategoryTaxController
77.78% covered (warning)
77.78%
7 / 9
50.00% covered (danger)
50.00%
1 / 2
4.18
0.00% covered (danger)
0.00%
0 / 1
 index
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 update
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace App\Http\Controllers\admin;
4
5use App\Http\Controllers\Controller;
6use App\Models\Category;
7use Illuminate\Http\RedirectResponse;
8use Illuminate\Http\Request;
9use Illuminate\View\View;
10
11/**
12 * Settings → Tax Rates by Category. A category with no override here uses
13 * the store-wide Checkout & Delivery tax rate (see CartService::totals()).
14 * Deliberately its own small screen rather than another field bolted onto
15 * the category tree editor (admin/configuration/category.blade.php) — that
16 * page's create/edit form is shared, JS-driven state that this doesn't need
17 * to risk touching. Gated by the `tax_vat` feature.
18 */
19class CategoryTaxController extends Controller
20{
21    public function index(): View
22    {
23        $categories = Category::query()->orderBy('parent_id')->orderBy('name')->get();
24
25        return view('admin.category-tax.index', compact('categories'));
26    }
27
28    public function update(Request $request): RedirectResponse
29    {
30        $data = $request->validate([
31            'rates' => ['nullable', 'array'],
32            'rates.*' => ['nullable', 'numeric', 'min:0', 'max:100'],
33        ]);
34
35        foreach ($data['rates'] ?? [] as $categoryId => $rate) {
36            Category::whereKey($categoryId)->update(['tax_rate' => $rate === '' ? null : $rate]);
37        }
38
39        return redirect()->route('admin.category-tax.index')->with('success', 'Tax rates updated.');
40    }
41}