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.
199 lines
7.3 KiB
PHP
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();
|
|
}
|
|
}
|
|
}
|