# Tenant Preferences API

Tenant preferences are a JSON object stored per service provider (tenant). They control display labels, units, currency formatting, and billing behaviour across the app.

All responses follow the shape `{ status, message, data }`.

---

## Where preferences come from

Preferences are returned on every successful **login response** inside the `data` object. Store them in your app state / local storage on login and treat them as the source of truth for rendering.

```json
{
  "status": true,
  "message": "Login successful",
  "data": {
    "id": 12,
    "name": "Jane Admin",
    "role_id": 2,
    "service_provider_id": 5,
    "preferences": {
      "water_label":       "CWA",
      "electricity_label": "CEB",
      "water_unit":        "m3",
      "electricity_unit":  "KWH",
      "gas_unit":          "m3",
      "currency":          "Rs.",
      "currency_position": "before",
      "date_format":       "d M Y",
      "wma_volume_ratio":  1.0,
      "signatory_name":    "The Syndic",
      "email_languages":   ["en", "fr"]
    }
  }
}
```

---

## Preference reference

| Key | Type | Default | Description |
|---|---|---|---|
| `water_label` | `string` | `CWA` | Display name for the water utility (e.g. "CWA", "LWSC") |
| `electricity_label` | `string` | `CEB` | Display name for the electricity utility (e.g. "CEB", "KPLC") |
| `water_unit` | `string` | `m3` | Meter reading unit for water. Use `m3` to render as m³ |
| `electricity_unit` | `string` | `KWH` | Meter reading unit for electricity |
| `gas_unit` | `string` | `m3` | Meter reading unit for gas (native meter, not billing unit) |
| `currency` | `string` | `Rs.` | Currency symbol or code (e.g. `Rs.`, `$`, `€`, `KES`) |
| `currency_position` | `string` | `before` | `before` → `Rs. 1,200.00` / `after` → `1,200.00 Rs.` |
| `date_format` | `string` | `d M Y` | PHP `date()` format string. Apply equivalent formatting on the frontend |
| `wma_volume_ratio` | `float` | `1.0` | Fraction of CWA volume used to calculate WMA charge. Backend-only — no frontend rendering required |
| `signatory_name` | `string` | `The Syndic` | Name that appears as the signatory on bill emails (e.g. "The Management", "Le Syndic") |
| `email_languages` | `string[]` | `["en","fr"]` | Ordered list of language codes for bill emails. Each code maps to a blade partial (`billrate_en`, `billrate_fr`, `billrate_es`). All sections are included in one email, separated by a divider |

> **Gas billing note:** `gas_unit` applies to meter *readings* only (previous unit / current unit display). Billed consumption for gas is always in **KG** (post litre-to-kg conversion) — use `KG` hardcoded wherever you display `units_consumed` or rate-per-unit for gas.

---

## Frontend formatting helpers

### Currency

```dart
// Dart / Flutter
String formatMoney(double value, Map<String, dynamic> prefs) {
  final currency = prefs['currency'] ?? 'Rs.';
  final position = prefs['currency_position'] ?? 'before';
  final formatted = value.toStringAsFixed(2)
      .replaceAllMapped(RegExp(r'(\d)(?=(\d{3})+\.)'), (m) => '${m[1]},');
  return position == 'after' ? '$formatted $currency' : '$currency $formatted';
}
```

```js
// JavaScript / Vue
function formatMoney(value, prefs) {
  const currency = prefs.currency ?? 'Rs.';
  const position = prefs.currency_position ?? 'before';
  const formatted = Number(value).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  return position === 'after' ? `${formatted} ${currency}` : `${currency} ${formatted}`;
}
```

### Unit labels

```dart
// Dart / Flutter
String unitLabel(int serviceTypeId, Map<String, dynamic> prefs) {
  switch (serviceTypeId) {
    case 1: return prefs['water_unit'] ?? 'm3';       // water
    case 2: return prefs['electricity_unit'] ?? 'KWH'; // electricity
    case 3: return prefs['gas_unit'] ?? 'm3';          // gas (meter reading only)
    default: return 'units';
  }
}

// For displaying units_consumed or per-unit rate on gas invoices, use 'KG' directly.
String billingUnitLabel(int serviceTypeId, Map<String, dynamic> prefs) {
  if (serviceTypeId == 3) return 'KG';
  return unitLabel(serviceTypeId, prefs);
}
```

