> ## Documentation Index
> Fetch the complete documentation index at: https://docs.taho.com.vn/llms.txt
> Use this file to discover all available pages before exploring further.

# Route handlers

> Server handler patterns, guards và response boundaries hiện tại.

Route handler là ranh giới tin cậy giữa browser hoặc provider bên ngoài với application services. Source có 28 handler files; [Route catalog](https://docs.taho.com.vn/reference/route-catalog.md) liệt kê đầy đủ URL và method.

## Trình tự kiểm tra một admin mutation

**1. Kiểm tra nguồn request**

Handler có mutation kiểm tra same-origin khi gọi `isSameOriginRequest()`.

**2. Xác thực phiên**

`getAuthenticatedUserFromRequest()` trả user cùng active tower và permission set.

**3. Kiểm tra quyền**

Handler kiểm tra route access, sau đó kiểm tra CRUD hoặc specialized action tùy intent.

**4. Giới hạn và parse input**

Upload, reminder và webhook dùng giới hạn body riêng. Handler chỉ chuyển input đã parse sang service.

**5. Gọi persistence path**

Generic module dùng repository; workflow phức tạp dùng domain service và transaction riêng.

## Chọn handler khi phát triển

| Loại thay đổi | Entry point | Không dùng khi |
| - | - | - |
| CRUD danh mục | `POST /admin/mutate` | Module bị policy hoặc handler chặn |
| Bảng kê/phiếu thu/điều chỉnh | `POST /admin/workflow` | Không có financial intent tương ứng |
| Cư dân–căn hộ | `/admin/resident-memberships` | Chỉ sửa field hồ sơ cư dân hiện có |
| Feedback/yêu cầu | `/admin/resident-workflow` | Module không phải feedback/service request |
| Phương tiện | `/admin/apartment-vehicles` | Generic vehicle mutation |
| RBAC | `/admin/rbac` | Thay đổi user field không liên quan linked account |
| Import | `/admin/import` | Export hoặc download template |
| Reminder | `/admin/debt-reminders` | Gửi email trực tiếp; route này chỉ queue |

## Hợp đồng request cho tính công nợ và lập phiếu thu

`POST /admin/workflow` nhận dữ liệu biểu mẫu qua `request.formData()`. Hai intent dưới đây dùng cùng handler; `intent` chọn thao tác, còn `returnTo` phải trỏ đến màn hình được phép cho thao tác đó. Tòa nhà và người thực hiện được lấy từ phiên qua `activeTowerId` và `email`.

> [tab] Tính công nợ
>
> | Trường | Cách handler và service sử dụng |
> | - | - |
> | `intent` | Giá trị `calculate-billing` |
> | `returnTo` | Màn hình `/admin/electric-meter` |
> | `period` | Kỳ được kiểm tra bởi `requirePeriod()` |
> | `dueDate` | Ngày hạn thanh toán được kiểm tra bởi `requireDate()` |
> | `apartmentCodes` | Không bắt buộc; tách bằng khoảng trắng, dấu phẩy hoặc chấm phẩy; rỗng nghĩa là tất cả căn hộ đang hoạt động trong tòa nhà |
>
> `calculateBillingPeriod()` tạo `Bills`, `BillLines` và `AuditLogs` trong một Prisma transaction. Response thành công chứa `data.notice = "billing-calculated"` và `data.count` là số bảng kê đã tạo.

> [tab] Lập phiếu thu
>
> | Trường | Cách handler và service sử dụng |
> | - | - |
> | `intent` | Giá trị `create-receipt` |
> | `returnTo` | Một trong `/admin/v2/bill/list-pay`, `/admin/v2/bill/x-wait-for-confirm`, `/admin/v2/receipt` |
> | `apartment` | Căn hộ đang hoạt động trong tòa nhà của phiên |
> | `amount` | Số tiền dương, bằng tổng phân bổ cộng tiền thừa |
> | `method` | Hình thức được `normalizeMethod()` kiểm tra trong tòa nhà |
> | `accountingDate` | Ngày xác định kỳ hạch toán cần kiểm tra mở |
> | `payer`, `description` | Chuỗi bắt buộc, không được rỗng |
> | `address` | Chuỗi được chuẩn hóa, không bắt buộc |
> | `allocationLines` | Trường biểu mẫu chứa chuỗi JSON của danh sách `{ billLineId, amount }`; phải có trường này, nhưng danh sách có thể rỗng khi thu vào tiền thừa |
> | `excessAmount` | Số không âm; mặc định 0 khi không truyền |
> | `afterSubmit` | Giá trị `print` chuyển đích trả về sang phần chứng từ của phiếu vừa tạo |
>
> `createReceiptWithAllocation()` ghi phiếu, phân bổ nếu có, cập nhật công nợ tương ứng, ghi sổ và audit log trong một transaction. Response thành công chứa `data.notice = "receipt-posted"`, `data.receipt` là mã phiếu và `data.excess` là tiền thừa.

### Quyền, response và lỗi

Handler kiểm tra nguồn request, phiên, màn hình nằm trong danh sách cho phép, quyền truy cập màn hình và quyền thao tác trước khi gọi service. Quyền thao tác là `calculate_billing` hoặc `create_receipt` tương ứng.

| Trường hợp | HTTP và cấu trúc response |
| - | - |
| Thành công | 200; `{ success: true, redirectTo, data }` |
| Nguồn request không hợp lệ | 403; `error.code` là `forbidden-origin` |
| Chưa xác thực | 401; `error.code` là `unauthorized`, đích đăng nhập nằm trong `error.details.redirectTo` |
| Màn hình hoặc quyền không hợp lệ | 403; `error.code` là `forbidden` |
| Lỗi nghiệp vụ không được ánh xạ thành lỗi database | 400; `error.code` chứa thông báo lỗi, `error.message` là `Financial workflow request failed` |

`jsonRedirect()` trả JSON chứa đích điều hướng, không phải HTTP 302. Lỗi database đi qua `mapDatabaseError()` và dùng mã/status từ bộ ánh xạ đó. Không áp dụng bảng response này cho mọi handler khác.

### Ví dụ request và response mẫu

```bash theme={null}
# Gửi yêu cầu tính công nợ kỳ 2026-07
curl -X POST "<APP_URL>/admin/workflow" \
  -H "Origin: <APP_URL>" \
  -H "Cookie: taho_session=<SESSION_COOKIE>" \
  -F "intent=calculate-billing" \
  -F "returnTo=/admin/electric-meter" \
  -F "period=2026-07" \
  -F "dueDate=2026-08-15"
```

Response khi thành công (HTTP 200):

```json theme={null}
{
  "success": true,
  "redirectTo": "<APP_URL>/admin/electric-meter?notice=billing-calculated&count=2",
  "data": {
    "notice": "billing-calculated",
    "count": 2
  }
}
```

Response khi phiên đăng nhập hết hạn hoặc chưa đăng nhập (HTTP 401):

```json theme={null}
{
  "success": false,
  "error": {
    "code": "unauthorized",
    "message": "Authentication is required",
    "details": {
      "redirectTo": "<APP_URL>/login"
    }
  }
}
```

Response khi không đủ quyền thao tác trên route (HTTP 403):

```json theme={null}
{
  "success": false,
  "error": {
    "code": "forbidden",
    "message": "Financial workflow request failed",
    "details": {
      "redirectTo": "<APP_URL>/admin?error=forbidden"
    }
  }
}
```

Xem hướng dẫn [Tính công nợ](https://docs.taho.com.vn/finance/calculate-billing.md), [Lập phiếu thu](https://docs.taho.com.vn/finance/create-receipt.md) và [Use case và workflow](https://docs.taho.com.vn/product-specs/workflow-specifications.md) để đối chiếu hành vi với màn hình.

## Response behavior

Các route dùng ba kiểu response:

* JSON success/error qua `api-response.ts`;
* redirect kèm `notice` hoặc `error` cho form navigation;
* raw file/stream response cho export và attachment.

Không giả định mọi handler trả cùng schema. Not confirmed by current source code: một middleware authorization chung thay thế các checks trong handler.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.