# ECStores Expense Tracking — Design Spec

## Goal

Give Pro-tier merchants a way to log and review business expenses — both general operating costs and cost-of-goods items — for better decision-making and tax preparation (HST/GST input credit tracking).

## Context

- **Platform:** Laravel 13, Livewire 4.3, Filament 5.6, stancl/tenancy 3.x
- **Plan gate:** `has_expense_tracker` on the `Plan` model; `PlanService::hasExpenseTracker()` already wired
- **Tenancy:** All new tables are tenant-scoped (migrations under `database/migrations/tenant/`)
- **Existing pattern:** Follows the same nav-badge + persistent-notification gate pattern as Coupons

---

## Data Layer

### Table: `expense_categories`

| Column | Type | Notes |
|---|---|---|
| `id` | bigint PK | |
| `name` | varchar(100) | e.g. "Cost of Goods Sold" |
| `is_default` | boolean | Seeded categories flagged true; user-created false |
| `sort_order` | unsignedInt | Controls display order |
| `timestamps` | | |

### Table: `expenses`

| Column | Type | Notes |
|---|---|---|
| `id` | bigint PK | |
| `expense_category_id` | FK → expense_categories | nullable (category soft-decoupled; if deleted, FK set null) |
| `description` | varchar(255) | What was purchased |
| `amount` | decimal(10,2) | Pre-tax amount, required |
| `tax_amount` | decimal(10,2) | HST/GST paid, default 0 |
| `expense_date` | date | When the expense occurred |
| `receipt_reference` | varchar(100) | Optional receipt/invoice number |
| `notes` | text | Optional free-text |
| `timestamps` | | |

### Default Categories (seeded per tenant)

| Sort | Name |
|---|---|
| 1 | Cost of Goods Sold |
| 2 | Shipping Supplies & Packaging |
| 3 | Advertising & Marketing |
| 4 | Platform & Software Fees |
| 5 | Shipping & Postage |
| 6 | Office & Supplies |
| 7 | Professional Services |
| 8 | Other |

Categories are seeded via a `TenantExpenseCategorySeeder`. It runs manually on existing tenants at deploy time. Wiring it into the new-tenant provisioning flow is left to the implementation plan based on the current provisioning setup. `is_default = true` is set on all seeded rows.

---

## Models

### `ExpenseCategory`

```
$fillable: name, sort_order, is_default
$casts: is_default → boolean, sort_order → integer
hasMany: expenses()
```

### `Expense`

```
$fillable: expense_category_id, description, amount, tax_amount,
           expense_date, receipt_reference, notes
$casts: amount → decimal:2, tax_amount → decimal:2, expense_date → date
belongsTo: category() → ExpenseCategory
accessor: total() → amount + tax_amount
```

---

## Filament Resources

Both resources are nested under a new **"Expenses"** navigation group.

### `ExpenseCategoryResource` (nav sort: 1)

**Table columns:** name, sort_order, is_default badge, expense count (via withCount).

**Form fields:**
- `name` — TextInput, required, maxLength 100
- `sort_order` — TextInput (integer), default 0

`is_default` is display-only (shown as a badge in the table; not editable in the form).

**`canDelete()` check:** blocked if the category has any associated expenses. A Filament notification explains why.

### `ExpenseResource` (nav sort: 2)

**Table columns:** `expense_date` (sortable, default sort desc), category name, description (searchable), amount, tax_amount, total (computed: amount + tax). Includes a **global search** on `description`.

**Form fields:**
- `expense_date` — DatePicker, default today, required
- `expense_category_id` — Select, options from `ExpenseCategory::orderBy('sort_order')`, with `createOptionForm` for inline category creation, required
- `description` — TextInput, required, maxLength 255
- `amount` — TextInput numeric, prefix `$`, minValue 0, required
- `tax_amount` — TextInput numeric, prefix `$`, minValue 0, default 0, helperText "HST/GST paid on this expense"
- `receipt_reference` — TextInput, optional, maxLength 100
- `notes` — Textarea, optional

**Table filters:** DatePicker range filter (from/to) on `expense_date`, Select filter on `expense_category_id`.

### Plan Gate (both resources)

Follows the Coupons gate pattern:

- Nav badge: `'Pro'` in `'warning'` color when `!hasExpenseTracker()`
- `canCreate()` returns false when plan not eligible
- `ListExpenses::mount()` sends a persistent warning notification when locked:
  > "Expense tracking is available on the Pro plan. Contact us to upgrade."

---

## Expense Reports Page

**Class:** `App\Filament\Pages\ExpenseReports`
**View:** `resources/views/filament/pages/expense-reports.blade.php`
**Navigation group:** Reports (sort: 2, after Financial Reports)
**Plan gate:** `hasExpenseTracker()` — redirect to dashboard with warning notification if accessed on a lower tier.

### Date Range Controls

- From / To date inputs + Apply button
- Quick presets: This Month, Last Month, Q1, Q2, Q3, Q4, This Year, Last Year
- Defaults: current year start → today on `mount()`

### Stat Cards (3)

| Card | Value |
|---|---|
| Total Expenses | Sum of `amount` in range |
| Total Tax Paid | Sum of `tax_amount` in range (input credits) |
| Total Spent | Sum of `amount + tax_amount` in range |

### Monthly Breakdown Table

Expenses grouped by month within the selected range.

| Month | Entries | Pre-tax Total | Tax Paid | Grand Total |
|---|---|---|---|---|
| January 2026 | 4 | $320.00 | $41.60 | $361.60 |
| … | | | | |

### Category Breakdown Table

| Category | Entries | Pre-tax | Tax | Total |
|---|---|---|---|---|
| Cost of Goods Sold | 12 | $1,200.00 | $156.00 | $1,356.00 |
| … | | | | |

Sorted by total desc.

### CSV Export

Single "Export CSV" button. Downloads all expenses in the selected date range with columns:

`Date, Category, Description, Amount (pre-tax), Tax, Total, Receipt Reference, Notes`

---

## Deployment

1. Run tenant migration (adds `expense_categories` and `expenses` tables to all tenant DBs)
2. Run `TenantExpenseCategorySeeder` on existing tenants to seed the 8 default categories
3. Clear caches
4. Verify in tenant admin: Expenses nav group appears for Pro tenants; locked with badge for lower tiers

---

## Future Considerations (out of scope)

- Receipt file uploads
- Recurring expense templates
- Expense approval workflows
- Integration with Financial Reports net-profit calculation
