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.
243 lines
10 KiB
PHP
243 lines
10 KiB
PHP
<?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();
|
||
}
|
||
}
|