rows ?? collect(); $created = 0; $updated = 0; foreach ($rows as $row) { /* * Normaliza as células da linha. */ $cells = collect($row) ->map(fn ($value) => trim((string) $value)) ->values(); /* * Ignora linha completamente vazia. */ if ($cells->filter(fn ($value) => $value !== '')->isEmpty()) { continue; } /* * Estrutura esperada do Excel: * * Coluna 0 = Nome do exame * Coluna 1 = Preço */ $name = $cells->get(0, ''); $price = $cells->get(1, ''); /* * Ignora linhas sem nome. */ if ($name === '') { continue; } /* * Trata cabeçalhos do Excel. * * Exemplos: * EXAME | VALOR * NOME | PREÇO * NOME DO EXAME | PREÇO */ $nameHeader = mb_strtoupper($name); $priceHeader = mb_strtoupper($price); $isHeader = $nameHeader === 'EXAME' || $nameHeader === 'NOME' || ( str_contains($nameHeader, 'EXAME') && ( str_contains($priceHeader, 'VALOR') || str_contains($priceHeader, 'PREÇO') || str_contains($priceHeader, 'PRECO') ) ); if ($isHeader) { continue; } /* * Converte o preço para o formato numérico. */ $associatePrice = $this->parsePrice($price); /* * Procura um exame já cadastrado para esse parceiro. * * O nome é comparado ignorando: * - maiúsculas/minúsculas * - espaços no início/fim */ $service = PartnerAgreementService::withTrashed() ->where('partner_agreement_id', $partnerAgreementId) ->whereRaw( 'LOWER(TRIM(name)) = LOWER(TRIM(?))', [$name] ) ->first(); /* * EXAME JÁ EXISTE */ if ($service) { /* * Se estava excluído logicamente, restaura. */ if ($service->trashed()) { $service->restore(); } $changed = false; /* * Atualiza o preço somente quando * o Excel trouxe um preço válido. */ if ( $associatePrice !== null && (float) $service->associate_price !== (float) $associatePrice ) { $service->associate_price = $associatePrice; $changed = true; } /* * Garante que o serviço seja do tipo EXAME. */ if ( $service->type !== PartnerAgreementServiceTypeEnum::EXAME ) { $service->type = PartnerAgreementServiceTypeEnum::EXAME; $changed = true; } /* * Garante que o exame fique ativo. */ if ( $service->status !== PartnerAgreementServiceStatusEnum::ACTIVE ) { $service->status = PartnerAgreementServiceStatusEnum::ACTIVE; $changed = true; } if ($changed) { $service->save(); $updated++; } continue; } /* * EXAME NÃO EXISTE * * Cria um novo serviço vinculado * ao parceiro informado. */ PartnerAgreementService::create([ 'partner_agreement_id' => $partnerAgreementId, 'name' => $name, 'associate_price' => $associatePrice, 'type' => PartnerAgreementServiceTypeEnum::EXAME, 'status' => PartnerAgreementServiceStatusEnum::ACTIVE, ]); $created++; } return [ 'total' => $rows->count(), 'created' => $created, 'updated' => $updated, ]; } /** * Converte diferentes formatos de preço * para float. */ private function parsePrice(mixed $value): ?float { if ($value === null || $value === '') { return null; } $value = trim((string) $value); /* * Remove moeda e espaços. * * Exemplos: * R$ 50,00 -> 50,00 * R$50,00 -> 50,00 */ $value = str_replace(['R$', ' '], '', $value); /* * Formato brasileiro: * * 50,00 * 1.250,50 */ if (str_contains($value, ',')) { $value = str_replace('.', '', $value); $value = str_replace(',', '.', $value); } /* * Formato decimal: * * 50 * 50.00 * 1250.50 */ return is_numeric($value) ? (float) $value : null; } }