ifnex/04_Laravel/app/Filament/Resources/CustomerCreditResource.php
Kazem Alghasi 02c29db696 feat(core): implement order approval flow, credit system, and import invoicing
Introduce a comprehensive set of commercial features including a multi-step
order approval workflow, customer credit management, and specialized
import service invoicing.

Key changes:
- Implement `pending_approval` and `approved` shipment statuses to allow
  staff verification before customer payment.
- Add a credit system to `User` model with `credit_limit` and `credit_used`
  to manage customer balances and debts.
- Develop a new `importInvoice` PDF generation service following the
  "Sheet ENG Invoice" specification for import services.
- Add Filament resources for managing Audit Logs, Commitment Forms,
  Customer Credits, and Shipment Checklists.
- Implement staff-specific APIs for order approval/rejection and
  customer financial status monitoring.
- Integrate Kavenegar SMS service for mobile verification and notifications.
- Add bulk tracking import functionality via CSV/Excel.
- Update WordPress bridge assets (CSS/JS) to support the new multi-step
  order form UI and updated redirection logic.
- Update deployment configurations and documentation to reflect new
  production domains and feature sets.
2026-09-03 06:04:20 +03:30

243 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Filament\Resources;
use App\Filament\Resources\CustomerCreditResource\Pages;
use App\Models\User;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class CustomerCreditResource extends Resource
{
protected static ?string $model = User::class;
protected static ?string $navigationIcon = 'heroicon-o-banknotes';
protected static ?string $navigationGroup = 'مدیریت مالی';
protected static ?string $navigationLabel = 'اعتبار مشتریان';
protected static ?string $modelLabel = 'اعتبار مشتری';
protected static ?string $pluralModelLabel = 'اعتبار مشتریان';
protected static ?int $navigationSort = 10;
public static function form(Form $form): Form
{
return $form->schema([
Forms\Components\Section::make('اطلاعات مشتری')
->schema([
Forms\Components\TextInput::make('name')
->label('نام')
->disabled(),
Forms\Components\TextInput::make('email')
->label('ایمیل')
->disabled(),
Forms\Components\TextInput::make('phone')
->label('موبایل')
->disabled(),
])->columns(3),
Forms\Components\Section::make('اطلاعات اعتبار')
->schema([
Forms\Components\TextInput::make('credit_limit')
->label('سقف اعتبار (ریال)')
->numeric()
->default(0)
->minValue(0)
->required(),
Forms\Components\TextInput::make('credit_used')
->label('مبلغ استفاده شده (ریال)')
->numeric()
->default(0)
->disabled(),
Forms\Components\TextInput::make('available_credit')
->label('اعتبار باقیمانده (ریال)')
->numeric()
->disabled()
->dehydrated(false),
])->columns(3),
Forms\Components\Section::make('تاریخچه تراکنش‌ها')
->schema([
Forms\Components\Placeholder::make('transactions_history')
->label('تراکنش‌های اخیر')
->content(function ($record) {
if (!$record) return '-';
$transactions = DB::table('wallet_transactions')
->where('user_id', $record->id)
->orderBy('created_at', 'desc')
->take(5)
->get();
if ($transactions->isEmpty()) return 'تراکنشی ثبت نشده';
$html = '<div style="font-size: 12px; line-height: 1.8;">';
foreach ($transactions as $tx) {
$type = match($tx->type) {
'credit_add' => ' افزایش اعتبار',
'credit_use' => ' استفاده از اعتبار',
default => $tx->type,
};
$html .= '<div>' . $type . ': ' . number_format($tx->amount) . ' ریال - ' . $tx->description . '</div>';
}
$html .= '</div>';
return $html;
}),
]),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('id')
->label('شناسه')
->sortable(),
Tables\Columns\TextColumn::make('name')
->label('نام')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('email')
->label('ایمیل')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('phone')
->label('موبایل')
->searchable(),
Tables\Columns\TextColumn::make('credit_limit')
->label('سقف اعتبار')
->numeric()
->formatStateUsing(fn ($state) => number_format($state) . ' ریال')
->sortable(),
Tables\Columns\TextColumn::make('credit_used')
->label('استفاده شده')
->numeric()
->formatStateUsing(fn ($state) => number_format($state) . ' ریال')
->sortable(),
Tables\Columns\TextColumn::make('available_credit')
->label('باقیمانده')
->numeric()
->formatStateUsing(fn ($state) => number_format($state) . ' ریال')
->color(fn ($state) => $state > 0 ? 'success' : 'danger')
->sortable(),
])
->filters([
Tables\Filters\SelectFilter::make('has_credit')
->label('وضعیت اعتبار')
->options([
'has_limit' => 'دارای سقف اعتبار',
'no_limit' => 'بدون سقف اعتبار',
'has_used' => 'استفاده شده',
'has_available' => 'باقیمانده',
])
->query(function ($query, $filter) {
match ($filter->getState()) {
'has_limit' => $query->where('credit_limit', '>', 0),
'no_limit' => $query->where('credit_limit', 0),
'has_used' => $query->where('credit_used', '>', 0),
'has_available' => $query->whereRaw('credit_limit - credit_used > 0'),
};
}),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\Action::make('add_credit')
->label('افزایش اعتبار')
->icon('heroicon-o-plus-circle')
->color('success')
->form([
Forms\Components\TextInput::make('amount')
->label('مبلغ (ریال)')
->numeric()
->required()
->minValue(1),
Forms\Components\TextInput::make('description')
->label('توضیحات')
->required(),
])
->action(function ($record, array $data): void {
DB::beginTransaction();
try {
$record->credit_limit += $data['amount'];
$record->save();
DB::table('wallet_transactions')->insert([
'user_id' => $record->id,
'type' => 'credit_add',
'amount' => $data['amount'],
'description' => $data['description'],
'created_at' => now(),
'updated_at' => now(),
]);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
}),
Tables\Actions\Action::make('reduce_credit')
->label('کاهش اعتبار')
->icon('heroicon-o-minus-circle')
->color('danger')
->form([
Forms\Components\TextInput::make('amount')
->label('مبلغ (ریال)')
->numeric()
->required()
->minValue(1),
Forms\Components\TextInput::make('description')
->label('توضیحات')
->required(),
])
->action(function ($record, array $data): void {
DB::beginTransaction();
try {
$record->credit_limit -= $data['amount'];
$record->save();
DB::table('wallet_transactions')->insert([
'user_id' => $record->id,
'type' => 'credit_reduce',
'amount' => $data['amount'],
'description' => $data['description'],
'created_at' => now(),
'updated_at' => now(),
]);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
}),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return [
'index' => Pages\ListCustomerCredits::route('/'),
'edit' => Pages\EditCustomerCredit::route('/{record}/edit'),
];
}
public static function getNavigationBadge(): ?string
{
return static::getModel()::where('credit_limit', '>', 0)->count();
}
}