ifnex/04_Laravel/app/Filament/Pages/BulkTrackingImport.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

199 lines
7.3 KiB
PHP

<?php
namespace App\Filament\Pages;
use App\Models\Shipment;
use App\Enums\ShipmentStatus;
use Filament\Forms;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Support\Facades\DB;
class BulkTrackingImport extends Page implements HasForms
{
use InteractsWithForms;
protected static ?string $navigationIcon = 'heroicon-o-arrow-up-tray';
protected static ?string $navigationGroup = 'مدیریت عملیات';
protected static ?string $navigationLabel = 'ایمپورت گروهی ترکینگ';
protected static ?string $title = 'ایمپورت گروهی وضعیت ترکینگ';
protected static ?int $navigationSort = 20;
protected static string $view = 'filament.pages.bulk-tracking-import';
public ?array $data = [];
public function mount(): void
{
$this->form->fill([
'csv_file' => null,
'status' => 'in_transit',
'description' => '',
]);
}
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('فایل CSV')
->schema([
Forms\Components\FileUpload::make('csv_file')
->label('فایل CSV')
->acceptedFileTypes(['text/csv', 'text/plain'])
->maxSize(10240) // 10MB
->required()
->helperText('فرمت: awb_no,status,description (هر سطر یک سفارش)'),
]),
Forms\Components\Section::make('تنظیمات')
->schema([
Forms\Components\Select::make('status')
->label('وضعیت جدید')
->options(collect(ShipmentStatus::cases())->mapWithKeys(fn ($status) => [$status->value => $status->label()])->toArray())
->default('in_transit')
->required(),
Forms\Components\TextInput::make('description')
->label('توضیحات پیش‌فرض')
->placeholder('مثال: به‌روزرسانی گروهی وضعیت ترکینگ')
->default('ایمپورت گروهی وضعیت'),
]),
Forms\Components\Section::make('پیش‌نمایش')
->schema([
Forms\Components\Placeholder::make('preview')
->label('پیش‌نمایش فایل')
->content(fn () => $this->getPreviewContent()),
])
->collapsible(),
])
->statePath('data');
}
private function getPreviewContent(): string
{
$file = $this->data['csv_file'] ?? null;
if (!$file) {
return 'فایلی انتخاب نشده است. لطفاً فایل CSV را آپلود کنید.';
}
try {
$path = is_array($file) ? ($file['path'] ?? reset($file)) : $file;
$content = file_get_contents($path);
$lines = explode("\n", trim($content));
$preview = "<div style='font-family: monospace; font-size: 12px; background: #f5f5f5; padding: 10px; border-radius: 5px;'>";
$preview .= "<strong>خط اول (هدر):</strong> " . e($lines[0] ?? '') . "<br>";
$preview .= "<strong>تعداد خطوط:</strong> " . count($lines) . "<br>";
if (isset($lines[1])) {
$preview .= "<br><strong>نمونه (خط دوم):</strong> " . e($lines[1]);
}
$preview .= "</div>";
return $preview;
} catch (\Exception $e) {
return 'خطا در خواندن فایل: ' . $e->getMessage();
}
}
public function submit(): void
{
$data = $this->form->getState();
$file = $data['csv_file'];
$path = is_array($file) ? ($file['path'] ?? reset($file)) : $file;
$content = file_get_contents($path);
$lines = explode("\n", trim($content));
// حذف خط اول (هدر)
$dataLines = array_slice($lines, 1);
$successCount = 0;
$errorCount = 0;
$errors = [];
DB::beginTransaction();
try {
foreach ($dataLines as $lineNumber => $line) {
$line = trim($line);
if (empty($line)) continue;
$parts = str_getcsv($line);
if (count($parts) < 1) {
$errors[] = "خط " . ($lineNumber + 2) . ": فرمت نادرست";
$errorCount++;
continue;
}
$awbNo = $parts[0];
$status = $parts[1] ?? $data['status'];
$description = $parts[2] ?? $data['description'];
// بررسی وجود سفارش
$shipment = Shipment::where('awb_no', $awbNo)->first();
if (!$shipment) {
$errors[] = "خط " . ($lineNumber + 2) . ": سفارش {$awbNo} یافت نشد";
$errorCount++;
continue;
}
// به‌روزرسانی وضعیت
$shipment->update([
'status' => $status,
'tracking_description' => $description,
]);
// ثبت تاریخچه
DB::table('tracking_histories')->insert([
'shipment_id' => $shipment->id,
'status' => $status,
'description' => $description,
'location' => 'سیستم ایمپورت گروهی',
'created_at' => now(),
'updated_at' => now(),
]);
$successCount++;
}
DB::commit();
Notification::make()
->title('ایمپورت با موفقیت انجام شد')
->body("{$successCount} سفارش با موفقیت به‌روزرسانی شد. {$errorCount} خطا رخ داد.")
->success()
->send();
if (!empty($errors)) {
Notification::make()
->title('خطاها')
->body(implode("\n", array_slice($errors, 0, 10)))
->warning()
->send();
}
$this->form->fill([
'csv_file' => null,
'status' => 'in_transit',
'description' => '',
]);
} catch (\Exception $e) {
DB::rollBack();
Notification::make()
->title('خطا در ایمپورت')
->body($e->getMessage())
->danger()
->send();
}
}
}