Files
hqland-app/AGENTS.md

20 KiB

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. GridSection 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:

  • ProjectResourceProjectForm (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:

  • CustomerResourceCustomerForm + 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:

  • ContractResourceContractForm + 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_amountexcess_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:

  • PaymentResourcePaymentForm + 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:

  • SalesPhaseResourceSalesPhaseForm + 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

  • Kiến trúc Schemas cho tất cả Resources
  • Import Customers, Products, Contracts từ Excel
  • Mở rộng bảng customers (type, representative, addresses...)
  • Mở rộng bảng contracts (land_value, foundation_value, discount_details...)
  • ContractScheduleService - Tạo lịch thanh toán từ template
  • PaymentObserver - Tự động tính toán tài chính + khấu trừ dư
  • PaymentResource (Form + Table)
  • Test: ContractFinanceFlowTest (PASS)
  • Cấu hình PHPUnit dùng PostgreSQL testing database
  • Fix ContractForm: payment_template_id đã lưu vào DB, tự động tạo lịch khi tạo HĐ mới
  • PaymentForm validation: Không cho phép thu quá công nợ đợt / HĐ
  • PaymentsTable: Thêm cột Loại đợt, Trạng thái đối soát, Còn thiếu
  • Command generate schedule hàng loạt: php artisan contracts:generate-schedules
  • PaymentFine Resource: Form + Table đầy đủ
  • Appendix Resource: Form + Table đầy đủ
  • Settlement Resource: Form + Table đầy đủ
  • Discount Engine: Tính toán tự động chiết khấu + hiển thị final_value trong ContractForm
  • 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
  • Form Templates: Mail Merge Engine cho phiếu tính giá, HĐ, phụ lục - Admin tự tạo template
  • Sales Phase Module: Quản lý đợt mở bán, giá bán riêng theo đợt, tích hợp vào ContractForm
  • ProjectReport Page: Báo cáo thống kê bán hàng, thanh toán theo từng dự án
  • 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)
  • Notification System: PaymentDueNotification + SendPaymentDueNotifications command + RecentNotifications widget

5.2. Đang dở / Cần tiếp tục

  • Dashboard thống kê: Đã tạo ContractStatsOverview + UpcomingPaymentsTable
  • 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Ý

  • ContractTable đã thêm cột paid_amount / remaining_amount, chuyển sang dùng ContractsTable Schemas
  • Logic syncWithoutDetaching trong ImportContractsComplex đảm bảo nhiều KH cùng 1 HĐ không bị ghi đè
  • Fix N+1 query ở PaymentScheduleItem::getPaidAmountAttribute() (dùng relationLoaded)
  • Fix PaymentForm validation khi edit (instanceof Payment thay vì truthy check)
  • 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)

  1. PaymentFine Resource: Quản lý tiền phạt chậm thanh toán
  2. Appendix Resource: Quản lý phụ lục hợp đồng
  3. Settlement Resource: Quản lý thanh lý hợp đồng
  4. 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)

  1. Dashboard Tài chính: Tổng doanh thu, dòng tiền dự kiến, công nợ phải thu
  2. Báo cáo theo Dự án: Thống kê bán hàng, thanh toán theo từng dự án
  3. Export Excel: Xuất báo cáo công nợ khách hàng
  4. Notification: Cảnh báo đợt thanh toán sắp đến hạn

7. CÂU LỆNH THƯỜNG DÙNG

# 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.