Add Soft Delete UI (Restore/ForceDelete) + Update docs (AGENTS, ASSESSMENT, WORKFLOW)
This commit is contained in:
@@ -306,6 +306,8 @@
|
|||||||
- [x] **ProjectReport Page:** Báo cáo thống kê bán hàng, thanh toán theo từng dự án
|
- [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] **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] **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
|
### 5.2. Đang dở / Cần tiếp tục
|
||||||
- [x] **Dashboard thống kê:** Đã tạo `ContractStatsOverview` + `UpcomingPaymentsTable`
|
- [x] **Dashboard thống kê:** Đã tạo `ContractStatsOverview` + `UpcomingPaymentsTable`
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
| 1 | **~~MailMergeService dùng `eval()`~~ [ĐÃ SỬA]** | `safeEval()` execute string bằng `eval('return ' . $expression)` | Nếu sanitize lỗi → Remote Code Execution. Hiện filter regex chưa đủ chặt |
|
| 1 | **~~MailMergeService dùng `eval()`~~ [ĐÃ SỬA]** | `safeEval()` execute string bằng `eval('return ' . $expression)` | Nếu sanitize lỗi → Remote Code Execution. Hiện filter regex chưa đủ chặt |
|
||||||
| 2 | **~~`Contract::saved()` gọi `saveQuietly()`~~ [ĐÃ SỬA]** | Sau khi save HĐ, trigger tính toán rồi save lại | Nếu logic thay đổi → infinite loop. Hiện tại may mắn không loop vì chỉ update `calculation_log` nhưng rủi ro cao |
|
| 2 | **~~`Contract::saved()` gọi `saveQuietly()`~~ [ĐÃ SỬA]** | Sau khi save HĐ, trigger tính toán rồi save lại | Nếu logic thay đổi → infinite loop. Hiện tại may mắn không loop vì chỉ update `calculation_log` nhưng rủi ro cao |
|
||||||
| 3 | **~~Không có Transaction~~ [ĐÃ SỬA]** | `ImportContractsComplex` dùng `DB::beginTransaction` nhưng các service khác không | Nếu tạo HĐ thành công nhưng tạo lịch TT lỗi → dữ liệu lệch |
|
| 3 | **~~Không có Transaction~~ [ĐÃ SỬA]** | `ImportContractsComplex` dùng `DB::beginTransaction` nhưng các service khác không | Nếu tạo HĐ thành công nhưng tạo lịch TT lỗi → dữ liệu lệch |
|
||||||
| 4 | **Không có Soft Delete** | Tất cả model dùng `Model::delete()` cứng | Xóa nhầm HĐ/Thu tiền → mất vĩnh viễn, không audit được |
|
| 4 | **~~Không có Soft Delete~~ [ĐÃ SỬA]** | Đã thêm SoftDeletes cho Contract, Payment, Customer + Restore/ForceDelete UI | Xóa nhầm → có thể khôi phục, audit được |
|
||||||
|
|
||||||
### 🟡 Trung bình - Ảnh hưởng trải nghiệm
|
### 🟡 Trung bình - Ảnh hưởng trải nghiệm
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
| 5 | **Chưa có phân quyền** | Chỉ có 1 loại user, ai cũng vào được mọi chức năng | Nhân viên thu ngân có thể xóa HĐ, sửa giá |
|
| 5 | **Chưa có phân quyền** | Chỉ có 1 loại user, ai cũng vào được mọi chức năng | Nhân viên thu ngân có thể xóa HĐ, sửa giá |
|
||||||
| 6 | **Chưa có API** | Hiện chỉ có Filament Admin Panel | Không làm app mobile, không tích hợp với website bán hàng |
|
| 6 | **Chưa có API** | Hiện chỉ có Filament Admin Panel | Không làm app mobile, không tích hợp với website bán hàng |
|
||||||
| 7 | **ContractForm chưa hiển thị `calculation_log` đúng** | Khi create HĐ mới, `final_value_display` dùng `DiscountEngine` cũ thay vì Pipeline | Giá trị hiển thị có thể khác với giá trị lưu DB |
|
| 7 | **ContractForm chưa hiển thị `calculation_log` đúng** | Khi create HĐ mới, `final_value_display` dùng `DiscountEngine` cũ thay vì Pipeline | Giá trị hiển thị có thể khác với giá trị lưu DB |
|
||||||
| 8 | **Payment chưa liên kết người thu** | `Payment` chỉ có `contract_id`, không có `collected_by` | Không biết ai thu tiền, khó trách nhiệm |
|
| 8 | **~~Payment chưa liên kết ngườ thu~~ [ĐÃ SỬA]** | Đã thêm `collected_by` (foreign key → users) + hiển thị trong Form/Table | Ghi nhận rõ ngườ thu tiền |
|
||||||
| 9 | **Chưa có sổ quỹ** | Thu tiền nhưng không ghi nhận vào quỹ tiền mặt/ngân hàng | Không đối soát được thực thu với ngân hàng |
|
| 9 | **Chưa có sổ quỹ** | Thu tiền nhưng không ghi nhận vào quỹ tiền mặt/ngân hàng | Không đối soát được thực thu với ngân hàng |
|
||||||
|
|
||||||
### 🟢 Thấp - Cần cải thiện lâu dài
|
### 🟢 Thấp - Cần cải thiện lâu dài
|
||||||
@@ -81,8 +81,8 @@
|
|||||||
### Giai đoạn 1: Sửa lỗi & An toàn (ĐÃ HOÀN THÀNH)
|
### Giai đoạn 1: Sửa lỗi & An toàn (ĐÃ HOÀN THÀNH)
|
||||||
1. **~~Thay thế `eval()`~~ [DONE]** - Dùng shunting yard + bcmath trong MailMergeService
|
1. **~~Thay thế `eval()`~~ [DONE]** - Dùng shunting yard + bcmath trong MailMergeService
|
||||||
2. **~~Thêm `DB::transaction`~~ [DONE]** - `ContractScheduleService::generateFromTemplate()` đã có transaction
|
2. **~~Thêm `DB::transaction`~~ [DONE]** - `ContractScheduleService::generateFromTemplate()` đã có transaction
|
||||||
3. **Thêm Soft Delete** cho Contract, Payment, Customer + model `DeletedBy` để audit
|
3. **~~Thêm Soft Delete~~ [DONE]** - Contract, Payment, Customer + Restore/ForceDelete UI
|
||||||
4. **Thêm `collected_by`** vào bảng `payments` + hiển thị ngườ thu trong Form/Table
|
4. **~~Thêm `collected_by`~~ [DONE]** - Bảng `payments` + Form/Table hiển thị ngườ thu
|
||||||
5. **~~Fix `Contract::saved()`~~ [DONE]** - Dùng `self::$calculating` guard flag + `updateQuietly()`
|
5. **~~Fix `Contract::saved()`~~ [DONE]** - Dùng `self::$calculating` guard flag + `updateQuietly()`
|
||||||
|
|
||||||
### Giai đoạn 2: Quyền hạn & Báo cáo (2 tuần)
|
### Giai đoạn 2: Quyền hạn & Báo cáo (2 tuần)
|
||||||
|
|||||||
@@ -451,9 +451,9 @@ User 1───* Notification (MorphMany)
|
|||||||
|
|
||||||
| # | Vấn đề | Mức độ | Ghi chú |
|
| # | Vấn đề | Mức độ | Ghi chú |
|
||||||
|---|--------|--------|---------|
|
|---|--------|--------|---------|
|
||||||
| 1 | **Soft Delete** | 🔴 Nghiêm trọng | Tất cả model dùng delete cứng. Xóa nhầm → mất vĩnh viễn |
|
| 1 | **~~Soft Delete~~ ✅** | 🟢 Đã xong | Contract, Payment, Customer có SoftDeletes + Restore/ForceDelete UI |
|
||||||
| 2 | **Phân quyền** | 🟡 Trung bình | Chỉ 1 loại user. Ai cũng xóa/sửa được mọi thứ |
|
| 2 | **Phân quyền** | 🟡 Trung bình | Chỉ 1 loại user. Ai cũng xóa/sửa được mọi thứ |
|
||||||
| 3 | **Payment.collected_by** | 🟡 Trung bình | Không ghi nhận ai thu tiền. Khó truy trách nhiệm |
|
| 3 | **~~Payment.collected_by~~ ✅** | 🟢 Đã xong | Đã thêm collected_by (FK → users) + hiển thị Form/Table |
|
||||||
| 4 | **Sổ quỹ** | 🟡 Trung bình | Thu tiền nhưng không ghi vào quỹ TM/NH. Không đối soát được |
|
| 4 | **Sổ quỹ** | 🟡 Trung bình | Thu tiền nhưng không ghi vào quỹ TM/NH. Không đối soát được |
|
||||||
| 5 | **CRM Pipeline** | 🟢 Thấp | Chưa quản lý Lead/Khách hàng tiềm năng |
|
| 5 | **CRM Pipeline** | 🟢 Thấp | Chưa quản lý Lead/Khách hàng tiềm năng |
|
||||||
| 6 | **Báo cáo BCTC** | 🟢 Thấp | Chưa có báo cáo theo chuẩn kế toán VN |
|
| 6 | **Báo cáo BCTC** | 🟢 Thấp | Chưa có báo cáo theo chuẩn kế toán VN |
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use Filament\Actions\Action;
|
|||||||
use Filament\Actions\BulkActionGroup;
|
use Filament\Actions\BulkActionGroup;
|
||||||
use Filament\Actions\DeleteBulkAction;
|
use Filament\Actions\DeleteBulkAction;
|
||||||
use Filament\Actions\EditAction;
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
class ContractsTable
|
class ContractsTable
|
||||||
@@ -84,10 +87,13 @@ class ContractsTable
|
|||||||
->queries(
|
->queries(
|
||||||
true: fn ($query) => $query->where('transfer_order', 0),
|
true: fn ($query) => $query->where('transfer_order', 0),
|
||||||
false: fn ($query) => $query->where('transfer_order', '>', 0),
|
false: fn ($query) => $query->where('transfer_order', '>', 0),
|
||||||
)
|
),
|
||||||
|
TrashedFilter::make(),
|
||||||
])
|
])
|
||||||
->recordActions([
|
->recordActions([
|
||||||
EditAction::make(),
|
EditAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
Action::make('generateSchedule')
|
Action::make('generateSchedule')
|
||||||
->label('Tạo lịch TT')
|
->label('Tạo lịch TT')
|
||||||
->icon('heroicon-o-calendar-days')
|
->icon('heroicon-o-calendar-days')
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ namespace App\Filament\Resources\Customers\Tables;
|
|||||||
use Filament\Actions\BulkActionGroup;
|
use Filament\Actions\BulkActionGroup;
|
||||||
use Filament\Actions\DeleteBulkAction;
|
use Filament\Actions\DeleteBulkAction;
|
||||||
use Filament\Actions\EditAction;
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Columns\IconColumn;
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
class CustomersTable
|
class CustomersTable
|
||||||
@@ -64,9 +67,12 @@ class CustomersTable
|
|||||||
'INDIVIDUAL' => 'Cá nhân',
|
'INDIVIDUAL' => 'Cá nhân',
|
||||||
'COMPANY' => 'Công ty',
|
'COMPANY' => 'Công ty',
|
||||||
]),
|
]),
|
||||||
|
TrashedFilter::make(),
|
||||||
])
|
])
|
||||||
->recordActions([
|
->recordActions([
|
||||||
EditAction::make(),
|
EditAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
])
|
])
|
||||||
->bulkActions([
|
->bulkActions([
|
||||||
BulkActionGroup::make([
|
BulkActionGroup::make([
|
||||||
|
|||||||
@@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Filament\Resources\Payments\Tables;
|
namespace App\Filament\Resources\Payments\Tables;
|
||||||
|
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteBulkAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
class PaymentsTable
|
class PaymentsTable
|
||||||
@@ -99,6 +105,17 @@ class PaymentsTable
|
|||||||
->when($data['from'], fn ($q) => $q->whereDate('paid_date', '>=', $data['from']))
|
->when($data['from'], fn ($q) => $q->whereDate('paid_date', '>=', $data['from']))
|
||||||
->when($data['to'], fn ($q) => $q->whereDate('paid_date', '<=', $data['to']));
|
->when($data['to'], fn ($q) => $q->whereDate('paid_date', '<=', $data['to']));
|
||||||
}),
|
}),
|
||||||
|
TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
EditAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
])
|
||||||
|
->bulkActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
DeleteBulkAction::make(),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
->defaultSort('paid_date', 'desc');
|
->defaultSort('paid_date', 'desc');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user