docs: add VAT + RFQ vatable checkbox design spec
This commit is contained in:
parent
dca9cd5d99
commit
3252cf7fb8
133
docs/superpowers/specs/2026-06-01-vat-rfq-design.md
Normal file
133
docs/superpowers/specs/2026-06-01-vat-rfq-design.md
Normal file
@ -0,0 +1,133 @@
|
||||
# VAT Settings + RFQ Per-Item Vatable Checkbox — Design Spec
|
||||
|
||||
**Date:** 2026-06-01
|
||||
**Status:** Approved
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Two connected features:
|
||||
|
||||
1. **Global VAT setting** — Admin sets a VAT rate (%) once in Settings. Stored in the existing `settings` key/value table as `vat_rate`.
|
||||
2. **Per-item vatable checkbox on RFQ portal** — When a supplier fills in their quote, each item row has a "VAT?" checkbox. Ticking it applies the global VAT rate to that item's total. The footer shows a live breakdown: Subtotal → VAT → Grand Total. Everything is stored against the quote on submission.
|
||||
|
||||
---
|
||||
|
||||
## 1. VAT Global Setting
|
||||
|
||||
### Route & Controller
|
||||
- `GET settings/vat` → `VatSettingController@index` — renders the VAT settings page
|
||||
- `POST settings/vat` → `VatSettingController@update` — saves the rate via AJAX, returns JSON
|
||||
|
||||
### Storage
|
||||
- Uses the existing `Setting` model (`settings` table, key/value pairs)
|
||||
- Key: `vat_rate`, value: decimal string e.g. `"10"` for 10%
|
||||
- Default when not set: `0`
|
||||
|
||||
### View: `resources/views/settings/vat.blade.php`
|
||||
- Extends `layouts.app`
|
||||
- Single card with a number input: "VAT Rate (%)" — accepts decimals (e.g. 5, 10, 14.5)
|
||||
- Save button submits via `fetch()` AJAX (no page reload, per project rules)
|
||||
- Shows current saved value on load
|
||||
- Success/error feedback via `showToast()`
|
||||
|
||||
### Sidebar
|
||||
- Add "VAT Settings" link under the `@role('Admin')` System section in `layouts/app.blade.php`
|
||||
- Active state: `request()->routeIs('settings.vat')`
|
||||
|
||||
---
|
||||
|
||||
## 2. RFQ Portal — Per-Item Vatable Checkbox
|
||||
|
||||
### Database
|
||||
- New migration: add `is_vatable` boolean (default `false`) to `supplier_quote_items` table
|
||||
- `SupplierQuoteItem::$fillable` gains `is_vatable`
|
||||
|
||||
### Controller: `RfqPortalController`
|
||||
|
||||
**`show()` method:**
|
||||
- Load VAT rate: `$vatRate = (float) Setting::get('vat_rate', 0)`
|
||||
- Pass `$vatRate` to `rfq.show` view
|
||||
|
||||
**`submit()` method:**
|
||||
- Add validation rule: `'items.*.is_vatable' => ['nullable', 'boolean']`
|
||||
- When creating each `SupplierQuoteItem`, set `is_vatable` from submitted checkbox value
|
||||
- Calculate VAT: sum of `(unit_price × quantity)` for vatable items × `vatRate / 100`
|
||||
- Store `total_amount` on `SupplierQuote` as the VAT-inclusive grand total
|
||||
|
||||
### View: `resources/views/rfq/show.blade.php`
|
||||
|
||||
**Table header** — add "VAT?" column:
|
||||
```
|
||||
# | Description | Qty | Unit | Unit Price (BD) | VAT? | Total (BD)
|
||||
```
|
||||
|
||||
**Each item row** — add checkbox cell:
|
||||
```html
|
||||
<input type="checkbox" name="items[N][is_vatable]" value="1"
|
||||
onchange="calcRow(N, qty)" style="accent-color:#2563eb;width:16px;height:16px;">
|
||||
```
|
||||
|
||||
**Footer** — replace single "Grand Total" row with three rows:
|
||||
```
|
||||
Subtotal (before VAT): BD XXX.XXX
|
||||
VAT (10%): BD XXX.XXX ← hidden when vatRate is 0
|
||||
Grand Total: BD XXX.XXX
|
||||
```
|
||||
|
||||
**JavaScript `calcRow()` update:**
|
||||
- Each row calculates its own total (unit_price × qty), unchanged
|
||||
- `recalcTotals()` called after every row change:
|
||||
- Sums all row totals → subtotal
|
||||
- Sums row totals for checked (vatable) rows × `vatRate/100` → vatAmount
|
||||
- grand = subtotal + vatAmount
|
||||
- Updates subtotal, VAT, and grand total display elements
|
||||
- VAT rate injected as a JS variable: `var _vatRate = {{ $vatRate }};`
|
||||
- VAT row is hidden when `_vatRate === 0`
|
||||
|
||||
### Mobile stacked layout
|
||||
On mobile (≤640px), the table renders as cards. The VAT checkbox appears as a labelled row inside each card.
|
||||
|
||||
---
|
||||
|
||||
## 3. Data Flow Summary
|
||||
|
||||
```
|
||||
Admin sets vat_rate = 10 in settings/vat
|
||||
↓
|
||||
RfqPortalController::show() reads vat_rate, passes to view
|
||||
↓
|
||||
Supplier ticks VAT on items 1 and 3
|
||||
↓
|
||||
JS: subtotal = sum(all items), vat = sum(vatable items) × 0.10, grand = subtotal + vat
|
||||
↓
|
||||
Supplier submits form
|
||||
↓
|
||||
RfqPortalController::submit():
|
||||
- stores is_vatable per SupplierQuoteItem
|
||||
- total_amount on SupplierQuote = subtotal + vat (VAT-inclusive)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Files Touched
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `routes/web.php` | Add `GET/POST settings/vat` routes |
|
||||
| `app/Http/Controllers/Settings/VatSettingController.php` | New controller |
|
||||
| `resources/views/settings/vat.blade.php` | New view |
|
||||
| `resources/views/layouts/app.blade.php` | Add sidebar link |
|
||||
| `database/migrations/..._add_is_vatable_to_supplier_quote_items.php` | New migration |
|
||||
| `app/Models/SupplierQuoteItem.php` | Add `is_vatable` to `$fillable` |
|
||||
| `app/Http/Controllers/Purchase/RfqPortalController.php` | Load VAT rate, store `is_vatable`, calc VAT total |
|
||||
| `resources/views/rfq/show.blade.php` | Add checkbox column, VAT footer breakdown, JS update |
|
||||
|
||||
---
|
||||
|
||||
## 5. Out of Scope
|
||||
|
||||
- Showing VAT breakdown on the internal quote comparison view (can be added later)
|
||||
- Per-supplier or per-item VAT rates (single global rate only)
|
||||
- VAT registration numbers
|
||||
Loading…
x
Reference in New Issue
Block a user