```js
// JavaScript / Vue
function unitLabel(serviceTypeId, prefs) {
  if (serviceTypeId === 1) return prefs.water_unit ?? 'm3';
  if (serviceTypeId === 2) return prefs.electricity_unit ?? 'KWH';
  if (serviceTypeId === 3) return prefs.gas_unit ?? 'm3';
  return 'units';
}

function billingUnitLabel(serviceTypeId, prefs) {
  return serviceTypeId === 3 ? 'KG' : unitLabel(serviceTypeId, prefs);
}
```

### Utility labels

```dart
String utilityLabel(int serviceTypeId, Map<String, dynamic> prefs) {
  switch (serviceTypeId) {
    case 1: return prefs['water_label'] ?? 'CWA';
    case 2: return prefs['electricity_label'] ?? 'CEB';
    case 3: return 'Gas';
    default: return 'Utility';
  }
}
```

### Dates

`date_format` uses PHP `date()` tokens. Map to your platform's equivalent:

| PHP token | Meaning | Dart (`intl`) | JS (`date-fns` / `dayjs`) |
|---|---|---|---|
| `d` | Day, 2-digit | `dd` | `dd` |
| `M` | Month, short name | `MMM` | `MMM` |
| `Y` | Year, 4-digit | `yyyy` | `yyyy` |
| `m` | Month, 2-digit | `MM` | `MM` |
| `j` | Day, no leading zero | `d` | `d` |

Default `d M Y` → `06 Jun 2026`

```dart
// Dart — using the intl package
import 'package:intl/intl.dart';

String formatDate(String dateString, Map<String, dynamic> prefs) {
  final phpFormat = prefs['date_format'] ?? 'd M Y';
  final dartFormat = phpFormatToDart(phpFormat); // see mapping above
  return DateFormat(dartFormat).format(DateTime.parse(dateString));
}

String phpFormatToDart(String phpFormat) {
  return phpFormat
      .replaceAll('d', 'dd')
      .replaceAll('M', 'MMM')
      .replaceAll('Y', 'yyyy')
      .replaceAll('m', 'MM')
      .replaceAll('j', 'd');
}
```

```js
// JavaScript — using dayjs
import dayjs from 'dayjs';

function phpFormatToDayjs(phpFormat) {
  return phpFormat
    .replace(/\bd\b/g, 'DD')
    .replace(/\bM\b/g, 'MMM')
    .replace(/\bY\b/g, 'YYYY')
    .replace(/\bm\b/g, 'MM')
    .replace(/\bj\b/g, 'D');
}

function formatDate(dateString, prefs) {
  const fmt = phpFormatToDayjs(prefs.date_format ?? 'd M Y');
  return dayjs(dateString).format(fmt);
}
```

---

## Updating preferences

Send the **complete** preferences object. The backend stores it as-is — it does not merge with defaults server-side. Read the current preferences from the login response, modify the keys you need, and PUT the full object back.

```
PUT /api/service-provider
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "service_provider_id": 5,
  "name": "Acme Properties",
  "preferences": {
    "water_label":       "LWSC",
    "electricity_label": "ZESCO",
    "water_unit":        "m3",
    "electricity_unit":  "KWH",
    "gas_unit":          "m3",
    "currency":          "ZMW",
    "currency_position": "before",
    "date_format":       "d M Y",
    "wma_volume_ratio":  0.85,
    "signatory_name":    "The Syndic",
    "email_languages":   ["en", "fr"]
  }
}
```

> **Important:** Always send all preference keys. Sending a partial object will overwrite stored preferences with only what you send — missing keys will fall back to defaults on the next read via `getPreferences()`, but they will not be persisted.

#### Response `200`

```json
{
  "status": true,
  "message": "Service provider updated",
  "data": { ... }
}
```

