# Debts — Implementation Tasks

> Produced by the Reversa **Writer** (phase: generation) · doc_level: `complete`
> Generated on 2026-09-21

**Confidence scale:** 🟢 CONFIRMED · 🟡 INFERRED · 🔴 GAP

## Prerequisites

- [ ] `Customer` model available with `debt_total` (float cast) and `SoftDeletes`, maintained by the `customers-debt-actions` write path (`CustomerDebt::record`). 🟢 (`app/Models/Customer.php:6,12,15-18`)
- [ ] The `customers-scan` endpoint (`GET /customers/scan`) is available for the modal customer pickers. 🟢 (`customers-scan`)
- [ ] The `customers-debt-actions` write routes exist: `POST /customers/{id}/repayments`, `POST /customers/{id}/debts`, and the ledger page `GET /customers/{id}/debt`. 🟢 (`routes/web.php:58,60,61`)
- [ ] Admin route group with `['web','admin']` middleware and CSRF protection is in place. 🟢 (`routes/web.php:23-27`)

## Tasks

> Each task references the legacy file the behaviour was extracted from.

- [ ] T-01, Register `GET /debts` (`debts.index`) in the admin route group, mapped to `DebtController@index`.
  - Legacy origin: `routes/web.php:67`
  - Done when: an authenticated admin gets `200` at `/debts`; an anonymous request is redirected `302` to `auth/login`.
  - Confidence: 🟢

- [ ] T-02, Implement `DebtController::index`: set header `Danh sách công nợ` and a single-item breadcrumb.
  - Legacy origin: `app/Http/Controllers/DebtController.php:12-15`
  - Done when: the rendered page title/breadcrumb reads the localized "accounts-receivable list" label.
  - Confidence: 🟢

- [ ] T-03, Build the base debtor query `Customer::where('debt_total','>',0)->orderBy('debt_total','desc')`.
  - Legacy origin: `DebtController.php:17-18`
  - Done when: only customers with `debt_total > 0` are returned, largest balance first; soft-deleted customers are excluded automatically.
  - Confidence: 🟢

- [ ] T-04, Apply the optional `q` filter as a **grouped** closure: `where(fn($query) => $query->where('phone','like',"%$q%")->orWhere('fullname','like','%'.$q.'%'))`, only when `$request->get('q')` is truthy.
  - Legacy origin: `DebtController.php:20-24`
  - Done when: `?q=…` narrows to phone-or-name matches while the `debt_total > 0` scope still holds (a zero-balance name match must NOT appear); absence of `q` returns the full debtor list.
  - Confidence: 🟢

- [ ] T-05, Paginate 30 per page (`->paginate(30)`) and render `pages.debts` with `compact('customers')`.
  - Legacy origin: `DebtController.php:26-28`
  - Done when: 35 debtors yield 30 rows on page 1 and 5 on page 2; pagination links preserve `?q=`.
  - Confidence: 🟢

- [ ] T-06, Render the debtor table: columns fullname / phone / `number_format(debt_total,1) ₫`; each row navigates to `route('customers.debt', [id])`; empty state shows "Không có dữ liệu".
  - Legacy origin: `resources/views/pages/debts.blade.php`
  - Done when: rows display the formatted balance and click through to the customer's debt ledger; an empty list shows the no-data row.
  - Confidence: 🟢

- [ ] T-07, Render the Thu nợ (repayment) and Ghi nợ tay (manual-debt) modals with select2 pickers hitting `/customers/scan` (min 3 chars) that set each form's `action` to `/customers/{id}/repayments` and `/customers/{id}/debts` respectively, with CSRF tokens.
  - Legacy origin: `resources/views/pages/debts.blade.php`
  - Done when: selecting a customer enables the submit button, shows their current balance, and POSTs to the correct `customers-debt-actions` route; this unit performs no server-side write.
  - Confidence: 🟢

- [ ] T-08, (Improvement, not in legacy) Add observability — log/metric for the receivables screen (e.g. debtor count, query latency).
  - Legacy origin: `DebtController.php:10-29` (absence)
  - Done when: each request emits a structured log or metric.
  - Confidence: 🔴

## Test Tasks

- [ ] TT-01, Happy path: seed customers with balances 0, 50, 120 → `GET /debts` returns 200 and lists only the 50 and 120 rows, 120 first (see `requirements.md`, Acceptance Criteria).
- [ ] TT-02, Search scoping: a customer with `debt_total = 0` whose name matches `q` must NOT appear (grouped-closure regression guard).
- [ ] TT-03, Empty state: with no positive-balance customers, the page renders the "Không có dữ liệu" row without error.
- [ ] TT-04, Pagination: 35 debtors → 30 on page 1, 5 on page 2; `?q=` preserved across page links.
- [ ] TT-05, Auth: anonymous `GET /debts` → `302` to `auth/login`.
- [ ] TT-06, Delegation: submitting the repayment modal POSTs to `/customers/{id}/repayments` (owned by `customers-debt-actions`), not to any `DebtController` action.

## Data Migration Tasks (if applicable)

- None. The unit introduces no tables; it is a read-only projection over `customers.debt_total` and links to the `customer_debts` ledger. 🟢 (`data-dictionary.md:256`)

## Suggested Order

1. T-01 → T-05 build the controller and query (the functional core); T-04 depends on T-03 (same query chain).
2. T-06 → T-07 build the view; T-07 depends on `customers-scan` and `customers-debt-actions` being available.
3. T-08 (observability) is independent and can follow at any point.

## Pending Gaps (🔴)

- Observability is absent (T-08); a slow or empty receivables list produces no operational signal — decide whether telemetry is required before reimplementation.
