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