- Add Enums: TransactionType, TransactionStatus, PaymentGateway - Migrations: wallets, wallet_transactions, wallet_activity_logs - Models: Wallet, WalletTransaction, WalletActivityLog - Services: WalletService with complete business logic - Controllers: WalletController, PaymentController - Filament Resources: WalletResource, WalletTransactionResource - Artisan Command: ifnex:token for API token generation - Sanctum integration with statefulApi and exception handling - Full API endpoints: balance, transactions, admin-adjust, freeze/unfreeze
195 lines
9.1 KiB
PHP
195 lines
9.1 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources;
|
|
|
|
use App\Filament\Resources\WalletResource\Pages;
|
|
use App\Models\Wallet;
|
|
use App\Models\WalletTransaction;
|
|
use Filament\Forms;
|
|
use Filament\Forms\Form;
|
|
use Filament\Resources\Resource;
|
|
use Filament\Tables;
|
|
use Filament\Tables\Table;
|
|
use Filament\Tables\Actions\Action;
|
|
use Filament\Notifications\Notification;
|
|
|
|
class WalletResource extends Resource
|
|
{
|
|
protected static ?string $model = Wallet::class;
|
|
protected static ?string $navigationIcon = 'heroicon-o-wallet';
|
|
protected static ?string $navigationLabel = 'کیف پول کاربران';
|
|
protected static ?string $navigationGroup = 'مالی';
|
|
protected static ?int $navigationSort = 2;
|
|
|
|
public static function form(Form $form): Form
|
|
{
|
|
return $form->schema([
|
|
Forms\Components\Section::make('اطلاعات کیف پول')
|
|
->schema([
|
|
Forms\Components\Select::make('user_id')
|
|
->relationship('user', 'name')
|
|
->searchable()
|
|
->preload()
|
|
->required()
|
|
->label('کاربر'),
|
|
Forms\Components\TextInput::make('balance')
|
|
->numeric()
|
|
->suffix('ریال')
|
|
->disabled()
|
|
->label('موجودی فعلی'),
|
|
Forms\Components\TextInput::make('total_deposited')
|
|
->numeric()
|
|
->suffix('ریال')
|
|
->disabled()
|
|
->label('مجموع واریزی'),
|
|
Forms\Components\TextInput::make('total_withdrawn')
|
|
->numeric()
|
|
->suffix('ریال')
|
|
->disabled()
|
|
->label('مجموع برداشت'),
|
|
Forms\Components\Toggle::make('is_frozen')
|
|
->label('مسدود است؟')
|
|
->reactive(),
|
|
Forms\Components\Textarea::make('freeze_reason')
|
|
->label('دلیل مسدودی')
|
|
->visible(fn ($get) => $get('is_frozen'))
|
|
->maxLength(1000),
|
|
])
|
|
->columns(2),
|
|
]);
|
|
}
|
|
|
|
public static function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->columns([
|
|
Tables\Columns\TextColumn::make('user.name')
|
|
->label('کاربر')
|
|
->searchable()
|
|
->sortable(),
|
|
Tables\Columns\TextColumn::make('user.email')
|
|
->label('ایمیل')
|
|
->searchable(),
|
|
Tables\Columns\TextColumn::make('balance')
|
|
->label('موجودی')
|
|
->money('IRR', divideBy: 1)
|
|
->sortable()
|
|
->color(fn ($record) => $record->balance > 0 ? 'success' : 'danger'),
|
|
Tables\Columns\TextColumn::make('total_deposited')
|
|
->label('مجموع واریزی')
|
|
->money('IRR', divideBy: 1)
|
|
->sortable(),
|
|
Tables\Columns\IconColumn::make('is_frozen')
|
|
->label('وضعیت')
|
|
->boolean()
|
|
->trueIcon('heroicon-o-lock-closed')
|
|
->falseIcon('heroicon-o-lock-open')
|
|
->trueColor('danger')
|
|
->falseColor('success'),
|
|
Tables\Columns\TextColumn::make('transactions_count')
|
|
->label('تعداد تراکنش')
|
|
->counts('transactions')
|
|
->sortable(),
|
|
Tables\Columns\TextColumn::make('created_at')
|
|
->dateTime('Y/m/d H:i', 'Asia/Tehran')
|
|
->label('تاریخ ایجاد')
|
|
->sortable(),
|
|
])
|
|
->defaultSort('created_at', 'desc')
|
|
->actions([
|
|
// اکشن شارژ/کسر دستی
|
|
Action::make('manual_adjust')
|
|
->label('شارژ/کسر دستی')
|
|
->icon('heroicon-o-currency-dollar')
|
|
->color('primary')
|
|
->form([
|
|
Forms\Components\TextInput::make('amount')
|
|
->label('مبلغ (ریال) — مثبت = شارژ، منفی = کسر')
|
|
->numeric()
|
|
->required()
|
|
->helperText('مثال: 5000000 برای شارژ، -1000000 برای کسر'),
|
|
Forms\Components\Textarea::make('description')
|
|
->label('توضیحات (قابل نمایش به کاربر)')
|
|
->required()
|
|
->maxLength(1000),
|
|
Forms\Components\Textarea::make('admin_notes')
|
|
->label('یادداشت داخلی ادمین')
|
|
->helperText('فقط برای ادمینها قابل مشاهده است')
|
|
->maxLength(2000),
|
|
])
|
|
->action(function (Wallet $record, array $data): void {
|
|
$admin = auth()->user();
|
|
$amount = $data['amount'];
|
|
$walletService = app(\App\Services\WalletService::class);
|
|
|
|
try {
|
|
if ($amount > 0) {
|
|
$walletService->manualDeposit($record, $amount, $data['description'], $admin, $data['admin_notes'] ?? null);
|
|
Notification::make()
|
|
->success()
|
|
->title('شارژ با موفقیت انجام شد')
|
|
->body("مبلغ: " . number_format($amount) . " ریال")
|
|
->send();
|
|
} else {
|
|
$walletService->manualWithdrawal($record, abs($amount), $data['description'], $admin, $data['admin_notes'] ?? null);
|
|
Notification::make()
|
|
->success()
|
|
->title('کسر با موفقیت انجام شد')
|
|
->body("مبلغ: " . number_format(abs($amount)) . " ریال")
|
|
->send();
|
|
}
|
|
} catch (\Exception $e) {
|
|
Notification::make()
|
|
->danger()
|
|
->title('خطا در انجام عملیات')
|
|
->body($e->getMessage())
|
|
->send();
|
|
}
|
|
}),
|
|
|
|
// اکشن مشاهده تراکنشها
|
|
Action::make('view_transactions')
|
|
->label('تراکنشها')
|
|
->icon('heroicon-o-list-bullet')
|
|
->url(fn (Wallet $record) => WalletTransactionResource::getUrl('index', ['wallet_id' => $record->id])),
|
|
|
|
// اکشن مسدود/رفع مسدود
|
|
Action::make('toggle_freeze')
|
|
->label(fn (Wallet $record) => $record->isFrozen() ? 'رفع مسدودی' : 'مسدود کردن')
|
|
->icon(fn (Wallet $record) => $record->isFrozen() ? 'heroicon-o-lock-open' : 'heroicon-o-lock-closed')
|
|
->color(fn (Wallet $record) => $record->isFrozen() ? 'success' : 'danger')
|
|
->form(fn (Wallet $record) => $record->isFrozen() ? [] : [
|
|
Forms\Components\Textarea::make('reason')
|
|
->label('دلیل مسدودی')
|
|
->required()
|
|
->maxLength(1000),
|
|
])
|
|
->action(function (Wallet $record, ?array $data): void {
|
|
$admin = auth()->user();
|
|
|
|
if ($record->isFrozen()) {
|
|
$record->unfreeze($admin);
|
|
Notification::make()->success()->title('کیف پول از مسدودی خارج شد')->send();
|
|
} else {
|
|
$record->freeze($data['reason'], $admin);
|
|
Notification::make()->success()->title('کیف پول مسدود شد')->send();
|
|
}
|
|
})
|
|
->requiresConfirmation(),
|
|
])
|
|
->filters([
|
|
Tables\Filters\TernaryFilter::make('is_frozen')
|
|
->label('وضعیت مسدودی'),
|
|
]);
|
|
}
|
|
|
|
public static function getPages(): array
|
|
{
|
|
return [
|
|
'index' => Pages\ListWallets::route('/'),
|
|
'create' => Pages\CreateWallet::route('/create'),
|
|
'edit' => Pages\EditWallet::route('/{record}/edit'),
|
|
'view' => Pages\ViewWallet::route('/{record}'),
|
|
];
|
|
}
|
|
} |