new RateSheetImport('export'), 'Import Rate' => new RateSheetImport('import'), 'DocEco' => new SimpleRateSheetImport(ShipmentType::DocEconomy, 'export'), ]; } } 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 { return 5; } public function onRow(Row $row) { $rowIndex = $row->getIndex(); $cells = $row->toArray(); if (empty($cells[0]) || !is_numeric($cells[0])) { $this->skipped++; return; } 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; } 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++; } } public function getStats(): array { return [ 'imported' => $this->imported, 'skipped' => $this->skipped, 'errors' => $this->errors, ]; } private function resolveType(int $rowIndex, array $cells): ShipmentType { $header = strtoupper((string) ($cells[3] ?? '')); if (str_contains($header, 'DOCUMENT') || str_contains($header, 'DOC')) { return ShipmentType::DocNormal; } if (str_contains($header, 'NON') || str_contains($header, 'PARCEL')) { return ShipmentType::Parcel; } return $rowIndex < 10 ? ShipmentType::DocNormal : ShipmentType::Parcel; } } 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' ) {} public function startRow(): int { return 2; } public function onRow(Row $row) { $cells = $row->toArray(); if (empty($cells[0]) || !is_numeric($cells[0])) { $this->skipped++; return; } try { $weight = (float) $cells[0]; $zoneNumber = (int) $cells[1]; $rawValue = str_replace([',', ' '], '', (string) ($cells[2] ?? 0)); $value = is_numeric($rawValue) ? (float) $rawValue : 0; if ($zoneNumber < 1 || $zoneNumber > 10 || $value <= 0) { $this->skipped++; return; } $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++; } } public function getStats(): array { return [ 'imported' => $this->imported, 'skipped' => $this->skipped, 'errors' => $this->errors, ]; } }