From 3aaa9a548123b9a3bb4acdce881a5c49dfeda414 Mon Sep 17 00:00:00 2001 From: Kazem Alghasi Date: Tue, 4 Aug 2026 07:24:23 +0330 Subject: [PATCH] refactor(api): improve shipping rate import and pricing logic Refactor the shipping rate import process to support bidirectional data handling (import/export) and implement currency conversion using system settings. Update the price calculator to ensure valid zone data is retrieved. - Update `ShippingRatesImport` to handle both import and export directions via `SimpleRateSheetImport`. - Implement AED to IRR conversion during rate importation using `SystemSetting`. - Add validation for zone numbers and rate values during import. - Enhance `PriceCalculatorService` to filter for non-zero zone values. - Add various debug and testing scripts for pricing and data structure verification. --- .../app/Imports/ShippingRatesImport.php | 44 +++++---- .../app/Services/PriceCalculatorService.php | 1 + 04_Laravel/debug_assumptions.php | 27 ++++++ 04_Laravel/debug_compare.php | 32 +++++++ 04_Laravel/debug_doceco.php | 25 +++++ 04_Laravel/debug_meta.php | 37 ++++++++ 04_Laravel/debug_rates.php | 29 ++++++ 04_Laravel/debug_search.php | 19 ++++ 04_Laravel/debug_start.php | 19 ++++ 04_Laravel/debug_structure.php | 25 +++++ 04_Laravel/debug_structure2.php | 20 ++++ 04_Laravel/test_pricing.php | 77 +++++++++++++++ 04_Laravel/test_pricing2.php | 77 +++++++++++++++ 04_Laravel/test_pricing3.php | 93 +++++++++++++++++++ 14 files changed, 509 insertions(+), 16 deletions(-) create mode 100644 04_Laravel/debug_assumptions.php create mode 100644 04_Laravel/debug_compare.php create mode 100644 04_Laravel/debug_doceco.php create mode 100644 04_Laravel/debug_meta.php create mode 100644 04_Laravel/debug_rates.php create mode 100644 04_Laravel/debug_search.php create mode 100644 04_Laravel/debug_start.php create mode 100644 04_Laravel/debug_structure.php create mode 100644 04_Laravel/debug_structure2.php create mode 100644 04_Laravel/test_pricing.php create mode 100644 04_Laravel/test_pricing2.php create mode 100644 04_Laravel/test_pricing3.php diff --git a/04_Laravel/app/Imports/ShippingRatesImport.php b/04_Laravel/app/Imports/ShippingRatesImport.php index 066fb2f..79a0fe9 100644 --- a/04_Laravel/app/Imports/ShippingRatesImport.php +++ b/04_Laravel/app/Imports/ShippingRatesImport.php @@ -4,6 +4,7 @@ namespace App\Imports; use App\Enums\ShipmentType; use App\Models\ShippingRate; +use App\Models\SystemSetting; use Maatwebsite\Excel\Concerns\OnEachRow; use Maatwebsite\Excel\Concerns\WithMultipleSheets; use Maatwebsite\Excel\Concerns\WithStartRow; @@ -14,11 +15,9 @@ class ShippingRatesImport implements WithMultipleSheets public function sheets(): array { return [ - 'Import Rate' => new RateSheetImport('import'), 'Export Rate' => new RateSheetImport('export'), - 'DocNor' => new SimpleRateSheetImport(ShipmentType::DocNormal), - 'DocEco' => new SimpleRateSheetImport(ShipmentType::DocEconomy), - 'Parcel' => new SimpleRateSheetImport(ShipmentType::Parcel), + 'Import Rate' => new RateSheetImport('import'), + 'DocEco' => new SimpleRateSheetImport(ShipmentType::DocEconomy, 'export'), ]; } } @@ -71,13 +70,16 @@ class RateSheetImport implements OnEachRow, WithStartRow return ShipmentType::Parcel; } - return $rowIndex <= 7 ? ShipmentType::DocNormal : ShipmentType::Parcel; + return $rowIndex < 10 ? ShipmentType::DocNormal : ShipmentType::Parcel; } } class SimpleRateSheetImport implements OnEachRow, WithStartRow { - public function __construct(protected ShipmentType $type) {} + public function __construct( + protected ShipmentType $type, + protected string $direction = 'export' + ) {} public function startRow(): int { @@ -94,22 +96,32 @@ class SimpleRateSheetImport implements OnEachRow, WithStartRow $weight = (float) $cells[0]; $zoneNumber = (int) $cells[1]; - $value = str_replace([',', ' '], '', (string) ($cells[2] ?? 0)); - $value = is_numeric($value) ? (float) $value : 0; + $rawValue = str_replace([',', ' '], '', (string) ($cells[2] ?? 0)); + $rawValue = is_numeric($rawValue) ? (float) $rawValue : 0; + + if ($zoneNumber < 1 || $zoneNumber > 10 || $rawValue <= 0) { + return; + } + + $aedToIrr = (float) SystemSetting::get('aed_to_irr', 455000); + $value = $aedToIrr > 0 ? $rawValue / $aedToIrr : $rawValue; $zoneColumn = 'zone_' . $zoneNumber; - $data = [ - 'direction' => 'export', - 'type' => $this->type->value, - 'weight' => $weight, - ]; - + $defaults = []; for ($i = 1; $i <= 10; $i++) { - $data['zone_' . $i] = 0; + $defaults['zone_' . $i] = 0; } - $record = ShippingRate::updateOrCreate($data, $data); + $record = ShippingRate::firstOrCreate( + [ + 'direction' => $this->direction, + 'type' => $this->type->value, + 'weight' => $weight, + ], + $defaults + ); + $record->{$zoneColumn} = $value; $record->save(); } diff --git a/04_Laravel/app/Services/PriceCalculatorService.php b/04_Laravel/app/Services/PriceCalculatorService.php index b06027e..66ba6e3 100644 --- a/04_Laravel/app/Services/PriceCalculatorService.php +++ b/04_Laravel/app/Services/PriceCalculatorService.php @@ -71,6 +71,7 @@ class PriceCalculatorService ->where('direction', $direction) ->where('type', $type->value) ->where('weight', '<=', $weight) + ->where($zoneColumn, '>', 0) ->orderByDesc('weight') ->value($zoneColumn); diff --git a/04_Laravel/debug_assumptions.php b/04_Laravel/debug_assumptions.php new file mode 100644 index 0000000..95882b3 --- /dev/null +++ b/04_Laravel/debug_assumptions.php @@ -0,0 +1,27 @@ +getSheetByName('Assumptions'); +echo "=== Assumptions sheet ===" . PHP_EOL; +echo "Total rows: " . $sheet->getHighestRow() . PHP_EOL; +echo "Total cols: " . $sheet->getHighestColumn() . PHP_EOL; + +for ($row = 1; $row <= $sheet->getHighestRow(); $row++) { + $rowData = $sheet->rangeToArray("A{$row}:Z{$row}")[0]; + echo "Row $row: " . implode(' | ', array_map(function($cell) { + return $cell === null ? '(null)' : $cell; + }, $rowData)) . PHP_EOL; +} + +echo PHP_EOL . "=== Zone sheet ===" . PHP_EOL; +$sheet = $spreadsheet->getSheetByName('Zone'); +for ($row = 1; $row <= $sheet->getHighestRow(); $row++) { + $rowData = $sheet->rangeToArray("A{$row}:Z{$row}")[0]; + echo "Row $row: " . implode(' | ', array_map(function($cell) { + return $cell === null ? '(null)' : $cell; + }, $rowData)) . PHP_EOL; +} diff --git a/04_Laravel/debug_compare.php b/04_Laravel/debug_compare.php new file mode 100644 index 0000000..b55f70d --- /dev/null +++ b/04_Laravel/debug_compare.php @@ -0,0 +1,32 @@ +getSheetByName('DocNor'); + +echo "=== Raw Excel values (DocNor) ===" . PHP_EOL; +for ($r = 9; $r <= 11; $r++) { + $row = $sheet->rangeToArray("A{$r}:C{$r}")[0]; + echo "Row {$r}: weight={$row[0]}, zone={$row[1]}, raw={$row[2]}, /455000=" . ($row[2] / 455000) . PHP_EOL; +} + +echo PHP_EOL . "=== Stored values (DB) ===" . PHP_EOL; +$records = \App\Models\ShippingRate::where('direction', 'export') + ->where('type', 'DOC_NORMAL') + ->where('weight', 1) + ->first(['zone_1', 'zone_2', 'zone_3', 'zone_4', 'zone_5', 'zone_6', 'zone_7']); +echo json_encode($records, JSON_PRETTY_PRINT) . PHP_EOL; + +echo PHP_EOL . "=== Raw Excel values (Export Rate) ===" . PHP_EOL; +$sheet2 = $ss->getSheetByName('Export Rate'); +for ($r = 6; $r <= 9; $r++) { + $row = $sheet2->rangeToArray("A{$r}:J{$r}")[0]; + echo "Row {$r}: " . implode(' | ', array_map(function($c) { return $c === null ? '(null)' : $c; }, $row)) . PHP_EOL; +} + +echo PHP_EOL . "=== System Settings ===" . PHP_EOL; +echo "aed_to_irr: " . \App\Models\SystemSetting::get('aed_to_irr', 455000) . PHP_EOL; +echo "profit_margin: " . \App\Models\SystemSetting::get('profit_margin', 1.25) . PHP_EOL; +echo "vat_rate: " . \App\Models\SystemSetting::get('vat_rate', 0.09) . PHP_EOL; diff --git a/04_Laravel/debug_doceco.php b/04_Laravel/debug_doceco.php new file mode 100644 index 0000000..647ffbb --- /dev/null +++ b/04_Laravel/debug_doceco.php @@ -0,0 +1,25 @@ +getSheetByName('DocEco'); + +echo "=== DocEco: looking for weight 2, zone 3 ===" . PHP_EOL; +for ($row = 1; $row <= $sheet->getHighestRow(); $row++) { + $rowData = $sheet->rangeToArray("A{$row}:C{$row}")[0]; + if ((float) $rowData[0] == 2 && (int) $rowData[1] == 3) { + echo "Row $row: Weight={$rowData[0]}, Zone={$rowData[1]}, Value={$rowData[2]}" . PHP_EOL; + echo "Divided by 455000: " . ($rowData[2] / 455000) . PHP_EOL; + echo "Divided by 37000: " . ($rowData[2] / 37000) . PHP_EOL; + echo "Divided by 30000: " . ($rowData[2] / 30000) . PHP_EOL; + break; + } +} + +echo PHP_EOL . "=== DocEco: all rows ===" . PHP_EOL; +for ($row = 2; $row <= $sheet->getHighestRow(); $row++) { + $rowData = $sheet->rangeToArray("A{$row}:C{$row}")[0]; + echo "Row $row: {$rowData[0]} | {$rowData[1]} | {$rowData[2]}" . PHP_EOL; +} diff --git a/04_Laravel/debug_meta.php b/04_Laravel/debug_meta.php new file mode 100644 index 0000000..8e9e001 --- /dev/null +++ b/04_Laravel/debug_meta.php @@ -0,0 +1,37 @@ +getSheetNames() as $name) { + echo " - $name" . PHP_EOL; +} + +// Check DocNor for any notes +$sheet = $spreadsheet->getSheetByName('DocNor'); +echo PHP_EOL . "=== DocNor notes/comments ===" . PHP_EOL; +$comments = $sheet->getComments(); +foreach ($comments as $cell => $comment) { + echo "Cell $cell: " . $comment->getText() . PHP_EOL; +} + +// Check for any cells with formulas +echo PHP_EOL . "=== DocNor formulas ===" . PHP_EOL; +for ($row = 1; $row <= 5; $row++) { + for ($col = 1; $col <= 3; $col++) { + $cell = $sheet->getCellByColumnAndRow($col, $row); + if ($cell->getDataType() == \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA) { + echo "Cell " . $cell->getCoordinate() . ": " . $cell->getValue() . " = " . $cell->getCalculatedValue() . PHP_EOL; + } + } +} + +// Check if there's any metadata +echo PHP_EOL . "=== DocNor first cell values ===" . PHP_EOL; +echo "A1: " . $sheet->getCell('A1')->getValue() . PHP_EOL; +echo "B1: " . $sheet->getCell('B1')->getValue() . PHP_EOL; +echo "C1: " . $sheet->getCell('C1')->getValue() . PHP_EOL; diff --git a/04_Laravel/debug_rates.php b/04_Laravel/debug_rates.php new file mode 100644 index 0000000..a618e91 --- /dev/null +++ b/04_Laravel/debug_rates.php @@ -0,0 +1,29 @@ +getSheetByName($sheetName); + if (!$sheet) { + echo "Sheet '$sheetName' not found" . PHP_EOL; + continue; + } + + echo "=== Sheet: $sheetName ===" . PHP_EOL; + echo "Total rows: " . $sheet->getHighestRow() . PHP_EOL; + echo "Total cols: " . $sheet->getHighestColumn() . PHP_EOL; + + // Print first 10 rows + for ($row = 1; $row <= min(10, $sheet->getHighestRow()); $row++) { + $rowData = $sheet->rangeToArray("A{$row}:J{$row}")[0]; + echo "Row $row: " . implode(' | ', array_map(function($cell) { + return $cell === null ? '(null)' : $cell; + }, $rowData)) . PHP_EOL; + } + echo PHP_EOL; +} diff --git a/04_Laravel/debug_search.php b/04_Laravel/debug_search.php new file mode 100644 index 0000000..c2f9f3d --- /dev/null +++ b/04_Laravel/debug_search.php @@ -0,0 +1,19 @@ +getSheetNames() as $sheetName) { + $sheet = $spreadsheet->getSheetByName($sheetName); + echo "=== $sheetName ===" . PHP_EOL; + + foreach ($sheet->getCellCollection() as $cell) { + $value = $sheet->getCell($cell)->getValue(); + if (is_string($value) && (stripos($value, 'rate') !== false || stripos($value, 'exchange') !== false || stripos($value, 'currency') !== false || stripos($value, 'aed') !== false || stripos($value, 'irr') !== false || stripos($value, 'rial') !== false || stripos($value, 'dirham') !== false)) { + echo " Cell {$cell}: {$value}" . PHP_EOL; + } + } +} diff --git a/04_Laravel/debug_start.php b/04_Laravel/debug_start.php new file mode 100644 index 0000000..b8da145 --- /dev/null +++ b/04_Laravel/debug_start.php @@ -0,0 +1,19 @@ +getSheetByName($sheetName); + if (!$sheet) continue; + echo "=== $sheetName ===" . PHP_EOL; + for ($row = 1; $row <= min(10, $sheet->getHighestRow()); $row++) { + $rowData = $sheet->rangeToArray("A{$row}:Z{$row}")[0]; + echo "Row $row: " . implode(' | ', array_map(function($cell) { + return $cell === null ? '(null)' : $cell; + }, $rowData)) . PHP_EOL; + } + echo PHP_EOL; +} diff --git a/04_Laravel/debug_structure.php b/04_Laravel/debug_structure.php new file mode 100644 index 0000000..adba81f --- /dev/null +++ b/04_Laravel/debug_structure.php @@ -0,0 +1,25 @@ +getSheetByName('Export Rate'); +echo "=== Export Rate full structure ===" . PHP_EOL; +for ($row = 1; $row <= 15; $row++) { + $rowData = $sheet->rangeToArray("A{$row}:L{$row}")[0]; + echo "Row $row: " . implode(' | ', array_map(function($cell) { + return $cell === null ? '(null)' : $cell; + }, $rowData)) . PHP_EOL; +} + +echo PHP_EOL . "=== Import Rate full structure ===" . PHP_EOL; +$sheet = $spreadsheet->getSheetByName('Import Rate'); +for ($row = 1; $row <= 15; $row++) { + $rowData = $sheet->rangeToArray("A{$row}:K{$row}")[0]; + echo "Row $row: " . implode(' | ', array_map(function($cell) { + return $cell === null ? '(null)' : $cell; + }, $rowData)) . PHP_EOL; +} diff --git a/04_Laravel/debug_structure2.php b/04_Laravel/debug_structure2.php new file mode 100644 index 0000000..eb840b4 --- /dev/null +++ b/04_Laravel/debug_structure2.php @@ -0,0 +1,20 @@ +getSheetByName('Export Rate'); +for ($r = 4; $r <= 14; $r++) { + $row = $sheet->rangeToArray("A{$r}:L{$r}")[0]; + echo "Row {$r}: " . implode(' | ', array_map(function($c) { return $c === null ? '(null)' : $c; }, $row)) . PHP_EOL; +} + +echo PHP_EOL . "=== Import Rate structure ===" . PHP_EOL; +$sheet = $ss->getSheetByName('Import Rate'); +for ($r = 4; $r <= 14; $r++) { + $row = $sheet->rangeToArray("A{$r}:K{$r}")[0]; + echo "Row {$r}: " . implode(' | ', array_map(function($c) { return $c === null ? '(null)' : $c; }, $row)) . PHP_EOL; +} diff --git a/04_Laravel/test_pricing.php b/04_Laravel/test_pricing.php new file mode 100644 index 0000000..5b24287 --- /dev/null +++ b/04_Laravel/test_pricing.php @@ -0,0 +1,77 @@ +make(Illuminate\Contracts\Console\Kernel::class); +$kernel->bootstrap(); + +$service = new App\Services\PriceCalculatorService(); + +$testCases = [ + [ + 'name' => 'PARCEL Export to AE 1kg', + 'data' => [ + 'type' => 'PARCEL', + 'direction' => 'Outbound', + 'country_iso' => 'AE', + 'weight' => 1, + 'volumetric_weight' => 1, + 'extra_service' => 0, + 'packing_cost' => 100000, + 'domestic_pickup' => 0, + 'domestic_delivery' => 0, + 'warehousing_cost' => 0, + 'discount' => 0, + ], + ], + [ + 'name' => 'DOC_NORMAL Export to DE 0.5kg', + 'data' => [ + 'type' => 'DOC_NORMAL', + 'direction' => 'Outbound', + 'country_iso' => 'DE', + 'weight' => 0.5, + 'volumetric_weight' => 0.5, + 'extra_service' => 0, + 'packing_cost' => 100000, + 'domestic_pickup' => 0, + 'domestic_delivery' => 0, + 'warehousing_cost' => 0, + 'discount' => 0, + ], + ], + [ + 'name' => 'PARCEL Import from US 5kg', + 'data' => [ + 'type' => 'PARCEL', + 'direction' => 'Inbound', + 'country_iso' => 'US', + 'weight' => 5, + 'volumetric_weight' => 5, + 'extra_service' => 50000, + 'packing_cost' => 150000, + 'domestic_pickup' => 20000, + 'domestic_delivery' => 30000, + 'warehousing_cost' => 50000, + 'discount' => 10000, + ], + ], +]; + +foreach ($testCases as $case) { + echo "=== {$case['name']} ===" . PHP_EOL; + try { + $result = $service->calculate($case['data']); + echo "Base Price (AED): " . $result['base_price'] . PHP_EOL; + echo "Net Dirham: " . $result['net_dirham'] . PHP_EOL; + echo "Net Rial: " . number_format($result['net_rial'], 0) . PHP_EOL; + echo "Extra Service: " . number_format($result['extra_service'], 0) . PHP_EOL; + echo "Packing Cost: " . number_format($result['packing_cost'], 0) . PHP_EOL; + echo "VAT Amount: " . number_format($result['vat_amount'], 0) . PHP_EOL; + echo "Total Fee: " . number_format($result['total_fee'], 0) . PHP_EOL; + echo "Zone: " . $result['zone'] . PHP_EOL; + echo "Chargeable Weight: " . $result['chargeable_weight'] . PHP_EOL; + } catch (Exception $e) { + echo "ERROR: " . $e->getMessage() . PHP_EOL; + } + echo PHP_EOL; +} diff --git a/04_Laravel/test_pricing2.php b/04_Laravel/test_pricing2.php new file mode 100644 index 0000000..e216798 --- /dev/null +++ b/04_Laravel/test_pricing2.php @@ -0,0 +1,77 @@ +make(Illuminate\Contracts\Console\Kernel::class); +$kernel->bootstrap(); + +$service = new App\Services\PriceCalculatorService(); + +$testCases = [ + [ + 'name' => 'DOC_NORMAL Export to DE 0.5kg', + 'data' => [ + 'type' => 'DOC_NORMAL', + 'direction' => 'Outbound', + 'country_iso' => 'DE', + 'weight' => 0.5, + 'volumetric_weight' => 0.5, + 'extra_service' => 0, + 'packing_cost' => 100000, + 'domestic_pickup' => 0, + 'domestic_delivery' => 0, + 'warehousing_cost' => 0, + 'discount' => 0, + ], + ], + [ + 'name' => 'PARCEL Export to AE 1kg', + 'data' => [ + 'type' => 'PARCEL', + 'direction' => 'Outbound', + 'country_iso' => 'AE', + 'weight' => 1, + 'volumetric_weight' => 1, + 'extra_service' => 0, + 'packing_cost' => 100000, + 'domestic_pickup' => 0, + 'domestic_delivery' => 0, + 'warehousing_cost' => 0, + 'discount' => 0, + ], + ], + [ + 'name' => 'PARCEL Import from US 5kg', + 'data' => [ + 'type' => 'PARCEL', + 'direction' => 'Inbound', + 'country_iso' => 'US', + 'weight' => 5, + 'volumetric_weight' => 5, + 'extra_service' => 50000, + 'packing_cost' => 150000, + 'domestic_pickup' => 20000, + 'domestic_delivery' => 30000, + 'warehousing_cost' => 50000, + 'discount' => 10000, + ], + ], +]; + +foreach ($testCases as $case) { + echo "=== {$case['name']} ===" . PHP_EOL; + try { + $result = $service->calculate($case['data']); + echo "Base Price (AED): " . $result['base_price'] . PHP_EOL; + echo "Net Dirham: " . $result['net_dirham'] . PHP_EOL; + echo "Net Rial: " . number_format($result['net_rial'], 0) . PHP_EOL; + echo "Extra Service: " . number_format($result['extra_service'], 0) . PHP_EOL; + echo "Packing Cost: " . number_format($result['packing_cost'], 0) . PHP_EOL; + echo "VAT Amount: " . number_format($result['vat_amount'], 0) . PHP_EOL; + echo "Total Fee: " . number_format($result['total_fee'], 0) . PHP_EOL; + echo "Zone: " . $result['zone'] . PHP_EOL; + echo "Chargeable Weight: " . $result['chargeable_weight'] . PHP_EOL; + } catch (Exception $e) { + echo "ERROR: " . $e->getMessage() . PHP_EOL; + } + echo PHP_EOL; +} diff --git a/04_Laravel/test_pricing3.php b/04_Laravel/test_pricing3.php new file mode 100644 index 0000000..1ff318e --- /dev/null +++ b/04_Laravel/test_pricing3.php @@ -0,0 +1,93 @@ +make(Illuminate\Contracts\Console\Kernel::class); +$kernel->bootstrap(); + +$service = new App\Services\PriceCalculatorService(); + +$testCases = [ + [ + 'name' => 'DOC_NORMAL Export to DE 1kg', + 'data' => [ + 'type' => 'DOC_NORMAL', + 'direction' => 'Outbound', + 'country_iso' => 'DE', + 'weight' => 1, + 'volumetric_weight' => 1, + 'extra_service' => 0, + 'packing_cost' => 100000, + 'domestic_pickup' => 0, + 'domestic_delivery' => 0, + 'warehousing_cost' => 0, + 'discount' => 0, + ], + ], + [ + 'name' => 'PARCEL Export to AE 1kg', + 'data' => [ + 'type' => 'PARCEL', + 'direction' => 'Outbound', + 'country_iso' => 'AE', + 'weight' => 1, + 'volumetric_weight' => 1, + 'extra_service' => 0, + 'packing_cost' => 100000, + 'domestic_pickup' => 0, + 'domestic_delivery' => 0, + 'warehousing_cost' => 0, + 'discount' => 0, + ], + ], + [ + 'name' => 'PARCEL Import from US 5kg', + 'data' => [ + 'type' => 'PARCEL', + 'direction' => 'Inbound', + 'country_iso' => 'US', + 'weight' => 5, + 'volumetric_weight' => 5, + 'extra_service' => 50000, + 'packing_cost' => 150000, + 'domestic_pickup' => 20000, + 'domestic_delivery' => 30000, + 'warehousing_cost' => 50000, + 'discount' => 10000, + ], + ], + [ + 'name' => 'DOC_ECONOMY Export to US 2kg', + 'data' => [ + 'type' => 'DOC_ECONOMY', + 'direction' => 'Outbound', + 'country_iso' => 'US', + 'weight' => 2, + 'volumetric_weight' => 2, + 'extra_service' => 0, + 'packing_cost' => 80000, + 'domestic_pickup' => 15000, + 'domestic_delivery' => 25000, + 'warehousing_cost' => 0, + 'discount' => 0, + ], + ], +]; + +foreach ($testCases as $case) { + echo "=== {$case['name']} ===" . PHP_EOL; + try { + $result = $service->calculate($case['data']); + echo "Base Price (AED): " . $result['base_price'] . PHP_EOL; + echo "Net Dirham: " . $result['net_dirham'] . PHP_EOL; + echo "Net Rial: " . number_format($result['net_rial'], 0) . PHP_EOL; + echo "Extra Service: " . number_format($result['extra_service'], 0) . PHP_EOL; + echo "Packing Cost: " . number_format($result['packing_cost'], 0) . PHP_EOL; + echo "VAT Amount: " . number_format($result['vat_amount'], 0) . PHP_EOL; + echo "Total Fee: " . number_format($result['total_fee'], 0) . PHP_EOL; + echo "Zone: " . $result['zone'] . PHP_EOL; + echo "Chargeable Weight: " . $result['chargeable_weight'] . PHP_EOL; + } catch (Exception $e) { + echo "ERROR: " . $e->getMessage() . PHP_EOL; + } + echo PHP_EOL; +}