WIP: SoftDelete Contract/Payment/Customer, collected_by, Notifications, ProjectReport, ExportDebtReport

This commit is contained in:
2026-04-29 04:46:58 +00:00
parent 0712046f4b
commit 78c22690eb
18 changed files with 1015 additions and 12 deletions

View File

@@ -0,0 +1,84 @@
<?php
namespace App\Filament\Pages;
use App\Models\Project;
use Filament\Pages\Page;
use Filament\Tables\Table;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
class ProjectReport extends Page implements HasTable
{
use InteractsWithTable;
protected static string | \BackedEnum | null $navigationIcon = 'heroicon-o-chart-bar';
protected static ?string $navigationLabel = 'Báo cáo theo Dự án';
protected static ?string $title = 'Báo cáo Thống kê theo Dự án';
protected static string | \UnitEnum | null $navigationGroup = 'Quản lý Dòng tiền';
protected static ?int $navigationSort = 50;
protected string $view = 'filament.pages.project-report';
public function table(Table $table): Table
{
return $table
->query(
Project::query()
->select('projects.id', 'projects.name', 'projects.code')
->selectRaw('COUNT(DISTINCT products.id) as product_count')
->selectRaw('COUNT(DISTINCT CASE WHEN contracts.id IS NOT NULL THEN products.id END) as sold_product_count')
->selectRaw('COUNT(DISTINCT contracts.id) as contract_count')
->selectRaw('COALESCE(SUM(contracts.total_value), 0) as total_revenue')
->selectRaw('COALESCE(SUM(contracts.paid_amount), 0) as total_paid')
->selectRaw('COALESCE(SUM(contracts.remaining_amount), 0) as total_remaining')
->leftJoin('products', 'products.project_id', '=', 'projects.id')
->leftJoin('contracts', 'contracts.product_id', '=', 'products.id')
->groupBy('projects.id', 'projects.name', 'projects.code')
)
->columns([
TextColumn::make('name')
->label('Dự án')
->searchable()
->sortable(),
TextColumn::make('product_count')
->label('Tổng SP')
->alignCenter()
->sortable(),
TextColumn::make('sold_product_count')
->label('Đã bán')
->alignCenter()
->sortable()
->color('success'),
TextColumn::make('contract_count')
->label('Số HĐ')
->alignCenter()
->sortable(),
TextColumn::make('total_revenue')
->label('Tổng giá trị HĐ')
->money('VND')
->sortable()
->summarize(\Filament\Tables\Columns\Summarizers\Sum::make()->label('Tổng')->money('VND')),
TextColumn::make('total_paid')
->label('Đã thu')
->money('VND')
->sortable()
->color('success')
->summarize(\Filament\Tables\Columns\Summarizers\Sum::make()->label('Tổng')->money('VND')),
TextColumn::make('total_remaining')
->label('Công nợ phải thu')
->money('VND')
->sortable()
->color('danger')
->summarize(\Filament\Tables\Columns\Summarizers\Sum::make()->label('Tổng')->money('VND')),
])
->defaultSort('total_revenue', 'desc')
->paginated([10, 25, 50]);
}
}

View File

@@ -6,8 +6,8 @@ use App\Models\Customer;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Actions\Action;
use Filament\Schemas\Components\Section;
use Filament\Actions\Action;
use Filament\Forms\Components\TagsInput;
use Filament\Schemas\Schema;
use Filament\Schemas\Components\Utilities\Set;

View File

@@ -149,6 +149,14 @@ class PaymentForm
])
->default('Chuyển khoản')
->required(),
Select::make('collected_by')
->label('Ngườ thu')
->relationship('collector', 'name')
->searchable()
->preload()
->default(auth()->id())
->required(),
]),
Section::make('Bổ sung')

View File

@@ -73,6 +73,11 @@ class PaymentsTable
->money('VND')
->placeholder('-')
->color('danger'),
Tables\Columns\TextColumn::make('collector.name')
->label('Ngườ thu')
->placeholder('-')
->sortable(),
])
->filters([
Tables\Filters\SelectFilter::make('method')

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Filament\Widgets;
use Filament\Actions\Action;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Widgets\TableWidget as BaseWidget;
class RecentNotifications extends BaseWidget
{
protected int | string | array $columnSpan = 'full';
protected static ?int $sort = 1;
public function table(Table $table): Table
{
return $table
->query(function () {
$user = auth()->user();
if (! $user) {
return \Illuminate\Notifications\DatabaseNotification::query()->whereRaw('1=0');
}
return $user->notifications()->whereNull('read_at')->latest()->getQuery();
})
->columns([
Tables\Columns\TextColumn::make('data.title')
->label('Tiêu đề')
->badge()
->color('warning'),
Tables\Columns\TextColumn::make('data.message')
->label('Nội dung')
->limit(100),
Tables\Columns\TextColumn::make('created_at')
->label('Thờ gian')
->dateTime('d/m/Y H:i')
->color('gray'),
])
->actions([
Action::make('markAsRead')
->label('Đánh dấu đã đọc')
->icon('heroicon-o-check-circle')
->color('success')
->action(function ($record) {
$record->markAsRead();
}),
])
->paginated([5, 10, 25])
->emptyStateHeading('Không có thông báo mới')
->emptyStateDescription('Bạn sẽ nhận được cảnh báo khi có đợt thanh toán sắp đến hạn.');
}
}