ifnex/04_Laravel/app/Filament/Pages/Reports/FinancialReport.php
Kazem Alghasi 8b0814230c feat(ui): implement unified design system and financial reporting
Introduce a comprehensive design system across WordPress and Laravel
to ensure visual consistency. Implement new financial reporting
capabilities and Excel export functionality within the Filament admin
panel.

- Add `DESIGN_SYSTEM.md` to define brand colors, typography, and spacing
- Implement `FinancialReport` page in Filament for transaction and
  shipment summaries
- Add Excel export support for `ShipmentResource` and
  `WalletTransactionResource` using Laravel Excel
- Refactor WordPress plugin and Filament CSS to utilize the new
  design system variables
- Update project status to reflect new reporting and design milestones
2026-08-08 06:13:09 +03:30

279 lines
11 KiB
PHP

<?php
namespace App\Filament\Pages\Reports;
use App\Exports\ShipmentsExport;
use App\Exports\WalletTransactionsExport;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Pages\Page;
use Filament\Notifications\Notification;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Facades\DB;
class FinancialReport extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-chart-bar';
protected static ?string $navigationLabel = 'گزارش مالی';
protected static ?string $navigationGroup = 'مالی';
protected static ?int $navigationSort = 5;
protected static ?string $slug = 'financial-report';
protected static string $view = 'filament.pages.reports.financial-report';
public ?array $data = [];
public ?array $reportData = null;
public function mount(): void
{
$this->form->fill();
}
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('فیلترهای گزارش')
->schema([
Forms\Components\Select::make('report_type')
->label('نوع گزارش')
->options([
'transactions' => 'تراکنش‌های کیف پول',
'shipments' => 'مرسولات',
'financial_summary' => 'خلاصه مالی',
])
->required()
->reactive()
->default('transactions'),
Forms\Components\DatePicker::make('from_date')
->label('از تاریخ')
->required()
->default(now()->subMonth()),
Forms\Components\DatePicker::make('to_date')
->label('تا تاریخ')
->required()
->default(now()),
Forms\Components\Select::make('transaction_type')
->label('نوع تراکنش')
->options([
'deposit' => 'واریزی',
'withdrawal' => 'برداشت',
'order_payment' => 'پرداخت سفارش',
'refund' => 'بازگشت وجه',
])
->visible(fn ($get) => $get('report_type') === 'transactions')
->placeholder('همه'),
Forms\Components\Select::make('shipment_status')
->label('وضعیت مرسوله')
->options([
'processed' => 'ثبت شده',
'picked_up' => 'تحویل به پست',
'in_transit' => 'در حال حمل',
'out_for_delivery' => 'آماده تحویل',
'delivered' => 'تحویل داده شده',
'failed' => 'ناموفق',
'returned' => 'بازگشته',
])
->visible(fn ($get) => $get('report_type') === 'shipments')
->placeholder('همه'),
])
->columns(2),
Forms\Components\Section::make('عملیات')
->schema([
Forms\Components\Actions::make([
Forms\Components\Actions\Action::make('generate')
->label('تولید گزارش')
->icon('heroicon-o-eye')
->color('primary')
->action('generateReport'),
Forms\Components\Actions\Action::make('export_excel')
->label('خروجی Excel')
->icon('heroicon-o-arrow-down-tray')
->color('success')
->action('exportExcel')
->visible(fn ($get) => $get('report_type') !== 'financial_summary'),
])
->fullWidth(),
]),
])
->statePath('data');
}
public function generateReport(): void
{
$this->validate();
$type = $this->data['report_type'];
$fromDate = $this->data['from_date'];
$toDate = $this->data['to_date'];
switch ($type) {
case 'transactions':
$this->generateTransactionsReport($fromDate, $toDate);
break;
case 'shipments':
$this->generateShipmentsReport($fromDate, $toDate);
break;
case 'financial_summary':
$this->generateFinancialSummary($fromDate, $toDate);
break;
}
}
private function generateTransactionsReport($fromDate, $toDate): void
{
$query = DB::table('wallet_transactions')
->join('wallets', 'wallet_transactions.wallet_id', '=', 'wallets.id')
->join('users', 'wallets.user_id', '=', 'users.id')
->select(
'wallet_transactions.id',
'users.name as user_name',
'users.email as user_email',
'wallet_transactions.amount',
'wallet_transactions.type',
'wallet_transactions.status',
'wallet_transactions.gateway',
'wallet_transactions.description',
'wallet_transactions.created_at'
)
->whereBetween('wallet_transactions.created_at', [$fromDate, $toDate . ' 23:59:59'])
->orderByDesc('wallet_transactions.created_at');
if (!empty($this->data['transaction_type'])) {
$query->where('wallet_transactions.type', $this->data['transaction_type']);
}
$this->reportData = [
'type' => 'transactions',
'data' => $query->get(),
'summary' => [
'total_count' => $query->count(),
'total_amount' => $query->sum('amount'),
'deposits' => $query->where('type', 'deposit')->sum('amount'),
'withdrawals' => $query->whereIn('type', ['withdrawal', 'order_payment'])->sum('amount'),
],
];
}
private function generateShipmentsReport($fromDate, $toDate): void
{
$query = DB::table('shipments')
->join('countries as from_country', 'shipments.from_country_id', '=', 'from_country.id')
->join('countries as to_country', 'shipments.to_country_id', '=', 'to_country.id')
->select(
'shipments.awb_no',
'shipments.direction',
'shipments.type',
'shipments.status',
'from_country.name as from_country',
'to_country.name as to_country',
'shipments.weight',
'shipments.chargeable_weight',
'shipments.shipping_price',
'shipments.total_fee',
'shipments.sender_name',
'shipments.receiver_name',
'shipments.created_at'
)
->whereBetween('shipments.created_at', [$fromDate, $toDate . ' 23:59:59'])
->orderByDesc('shipments.created_at');
if (!empty($this->data['shipment_status'])) {
$query->where('shipments.status', $this->data['shipment_status']);
}
$this->reportData = [
'type' => 'shipments',
'data' => $query->get(),
'summary' => [
'total_count' => $query->count(),
'total_fee' => $query->sum('total_fee'),
'total_weight' => $query->sum('chargeable_weight'),
],
];
}
private function generateFinancialSummary($fromDate, $toDate): void
{
// Total deposits
$deposits = DB::table('wallet_transactions')
->where('type', 'deposit')
->where('status', 'completed')
->whereBetween('created_at', [$fromDate, $toDate . ' 23:59:59'])
->sum('amount');
// Total withdrawals/order payments
$withdrawals = DB::table('wallet_transactions')
->whereIn('type', ['withdrawal', 'order_payment'])
->where('status', 'completed')
->whereBetween('created_at', [$fromDate, $toDate . ' 23:59:59'])
->sum('amount');
// Total shipments revenue
$shipmentsRevenue = DB::table('shipments')
->whereBetween('created_at', [$fromDate, $toDate . ' 23:59:59'])
->sum('total_fee');
// Total discount codes used
$discounts = DB::table('discount_codes')
->where('is_active', true)
->whereBetween('updated_at', [$fromDate, $toDate . ' 23:59:59'])
->sum('used_count');
// Wallet balances
$totalBalance = DB::table('wallets')->sum('balance');
$totalDeposited = DB::table('wallets')->sum('total_deposited');
$totalWithdrawn = DB::table('wallets')->sum('total_withdrawn');
$this->reportData = [
'type' => 'financial_summary',
'summary' => [
'deposits' => $deposits,
'withdrawals' => abs($withdrawals),
'shipments_revenue' => $shipmentsRevenue,
'discounts_used' => $discounts,
'total_balance' => $totalBalance,
'total_deposited' => $totalDeposited,
'total_withdrawn' => $totalWithdrawn,
'net_profit' => $shipmentsRevenue - $withdrawals,
],
];
}
public function exportExcel(): \Symfony\Component\HttpFoundation\BinaryFileResponse
{
$this->validate();
$type = $this->data['report_type'];
$filters = [
'from_date' => $this->data['from_date'],
'to_date' => $this->data['to_date'],
];
if ($type === 'transactions') {
$filters['type'] = $this->data['transaction_type'] ?? null;
$export = new WalletTransactionsExport($filters);
$fileName = 'transactions_' . now()->format('Y-m-d_H-i') . '.xlsx';
} elseif ($type === 'shipments') {
$filters['status'] = $this->data['shipment_status'] ?? null;
$export = new ShipmentsExport($filters);
$fileName = 'shipments_' . now()->format('Y-m-d_H-i') . '.xlsx';
} else {
Notification::make()
->title('خطا')
->body('برای خلاصه مالی خروجی Excel موجود نیست.')
->danger()
->send();
return redirect()->back();
}
return Excel::download($export, $fileName);
}
}