---

## Invoice / bill field mapping

These fields appear on `archived_sessions` rows (bill history). Use the preferences to render them correctly:

| Field | Render with |
|---|---|
| `bill_rate` | `formatMoney(bill_rate, prefs)` |
| `wma_rate` | `formatMoney(wma_rate, prefs)` |
| `load_rate` | `formatMoney(load_rate, prefs)` |
| `net_bill_value` | `formatMoney(net_bill_value, prefs)` — pre-tax subtotal |
| `tax_value` | `formatMoney(tax_value, prefs)` — append `tax_label` as description |
| `tax_label` | Use as the line-item label for the tax row |
| `tax_percentage` | Display as `tax_label (tax_percentage%)` |
| `per_unit_rate` | `formatMoney(per_unit_rate, prefs)` + `billingUnitLabel(service_type_id, prefs)` |
| `units_consumed` | `number + billingUnitLabel(service_type_id, prefs)` |
| `previous_unit` | `number + unitLabel(service_type_id, prefs)` |
| `current_unit` | `number + unitLabel(service_type_id, prefs)` |
| `survey_end_date` | `formatDate(survey_end_date, prefs)` |
| `due_date` | `formatDate(due_date, prefs)` |

**Total amount due** = `net_bill_value + tax_value`

---

## Tax configuration

Tax is configured **per site-service** (not per tenant preference). Each service on a site can have its own tax label and rate.

### Fields

| Field | Type | Description |
|---|---|---|
| `tax_label` | `string\|null` | Display name for the tax line item, e.g. `VAT`, `GST`, `TVA`. `null` means no tax applies |
| `tax_percentage` | `float` | Tax rate as a percentage of `net_bill_value`, e.g. `15.0` for 15%. `0` means no tax |

`tax_value` on a bill is computed as:

```
tax_value = round(net_bill_value × tax_percentage / 100, 2)
```

`net_bill_value` already includes `bill_rate + wma_rate + service_charge + meter_rental` — tax is applied on the full pre-tax subtotal.

### Updating tax on a site service

Tax is set as part of the full site edit — there is no standalone site-service endpoint. Use:

```
PUT /api/site
Authorization: Bearer <token>
Content-Type: application/json
```

Include `tax_label` and `tax_percentage` inside the relevant service block (`water`, `electricity`, or `gas`). All service blocks must be sent in full:

```json
{
  "id":       16,
  "name":     "Block A",
  "fullname": "Block A Residences",
  "water": {
    "status":                 true,
    "id":                     3,
    "description":            "CWA metered supply",
    "cwa_rate_management_id": 1,
    "wma_rate_management_id": 2,
    "service_charge":         50,
    "meter_rental":           20,
    "payment_terms":          30,
    "custom_message":         null,
    "tax_label":              "VAT",
    "tax_percentage":         15.0
  },
  "electricity": { "status": false },
  "gas":         { "status": false }
}
```

To remove tax from a service, send `"tax_label": null, "tax_percentage": 0` in that service block.

### Rendering tax on the frontend

```dart
// Dart / Flutter
Widget taxRow(Map bill, Map prefs) {
  final pct = (bill['tax_percentage'] as num?)?.toDouble() ?? 0.0;
  if (pct == 0) return SizedBox.shrink();
  final label = bill['tax_label'] ?? 'Tax';
  return Row(children: [
    Text('$label (${pct.toStringAsFixed(pct % 1 == 0 ? 0 : 2)}%)'),
    Text(formatMoney((bill['tax_value'] as num).toDouble(), prefs)),
  ]);
}
```

```js
// JavaScript / Vue
function taxLineItem(bill, prefs) {
  const pct = Number(bill.tax_percentage ?? 0);
  if (pct === 0) return null;
  const label = bill.tax_label ?? 'Tax';
  return {
    label: `${label} (${pct % 1 === 0 ? pct : pct.toFixed(2)}%)`,
    value: formatMoney(Number(bill.tax_value ?? 0), prefs),
  };
}
```
