| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- <?php
- namespace App\Services;
- use App\Enums\PartnerAgreementServiceStatusEnum;
- use App\Enums\PartnerAgreementServiceTypeEnum;
- use App\Imports\ExamesImport;
- use App\Models\PartnerAgreementService;
- use Maatwebsite\Excel\Facades\Excel;
- class ExamesImportService
- {
- public function syncFromExcel(
- string $filePath,
- int $partnerAgreementId
- ): array {
- $import = new ExamesImport();
- Excel::import($import, $filePath);
- $rows = $import->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 = Código do exame
- * Coluna 1 = Nome do exame
- * Coluna 2 = Preço
- */
- $codeExams = $cells->get(0, '');
- $name = $cells->get(1, '');
- $price = $cells->get(2, '');
- /*
- * Ignora linhas sem nome.
- */
- if ($name === '') {
- continue;
- }
- /*
- * Trata cabeçalhos do Excel.
- */
- $codeHeader = mb_strtoupper($codeExams);
- $nameHeader = mb_strtoupper($name);
- $priceHeader = mb_strtoupper($price);
- $isHeader =
- str_contains($codeHeader, 'CÓDIGO') ||
- str_contains($codeHeader, 'CODIGO') ||
- $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.
- *
- * Primeiro tenta localizar pelo código quando
- * o código foi informado.
- */
- $service = null;
- if ($codeExams !== '') {
- $service = PartnerAgreementService::withTrashed()
- ->where('partner_agreement_id', $partnerAgreementId)
- ->where('code_exams', $codeExams)
- ->first();
- }
- /*
- * Caso não exista código ou não tenha encontrado pelo código,
- * procura pelo nome.
- */
- if (!$service) {
- $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 código quando informado.
- */
- if (
- $codeExams !== '' &&
- $service->code_exams !== $codeExams
- ) {
- $service->code_exams = $codeExams;
- $changed = true;
- }
- /*
- * 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,
- 'code_exams' => $codeExams !== '' ? $codeExams : null,
- '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;
- }
- }
|