Files
hqland-app/AGENTS.md

454 lines
21 KiB
Markdown

# HQLAND - TRẠNG THÁI CODEBASE & LỘ TRÌNH PHÁT TRIỂN
> File này được tạo để lưu trữ ngữ cảnh dự án cho các phiên làm việc sau.
> **Cập nhật:** 29/04/2026
> **Dự án:** HQLand - Hệ thống quản lý Bất động sản
> **Stack:** Laravel 13 + Filament v5.5 (Schemas Architecture) + PostgreSQL + UUID
---
## 1. THÔNG TIN KẾT NỐI DATABASE (CRITICAL)
### Database Chính (Production Data)
- **Connection:** pgsql
- **Host:** pgsql (trong Docker), 127.0.0.1 (từ Host machine)
- **Port:** 5432
- **Database:** laravel
- **Username:** sail
- **Password:** password
- **Dữ liệu hiện có:** 120 khách hàng, 45 sản phẩm, 139 hợp đồng
### Database Thử Nghiệm
- **Database:** laravel_testing (đã tạo)
- **Cách chạy test:** `DB_HOST=127.0.0.1 ./vendor/bin/pest`
- **Cách chạy artisan:** `DB_HOST=127.0.0.1 php artisan tinker`
### Quy tắc VÀNG
- **TUYỆT ĐỐI KHÔNG** dùng `migrate:fresh` trên database chính.
- Tài khoản admin: `admin@phuongtc.com` / `1Qazxsw2@!321`
- Dữ liệu Excel đã import là tài sản quý - không xóa.
---
## 2. KIẾN TRÚC KỸ THUẬT
### Tech Stack
| Thành phần | Phiên bản / Công nghệ |
|------------|----------------------|
| Framework | Laravel 13.x |
| PHP | 8.3 |
| Admin Panel | Filament v5.5 |
| Kiến trúc UI | **Schemas Architecture** (Tách Form/Table ra khỏi Resource) |
| Database | PostgreSQL |
| Khóa chính | UUID (100% các bảng) |
| Excel | PhpSpreadsheet 5.7 |
| Testing | Pest PHP 4.6 |
### Quy chuẩn Code
1. **LUÔN** dùng `Schemas` class. **KHÔNG** định nghĩa inline trong Resource.
2. `Grid``Section` nằm trong `Filament\Schemas\Components`.
3. Khi render HTML động trong Form, dùng **Inline Styles** thay vì Tailwind class.
4. Mọi trường JSONB trong Model phải khai báo trong `$casts = ['field' => 'array']`.
5. Naming database: **snake_case** cho mọi bảng và cột.
### ⚠️ Quy tắc Layout Filament v5.5 (BÀI HỌC QUAN TRỌNG)
- **Section muốn full width** phải thêm `->columnSpanFull()` ngay sau `Section::make()`
- **Schema mặc định** có thể tự động chia cột nếu không chỉ định `columnSpanFull`
- **Grid::make(3)** chỉ dùng *bên trong* Section để chia field thành cột, KHÔNG dùng để bọc nhiều Section
- **Layout đúng:** Section xếp dọc (mỗi Section `->columnSpanFull()`), bên trong Section dùng Grid chia field
- **RichEditor tăng chiều cao:** `->extraInputAttributes(['style' => 'min-height: 500px;'])`
---
## 3. CẤU TRÚC MODULE HIỆN TẠI
### 3.1. Warehouse (Kho hàng)
**Models:** `Project`, `Product`
**Project:**
- `code`, `name`, `type`, `address`
- `payment_template_id` (relationship với PaymentTemplate)
**Product:**
- `code`, `project_id`, `product_type` (LAND | APARTMENT)
- `area`, `price_per_unit`, `total_price`
- `qsdd_value`, `foundation_temp_value`, `contract_temp_value`
- `infrastructure_status` (JSONB)
- `custom_data` (JSONB): block, building_density, legal_status_raw
- `status`
**Filament Resources:**
- `ProjectResource``ProjectForm` (Schemas)
- `ProductResource`
---
### 3.2. CRM (Khách hàng)
**Model:** `Customer`
**Cấu trúc:**
- `type`: INDIVIDUAL | COMPANY
- `full_name`, `cmnd_cccd`, `tax_code`, `title`
- `phone`, `secondary_phones` (JSONB)
- `email`, `dob`
- `permanent_address`, `contact_address` (lưu cứng, không JSON)
- `id_issue_date`, `id_issue_place`
- `representative_id` (self-referencing, cho công ty)
**Quan hệ:**
- `representedCompanies()`: Công ty mà khách hàng đại diện
- `representative()`: Ngườ đại diện của công ty
- `contracts()`: belongsToMany qua `contract_customers`
**Filament Resources:**
- `CustomerResource``CustomerForm` + `CustomersTable` (Schemas)
- Form hỗ trợ chuyển đổi INDIVIDUAL/COMPANY động (live)
- Copy địa chỉ thường trú → liên hệ (suffixAction)
---
### 3.3. Contracts (Hợp đồng & Tài chính)
**Model:** `Contract`
**Cấu trúc:**
- `contract_number`, `contract_type` (HĐMB | HĐGV | HĐDC)
- `product_id`, `status`
- `signing_date`, `sale_date`, `hql_confirmation_date`
- `land_value`, `foundation_value`, `total_value`, `total_value_with_foundation`
- `paid_amount`, `remaining_amount`, `excess_amount`
- `discount_details` (JSONB)
- `brokerage_name`, `stored_contract_count`, `filing_note`
- `transfer_order`: 0 = chủ hiện tại, 1 = F0, 2+ = F1, F2...
**Logic tự động trong Model (booted):**
- `total_value` = `land_value` + `foundation_value` (nếu có giá trị)
- Fallback: lấy từ `product.total_price` khi tạo mới
- `remaining_amount` = `total_value` - `paid_amount`
**Quan hệ:**
- `product()`, `customers()` (belongsToMany qua contract_customers)
- `paymentTemplate()` (belongsTo PaymentTemplate)
- `salesPhase()` (belongsTo SalesPhase)
- `appendices()`, `paymentSchedule()`, `scheduleItems()` (HasManyThrough)
- `payments()`, `paymentFines()`
**Accessor:**
- `final_value`: Giá trị sau chiết khấu (tính từ `DiscountEngine`)
**Filament Resources:**
- `ContractResource``ContractForm` + `ContractsTable`
- Action "Tạo lịch TT" trong Table (gọi `ContractScheduleService`)
- Form có tính toán live: land_value + foundation_value = total_value
- Hiển thị discount_details dạng grid inline style
---
### 3.4. Finance (Tài chính & Thu tiền)
**Models:** `PaymentTemplate`, `PaymentSchedule`, `PaymentScheduleItem`, `Payment`, `PaymentFine`
**PaymentTemplate:**
- `project_id`, `name`, `is_default`
- `items()`: các đợt thanh toán mẫu
**PaymentSchedule:**
- `contract_id`, `template_id`
- `items()`: các đợt thanh toán thực tế
**PaymentScheduleItem:**
- `schedule_id` (hoặc `template_id` - dùng chung bảng)
- `installment_no`, `type` (PaymentType enum), `percentage`, `amount`, `due_date`
- `days_after_signing`, `days_after_previous`
**Payment:**
- `contract_id`, `schedule_item_id`, `amount`, `paid_date`
- `method`, `receipt_number`, `metadata` (JSONB)
**PaymentObserver (TỰ ĐỘNG):**
- Khi tạo/sửa/xóa Payment:
1. Tính lại `contract.paid_amount` = SUM(payments)
2. Tính lại `remaining_amount``excess_amount`
3. Nếu có `excess_amount` > 0: **Tự động khấu trừ** vào đợt thanh toán tiếp theo chưa đủ tiền (tạo Payment auto)
**Filament Resources:**
- `PaymentResource``PaymentForm` + `PaymentsTable`
- Form chọn Contract → chọn Đợt thanh toán (cascade)
- Validation số tiền không vượt quá công nợ đợt / công nợ HĐ
- Table có filter theo phương thức và ngày thu
- Cột đối soát: Đủ / Thiếu / Thừa (tính tổng payments của đợt)
- Cột còn thiếu tiền theo đợt
- `PaymentFineResource` → Quản lý tiền phạt chậm thanh toán
- `AppendixResource` → Quản lý phụ lục hợp đồng
- `SettlementResource` → Quản lý quyết toán & sổ đỏ
---
### 3.5. Form Templates (Biểu mẫu in ấn)
**Kiến trúc:** Mail Merge Engine - Word-style template với merge fields.
**Models:** `FormTemplate`, `FormField`, `FormPrintLog`
**FormTemplate:**
- `name`, `code`, `target_model` (Contract/Product/Customer)
- `html_template`: Nội dung HTML với placeholder `{{ma_truong}}`
- `paper_size`: A4/A5/Letter
**FormField (Merge Fields):**
- `code`: Tên biến trong template (ví dụ: `ten_khach_hang`)
- `source_type`: `db_column` | `db_relation` | `formula` | `input` | `static`
- `source_config`: JSON cấu hình (tên cột, công thức, relation path...)
- `format`: `text` | `number` | `currency` | `date` | `percent`
- `decimal_places`: Số chữ số thập phân
**Cách hoạt động:**
1. Admin tạo template HTML, chèn `{{ten_khach_hang}}`
2. Định nghĩa FormField: `ten_khach_hang` lấy từ `contract.customers.0.full_name`
3. Khi in: `MailMergeService::render()` evaluate tất cả fields → thay vào template
4. Snapshot được lưu vào `FormPrintLog`
**Filament Resource:**
- `FormTemplateResource` → CRUD biểu mẫu với Repeater fields
**Services:**
- `MailMergeService::evaluateFields()` - Tính toán giá trị tất cả fields
- `MailMergeService::render()` - Render HTML cuối cùng
- `MailMergeService::savePrintLog()` - Lưu snapshot + rendered HTML
---
### 3.6. Sales Phases (Đợt mở bán)
**Models:** `SalesPhase`, `SalesPhaseProduct` (pivot)
**SalesPhase:**
- `project_id`, `name`, `code`, `status` (Chuẩn bị | Đang mở bán | Tạm dừng | Đã đóng)
- `start_date`, `end_date`, `description`
- `payment_template_id` - Mẫu lịch thanh toán mặc định cho đợt
- `discount_policy` (JSONB) - Chính sách chiết khấu mặc định
**SalesPhaseProduct (pivot):**
- `sales_phase_id`, `product_id`
- `sale_price`, `land_value`, `foundation_value` - Giá bán riêng của đợt
- `discount_details` (JSONB) - Chiết khấu riêng cho từng sản phẩm trong đợt
- `status` (Còn hàng | Đã giữ | Đã bán | Khóa)
**Quan hệ:**
- `SalesPhase` belongsTo `Project`, belongsTo `PaymentTemplate`
- `SalesPhase` belongsToMany `Product` qua `sales_phase_products`
- `Product` belongsToMany `SalesPhase`
- `Project` hasMany `SalesPhase`
- `Contract` belongsTo `SalesPhase` (nullable)
**Logic tích hợp:**
- `ContractForm`: Khi chọn `sales_phase_id` + `product_id`, tự động populate giá trị tài chính từ `SalesPhaseProduct` pivot
- `CreateContract`: Nếu HĐ có `sales_phase_id` và không có `payment_template_id` trực tiếp, tự động lấy template từ `salesPhase->paymentTemplate`
- `SalesPhaseForm`: Repeater `phaseProducts` cho phép thêm sản phẩm với giá và chiết khấu riêng
**Filament Resources:**
- `SalesPhaseResource``SalesPhaseForm` + `SalesPhasesTable`
---
## 4. CÁC COMMAND IMPORT DỮ LIỆU
### `import:products-excel {file=sanpham.xlsx}`
- Import sản phẩm vào dự án "Hà Quang 1"
- Tự động parse hạ tầng từ chuỗi "Key: Value - Key2: Value2"
- Tạo custom_data (block, building_density...)
### `import:customers-excel {file=khachhang.xlsx}`
- Import khách hàng cá nhân
- Tách nhiều số điện thoại (dấu phẩy, gạch chéo, xuống dòng)
- Parse ngày tháng Excel (số serial hoặc chuỗi)
- Tự động tạo mẫu Công ty + Ngườ đại diện (Công ty TNHH BĐS Thịnh Vượng)
### `contracts:generate-schedules {--force}`
- Tự động tạo lịch thanh toán cho các hợp đồng chưa có lịch
- Ưu tiên `contract.payment_template_id`, fallback lấy từ `product.project.paymentTemplate`
- Option `--force` để tạo lại lịch cho HĐ đã có schedule
### `import:contracts-complex {hopdong=hopdong.xlsx} {hdkh=Hd_kh.xlsx}`
- Logic "Bắc cầu" 2 file:
1. `hopdong.xlsx`: Dữ liệu tài chính (theo Số HĐMB)
2. `Hd_kh.xlsx`: Liên kết Khách hàng - Lô đất - Thứ tự chuyển nhượng
- Tìm mapping giữa mã lô và số HĐMB (str_contains)
- Tạo/cập nhật Contract + liên kết pivot `contract_customers`
### Các file Excel quan trọng (KHÔNG ĐƯỢC XÓA)
- `hopdong.xlsx` - Dữ liệu hợp đồng tài chính
- `Hd_kh.xlsx` - Liên kết hợp đồng-khách hàng
- `khachhang.xlsx` - Danh sách khách hàng
- `sanpham.xlsx` - Danh sách sản phẩm/lô đất
---
## 5. TÌNH TRẠNG CÁC PHẦN ĐÃ LÀM / ĐANG DỞ
### 5.1. Đã hoàn thành
- [x] Kiến trúc Schemas cho tất cả Resources
- [x] Import Customers, Products, Contracts từ Excel
- [x] Mở rộng bảng customers (type, representative, addresses...)
- [x] Mở rộng bảng contracts (land_value, foundation_value, discount_details...)
- [x] ContractScheduleService - Tạo lịch thanh toán từ template
- [x] PaymentObserver - Tự động tính toán tài chính + khấu trừ dư
- [x] PaymentResource (Form + Table)
- [x] Test: ContractFinanceFlowTest (PASS)
- [x] Cấu hình PHPUnit dùng PostgreSQL testing database
- [x] **Fix ContractForm:** `payment_template_id` đã lưu vào DB, tự động tạo lịch khi tạo HĐ mới
- [x] **PaymentForm validation:** Không cho phép thu quá công nợ đợt / HĐ
- [x] **PaymentsTable:** Thêm cột Loại đợt, Trạng thái đối soát, Còn thiếu
- [x] **Command generate schedule hàng loạt:** `php artisan contracts:generate-schedules`
- [x] **PaymentFine Resource:** Form + Table đầy đủ
- [x] **Appendix Resource:** Form + Table đầy đủ
- [x] **Settlement Resource:** Form + Table đầy đủ
- [x] **Discount Engine:** Tính toán tự động chiết khấu + hiển thị `final_value` trong ContractForm
- [x] **Calculation Pipeline:** Kiến trúc tính toán tường minh (Step-by-step) với làm tròn tại mỗi bước
- [x] **Form Templates:** Mail Merge Engine cho phiếu tính giá, HĐ, phụ lục - Admin tự tạo template
- [x] **Sales Phase Module:** Quản lý đợt mở bán, giá bán riêng theo đợt, tích hợp vào ContractForm
- [x] **ProjectReport Page:** Báo cáo thống kê bán hàng, thanh toán theo từng dự án
- [x] **ExportDebtReport Command:** Xuất Excel báo cáo công nợ khách hàng (2 sheet: Tổng hợp + Chi tiết đợt TT)
- [x] **Notification System:** `PaymentDueNotification` + `SendPaymentDueNotifications` command + `RecentNotifications` widget
- [x] **Soft Delete:** Contract, Payment, Customer + Restore/ForceDelete UI trong Filament Tables
- [x] **Payment.collected_by:** Ghi nhận ngườ thu tiền trong PaymentForm + PaymentsTable
### 5.2. Đang dở / Cần tiếp tục
- [x] **Dashboard thống kê:** Đã tạo `ContractStatsOverview` + `UpcomingPaymentsTable`
- [x] **Notification:** Đã có hệ thống cảnh báo đợt thanh toán sắp đến hạn (database notifications)
### 5.3. Vấn đề kỹ thuật ĐÃ XỬ LÝ
- [x] ContractTable đã thêm cột `paid_amount` / `remaining_amount`, chuyển sang dùng `ContractsTable` Schemas
- [x] Logic `syncWithoutDetaching` trong ImportContractsComplex đảm bảo nhiều KH cùng 1 HĐ không bị ghi đè
- [x] Fix N+1 query ở `PaymentScheduleItem::getPaidAmountAttribute()` (dùng `relationLoaded`)
- [x] Fix PaymentForm validation khi edit (`instanceof Payment` thay vì truthy check)
- [x] Fix ContractForm `final_value_display` hiển thị được cả khi create (dùng `$get` state)
---
## 6. LỘ TRÌNH PHÁT TRIỂN TIẾP THEO (ĐỀ XUẤT)
### Giai đoạn 1: Hoàn thiện Core Finance (Ưu tiên CAO)
1. **Fix ContractForm:** Cho phép chọn template và tự động tạo lịch thanh toán ngay khi tạo hợp đồng
2. **Hoàn thiện PaymentForm:** Thêm validation số tiền không vượt quá công nợ đợt
3. **Cập nhật PaymentsTable:** Thêm cột "Đợt TT", "Trạng thái đối soát", "Còn thiếu"
4. **Command generate schedule hàng loạt:** `php artisan contracts:generate-schedules` cho 139 hợp đồng đã import
### Giai đoạn 2: Module Bổ sung (Ưu tiên TRUNG BÌNH)
5. **PaymentFine Resource:** Quản lý tiền phạt chậm thanh toán ✅
6. **Appendix Resource:** Quản lý phụ lục hợp đồng ✅
7. **Settlement Resource:** Quản lý thanh lý hợp đồng ✅
8. **Discount Engine:** Tính toán tự động chiết khấu từ `discount_details` vào giá trị hợp đồng ✅
### Giai đoạn 3: Báo cáo & Tối ưu (Ưu tiên THẤP)
9. **Dashboard Tài chính:** Tổng doanh thu, dòng tiền dự kiến, công nợ phải thu ✅
10. **Báo cáo theo Dự án:** Thống kê bán hàng, thanh toán theo từng dự án ✅
11. **Export Excel:** Xuất báo cáo công nợ khách hàng ✅
12. **Notification:** Cảnh báo đợt thanh toán sắp đến hạn ✅
### Giai đoạn 4: An toàn & Audit (Đang làm)
13. **Soft Delete:** Contract, Payment, Customer + Restore/ForceDelete UI ✅
14. **Payment.collected_by:** Ghi nhận ngườ thu tiền ✅
### Giai đoạn 5: Phân quyền (Thiết kế xong, chờ triển khai)
15. **Permission System (Hướng B - Tự viết, không Spatie):**
- `role_templates`: Mẫu nhóm với permissions JSONB
- `users`: role_template_id + extra_permissions + excluded_permissions
- Session cache effective permissions (tính 1 lần/login)
- `php artisan permissions:sync` thủ công khi thêm module
- Action mới mặc định TẮT, admin bật thủ công
- Xem chi tiết: `WORKFLOW.md` Phần VIII
---
## 7. CÂU LỆNH THƯỜNG DÙNG
```bash
# Chạy test
DB_HOST=127.0.0.1 ./vendor/bin/pest
# Chạy test cụ thể
DB_HOST=127.0.0.1 ./vendor/bin/pest --filter="ContractFinanceFlowTest"
# Import dữ liệu
db:host=127.0.0.1 php artisan import:products-excel
db:host=127.0.0.1 php artisan import:customers-excel
db:host=127.0.0.1 php artisan import:contracts-complex
# Tinker
DB_HOST=127.0.0.1 php artisan tinker
# Migrate (KHÔNG dùng fresh!)
DB_HOST=127.0.0.1 php artisan migrate
```
---
## 8. DANH SÁCH FILE MỚI / THAY ĐỔI TRONG PHIÊN NÀY
### Migrations mới
- `database/migrations/2026_04_24_083000_add_payment_template_id_to_contracts.php`
- `database/migrations/2026_04_28_013900_add_calculation_log_to_contracts.php`
- `database/migrations/2026_04_28_020000_create_form_templates_tables.php`
- `database/migrations/2026_04_28_030000_create_sales_phases_tables.php`
- `database/migrations/2026_04_28_031000_add_sales_phase_id_to_contracts.php`
- `database/migrations/2026_04_29_000000_create_notifications_table.php`
### Services mới
- `app/Services/DiscountEngine.php` - Tính toán chiết khấu
- `app/Services/Calculation/` - Calculation Pipeline (RoundingRule, CalculationStep, CalculationResult, CalculationPipeline, PriceCalculationService)
- `app/Services/Forms/MailMergeService.php` - Engine xử lý biểu mẫu in ấn
- `app/Console/Commands/GenerateContractSchedules.php` - Command tạo lịch hàng loạt
- `app/Console/Commands/ExportDebtReport.php` - Command xuất Excel báo cáo công nợ
- `app/Console/Commands/SendPaymentDueNotifications.php` - Command gửi cảnh báo đợt TT sắp đến hạn
- `app/Notifications/PaymentDueNotification.php` - Database notification cho đợt thanh toán
### Filament Resources mới
- `app/Filament/Resources/PaymentFines/` (Resource + Form + Table + Pages)
- `app/Filament/Resources/Appendices/` (Resource + Form + Table + Pages)
- `app/Filament/Resources/Settlements/` (Resource + Form + Table + Pages)
- `app/Filament/Resources/FormTemplates/` (Resource + Form + Table + Pages)
- `app/Filament/Resources/SalesPhases/` (Resource + Form + Table + Pages)
### Widgets mới
- `app/Filament/Widgets/ContractStatsOverview.php` - Dashboard tổng quan tài chính
- `app/Filament/Widgets/UpcomingPaymentsTable.php` - Danh sách đợt TT sắp đến hạn
- `app/Filament/Widgets/RecentNotifications.php` - Danh sách thông báo chưa đọc
### Filament Pages mới
- `app/Filament/Pages/ProjectReport.php` - Báo cáo thống kê theo dự án
### Models mới
- `app/Models/SalesPhase.php` - Đợt mở bán
- `app/Models/SalesPhaseProduct.php` - Pivot model giá bán theo đợt
### Models sửa đổi
- `app/Models/Contract.php` - Thêm `paymentTemplate()`, `salesPhase()`, accessor `final_value`
- `app/Models/PaymentScheduleItem.php` - Thêm accessor `paid_amount`, `remaining_amount`
- `app/Models/Product.php` - Thêm `salesPhases()`, `activeSalesPhase()`
- `app/Models/Project.php` - Thêm `salesPhases()`
- `app/Models/User.php` - Thêm `FilamentUser` interface để user có quyền truy cập panel
### Forms/Tables sửa đổi
- `app/Filament/Resources/Contracts/ContractResource.php` - Fix action `EditAction` namespace (`Filament\Actions\EditAction`)
- `app/Filament/Resources/Contracts/Schemas/ContractForm.php` - Fix `payment_template_id`, thêm `final_value_display`, thêm `sales_phase_id` với auto-populate giá từ SalesPhaseProduct
- `app/Filament/Resources/Contracts/Pages/CreateContract.php` - Refactor dùng `$contract->payment_template_id`, fallback lấy từ `salesPhase->paymentTemplate`
- `app/Filament/Resources/Payments/Schemas/PaymentForm.php` - Thêm validation amount + helper text công nợ
- `app/Filament/Resources/Payments/Tables/PaymentsTable.php` - Thêm cột Loại đợt, Đối soát, Còn thiếu
- `app/Filament/Resources/Payments/PaymentResource.php` - Thêm eager load `scheduleItem.payments`
### Config/Provider sửa đổi
- `app/Providers/Filament/AdminPanelProvider.php` - Đăng ký widgets mới (ContractStatsOverview, UpcomingPaymentsTable, RecentNotifications)
- `phpunit.xml` - Cấu hình PostgreSQL testing (DB_HOST, DB_DATABASE, etc.)
- `config/database.php` - Default `pgsql`
- `composer.json` - Xóa script tạo SQLite
### Views mới
- `resources/views/filament/pages/project-report.blade.php` - View cho ProjectReport Page
---
## 9. FILE HỖ TRỢ CHUYỂN MÁY
- `NEXT_SESSION.md` - Checklist và hướng dẫn nhanh cho phiên làm việc tiếp theo
- `COMMIT_GUIDE.md` - Hướng dẫn commit toàn bộ thay đổi chưa commit
---
*File này cần được cập nhật mỗi khi có thay đổi lớn trong kiến trúc hoặc lộ trình phát triển.*