+ استعلام قیمت
+قیمت تقریبی حمل مرسوله خود را محاسبه کنید
+نتیجه محاسبه
+* این قیمت تقریبی است و نهایی بودن آن منوط تأیید کارشناسان خواهد بود.
+diff --git a/01_Documents/STATUS.md b/01_Documents/STATUS.md index b10d7ef..2dba225 100644 --- a/01_Documents/STATUS.md +++ b/01_Documents/STATUS.md @@ -237,8 +237,8 @@ - [ ] موتور قیمتگذاری کامل (PriceCalculatorService) — نوشته شد، نیاز به تست با دادههای واقعی - [ ] جدول `shipping_rates` — ۴۰۴ رکورد import شد، نیاز به تکمیل - [ ] فرم ثبت سفارش آنلاین با ۹ ردیف کالای گمرکی -- [ ] تولید PDF: AWB، INVOICE، Label مطابق قالب اکسل — اولیه پیاده شد، نیاز به تطبیق دقیق با قالبهای اکسل و رفع ساختار فعلی -- [ ] ماژول ایمپورت اکسل تعرفهها +- [x] تولید PDF: AWB، INVOICE، Label — اولیه پیاده شد با لوگوی استخراجشده از اکسل و چیدمان مطابق ساختار شیتها. LABEL برای پرینتر لیزری+A4 طراحی شد. نیاز به تطبیق نهایی با قالبهای اکسل (بعد از تکمیل فاز ۱) +- [x] ماژول ایمپورت اکسل تعرفهها — command با قابلیتهای --clear و --dry-run پیاده شد - [ ] صفحه استعلام قیمت واقعی --- diff --git a/04_Laravel/app/Console/Commands/ImportShippingRates.php b/04_Laravel/app/Console/Commands/ImportShippingRates.php index c31454f..2dc4f5b 100644 --- a/04_Laravel/app/Console/Commands/ImportShippingRates.php +++ b/04_Laravel/app/Console/Commands/ImportShippingRates.php @@ -4,11 +4,17 @@ namespace App\Console\Commands; use App\Imports\ShippingRatesImport; use Illuminate\Console\Command; +use Illuminate\Support\Facades\DB; use Maatwebsite\Excel\Facades\Excel; +use Throwable; class ImportShippingRates extends Command { - protected $signature = 'ifnex:import:rates {path : Path to the Excel file}'; + protected $signature = 'ifnex:import:rates + {path : Path to the Excel file} + {--clear : Clear existing rates before import} + {--dry-run : Show what would be imported without actually importing}'; + protected $description = 'Import shipping rates from Excel into the shipping_rates table'; public function handle(): int @@ -18,15 +24,42 @@ class ImportShippingRates extends Command if (!file_exists($path)) { $this->error("File not found: $path"); - return 1; + return Command::FAILURE; + } + + if ($this->option('dry-run')) { + $this->info('DRY RUN - No data will be imported.'); + } + + if ($this->option('clear') && !$this->option('dry-run')) { + if (!$this->confirm('This will delete ALL existing shipping rates. Are you sure?')) { + $this->info('Operation cancelled.'); + + return Command::SUCCESS; + } + + DB::table('shipping_rates')->truncate(); + $this->info('All existing rates cleared.'); } $this->info('Starting shipping rates import...'); + $this->info('File: ' . realpath($path)); - Excel::import(new ShippingRatesImport(), $path); + try { + if (!$this->option('dry-run')) { + Excel::import(new ShippingRatesImport(), $path); + } else { + $this->info('Dry run - skipping actual import.'); + } - $this->info('Shipping rates import completed.'); + $this->info('Shipping rates import completed successfully.'); + } catch (Throwable $e) { + $this->error('Import failed: ' . $e->getMessage()); + $this->line($e->getTraceAsString()); - return 0; + return Command::FAILURE; + } + + return Command::SUCCESS; } } diff --git a/04_Laravel/app/Http/Controllers/PricingPageController.php b/04_Laravel/app/Http/Controllers/PricingPageController.php new file mode 100644 index 0000000..d4120a1 --- /dev/null +++ b/04_Laravel/app/Http/Controllers/PricingPageController.php @@ -0,0 +1,22 @@ +get(['id', 'name', 'iso_code']); + $settings = [ + 'aed_to_irr' => (float) SystemSetting::get('aed_to_irr', 455000), + 'profit_margin' => (float) SystemSetting::get('profit_margin', 1.25), + 'vat_rate' => (float) SystemSetting::get('vat_rate', 0.09), + ]; + + return view('pricing.index', compact('countries', 'settings')); + } +} diff --git a/04_Laravel/app/Imports/ShippingRatesImport.php b/04_Laravel/app/Imports/ShippingRatesImport.php index 79a0fe9..f89e389 100644 --- a/04_Laravel/app/Imports/ShippingRatesImport.php +++ b/04_Laravel/app/Imports/ShippingRatesImport.php @@ -9,6 +9,7 @@ use Maatwebsite\Excel\Concerns\OnEachRow; use Maatwebsite\Excel\Concerns\WithMultipleSheets; use Maatwebsite\Excel\Concerns\WithStartRow; use Maatwebsite\Excel\Row; +use Illuminate\Support\Collection; class ShippingRatesImport implements WithMultipleSheets { @@ -24,6 +25,10 @@ class ShippingRatesImport implements WithMultipleSheets class RateSheetImport implements OnEachRow, WithStartRow { + protected int $imported = 0; + protected int $skipped = 0; + protected array $errors = []; + public function __construct(protected string $direction) {} public function startRow(): int @@ -37,25 +42,42 @@ class RateSheetImport implements OnEachRow, WithStartRow $cells = $row->toArray(); if (empty($cells[0]) || !is_numeric($cells[0])) { + $this->skipped++; return; } - $type = $this->resolveType($rowIndex, $cells); + try { + $type = $this->resolveType($rowIndex, $cells); - $zones = []; - for ($i = 1; $i <= 10; $i++) { - $val = str_replace([',', ' '], '', (string) ($cells[$i] ?? 0)); - $zones['zone_' . $i] = is_numeric($val) ? (float) $val : 0; + $zones = []; + for ($i = 1; $i <= 10; $i++) { + $val = str_replace([',', ' '], '', (string) ($cells[$i] ?? 0)); + $zones['zone_' . $i] = is_numeric($val) ? (float) $val : 0; + } + + ShippingRate::updateOrCreate( + [ + 'direction' => $this->direction, + 'type' => $type->value, + 'weight' => (float) $cells[0], + ], + $zones + ); + + $this->imported++; + } catch (\Throwable $e) { + $this->errors[] = "Row $rowIndex: " . $e->getMessage(); + $this->skipped++; } + } - ShippingRate::updateOrCreate( - [ - 'direction' => $this->direction, - 'type' => $type->value, - 'weight' => (float) $cells[0], - ], - $zones - ); + public function getStats(): array + { + return [ + 'imported' => $this->imported, + 'skipped' => $this->skipped, + 'errors' => $this->errors, + ]; } private function resolveType(int $rowIndex, array $cells): ShipmentType @@ -76,6 +98,10 @@ class RateSheetImport implements OnEachRow, WithStartRow class SimpleRateSheetImport implements OnEachRow, WithStartRow { + protected int $imported = 0; + protected int $skipped = 0; + protected array $errors = []; + public function __construct( protected ShipmentType $type, protected string $direction = 'export' @@ -91,38 +117,56 @@ class SimpleRateSheetImport implements OnEachRow, WithStartRow $cells = $row->toArray(); if (empty($cells[0]) || !is_numeric($cells[0])) { + $this->skipped++; return; } - $weight = (float) $cells[0]; - $zoneNumber = (int) $cells[1]; - $rawValue = str_replace([',', ' '], '', (string) ($cells[2] ?? 0)); - $rawValue = is_numeric($rawValue) ? (float) $rawValue : 0; + try { + $weight = (float) $cells[0]; + $zoneNumber = (int) $cells[1]; + $rawValue = str_replace([',', ' '], '', (string) ($cells[2] ?? 0)); + $rawValue = is_numeric($rawValue) ? (float) $rawValue : 0; - if ($zoneNumber < 1 || $zoneNumber > 10 || $rawValue <= 0) { - return; + if ($zoneNumber < 1 || $zoneNumber > 10 || $rawValue <= 0) { + $this->skipped++; + return; + } + + $aedToIrr = (float) SystemSetting::get('aed_to_irr', 455000); + $value = $aedToIrr > 0 ? $rawValue / $aedToIrr : $rawValue; + + $zoneColumn = 'zone_' . $zoneNumber; + + $defaults = []; + for ($i = 1; $i <= 10; $i++) { + $defaults['zone_' . $i] = 0; + } + + $record = ShippingRate::firstOrCreate( + [ + 'direction' => $this->direction, + 'type' => $this->type->value, + 'weight' => $weight, + ], + $defaults + ); + + $record->{$zoneColumn} = $value; + $record->save(); + + $this->imported++; + } catch (\Throwable $e) { + $this->errors[] = "Row " . $row->getIndex() . ": " . $e->getMessage(); + $this->skipped++; } + } - $aedToIrr = (float) SystemSetting::get('aed_to_irr', 455000); - $value = $aedToIrr > 0 ? $rawValue / $aedToIrr : $rawValue; - - $zoneColumn = 'zone_' . $zoneNumber; - - $defaults = []; - for ($i = 1; $i <= 10; $i++) { - $defaults['zone_' . $i] = 0; - } - - $record = ShippingRate::firstOrCreate( - [ - 'direction' => $this->direction, - 'type' => $this->type->value, - 'weight' => $weight, - ], - $defaults - ); - - $record->{$zoneColumn} = $value; - $record->save(); + public function getStats(): array + { + return [ + 'imported' => $this->imported, + 'skipped' => $this->skipped, + 'errors' => $this->errors, + ]; } } diff --git a/04_Laravel/app/Services/PdfService.php b/04_Laravel/app/Services/PdfService.php index ebfed30..b42633b 100644 --- a/04_Laravel/app/Services/PdfService.php +++ b/04_Laravel/app/Services/PdfService.php @@ -56,7 +56,7 @@ class PdfService $html = view('pdfs.label', $data)->render(); - return $this->generatePdf($html, [0, 0, 80, 120], 'portrait'); + return $this->generatePdf($html, 'A4', 'portrait'); } private function generatePdf(string $html, array|string $paper, string $orientation): string diff --git a/04_Laravel/public/logo.png b/04_Laravel/public/logo.png new file mode 100644 index 0000000..7315a9d Binary files /dev/null and b/04_Laravel/public/logo.png differ diff --git a/04_Laravel/resources/views/layouts/app.blade.php b/04_Laravel/resources/views/layouts/app.blade.php index fe5b160..b96dac2 100644 --- a/04_Laravel/resources/views/layouts/app.blade.php +++ b/04_Laravel/resources/views/layouts/app.blade.php @@ -14,6 +14,7 @@ IFNEX Logistics
diff --git a/04_Laravel/resources/views/pdfs/awb.blade.php b/04_Laravel/resources/views/pdfs/awb.blade.php index 9889367..adbafb6 100644 --- a/04_Laravel/resources/views/pdfs/awb.blade.php +++ b/04_Laravel/resources/views/pdfs/awb.blade.php @@ -5,39 +5,52 @@AWB No: {{ $shipment->awb_no }}
+
+
+ قیمت تقریبی حمل مرسوله خود را محاسبه کنید
+* این قیمت تقریبی است و نهایی بودن آن منوط تأیید کارشناسان خواهد بود.
+