# F-072 — Architect: Dynamic VAT tax rates in product edit prices tab
## Current state
`PricingSection.tsx` (admin) has **hardcoded** VAT logic:
```ts
const VAT_GENERAL = 1.21; // hardcoded constant
const VAT_REDUCED = 1.10; // hardcoded constant
// calcGross: gross = net * VAT_GENERAL/VAT_REDUCED
```
The dropdown has **two hardcoded options**:
```tsx
```
But the DB `tax_rates` table already has **3 active rates**:
- IVA Superreducido: 4% (applies_to: `super-reduced`)
- IVA Reducido: 10% (applies_to: `reduced`)
- IVA General: 21% (applies_to: `general`)
The 4% rate is **missing** from the dropdown and calculation.
## Goal
Replace hardcoded VAT constants and dropdown with **dynamic** fetch of active tax rates from `GET /admin/tax-rates`. Also add `super-reduced` support to the backend so the 4% rate can be used.
## Scope IN
- `project/src/modules/pricing/domain/price.ts` — add `super-reduced` to `VatRate` type and `VAT_BASIS_POINTS`
- `project/src/modules/pricing/api/pricing.routes.ts` — accept 'super-reduced' in `vatRateSchema`
- `project/migrations/031_vat_super_reduced.js` — update CHECK constraints on `pricing_variant_prices.vat_rate` and `pricing_price_history` columns to include 'super-reduced'
- `project/apps/admin/src/features/products/components/sections/PricingSection.tsx` — fetch active tax rates from `GET /admin/tax-rates`, populate dropdown dynamically, use fetched rate percentages for calculations
- `project/apps/admin/src/lib/api-client.ts` — extend `VariantPrice.vatRate` type to include `super-reduced`
## Scope OUT
- Tax rates admin page (`/tax-rates`) — no changes needed, already supports activate/deactivate
- Backend pricing service — already generic, only needs domain type update
- Storefront/frontend — no changes
## Design
### Backend: add super-reduced
`price.ts`:
```ts
export type VatRate = 'general' | 'reduced' | 'super-reduced';
export const VAT_BASIS_POINTS: Record = {
general: 2100, // 21%
reduced: 1000, // 10%
'super-reduced': 400, // 4%
};
```
`pricing.routes.ts` — `vatRateSchema`:
```ts
const vatRateSchema = z.enum(['general', 'reduced', 'super-reduced']);
```
### Migration 031
Update CHECK constraints to:
```sql
CHECK (vat_rate IN ('general', 'reduced', 'super-reduced'))
```
### PricingSection.tsx
1. Add `TaxRate[]` state for active tax rates
2. `useEffect` on mount: fetch `taxApi.list()`, filter `active === true`
3. Replace `VAT_GENERAL/VAT_REDUCED` constants with dynamic lookup:
```ts
const rate = activeRates.find(r => r.appliesTo === vatRate[variantId]);
const multiplier = 1 + (rate?.ratePercent ?? 21) / 100;
const grossCents = Math.round(netCents * multiplier);
```
4. Dropdown maps `appliesTo` → option value, displays `name + ratePercent%`:
```tsx
{activeRates.map(r => (
))}
```
5. `setVatRate` stores `appliesTo` key (e.g. `'super-reduced'`) as the vat rate value
6. Default: use first active rate if none selected
### Admin api-client
Extend `VariantPrice.vatRate` type to include `'super-reduced'`:
```ts
vatRate: 'general' | 'reduced' | 'super-reduced';
```
## Risk
- **Low risk**: adding a new allowed value to an existing enum + updating a CHECK constraint; no data migration needed (existing data already uses 'general' or 'reduced')
- **Risk mitigation**: migration is additive (adds 'super-reduced' to allowed values, doesn't remove any); rollback simply reverts constraint
## Verification
- `npx tsc --noEmit` on all projects
- `npx eslint` on changed files
- Apply migration 031 to dev DB
- Backend rebuilt and restarted
- API test: `PATCH /pricing/variants/:id` with `vatRate: 'super-reduced'` → HTTP 200
- Admin product edit → pricing tab → dropdown shows all 3 active rates
- `./scripts/verify.sh` green