with(['user', 'profileMedia']) ->join('users', 'providers.user_id', '=', 'users.id') ->select('providers.*') ->orderBy('users.name', 'asc') ->get(); return $providers; } public function findById(int $id): ?Provider { return Provider::with(['user', 'profileMedia', 'documentFrontMedia', 'documentBackMedia'])->find($id); } public function create(array $data): Provider { return DB::transaction(function () use ($data) { $provider = Provider::create($data); if (! empty(data_get($data, 'recipient_name')) && ! empty(data_get($data, 'recipient_default_bank_account'))) { $this->pagarmeRecipientService->createRecipientForProvider($provider, $data); } return $provider->fresh(['user', 'profileMedia']); }); } public function update(int $id, array $data): ?Provider { $model = $this->findById($id); if (! $model) { return null; } $wasAccepted = $model->approval_status === ApprovalStatusEnum::ACCEPTED; if (data_get($data, 'avatar') !== null && data_get($data, 'avatar') instanceof UploadedFile) { $media = $this->mediaService->replaceFile( newFile: data_get($data, 'avatar'), folder: "provider/avatar/{$model->id}", source: 'provider', sourceId: $model->id, old: $model->profileMedia, ); $data['profile_media_id'] = $media->id; unset($data['avatar']); } $model->update($data); $provider = $model->fresh(['user', 'profileMedia']); if (! $wasAccepted && $provider->approval_status === ApprovalStatusEnum::ACCEPTED) { $this->sendApprovedEmail($provider); } return $provider; } public function delete(int $id): bool { $model = $this->findById($id); if (! $model) { return false; } return $model->delete(); } // public function getPaymentMethods(int $id): array { $provider = Provider::find($id); if (! $provider || empty($provider->recipient_default_bank_account)) { return []; } $bankAccount = $provider->recipient_default_bank_account; return [[ 'id' => $provider->id, 'provider_id' => $provider->id, 'account_type' => 'bank_account', 'pix_key' => data_get($bankAccount, 'pix_key'), 'bank_account_type' => data_get($bankAccount, 'type'), 'agency' => data_get($bankAccount, 'branch_number'), 'account' => data_get($bankAccount, 'account_number'), 'digit' => data_get($bankAccount, 'account_check_digit'), 'recipient_default_bank_account' => $bankAccount, ]]; } public function getPending(int $page = 1, int $perPage = 10): LengthAwarePaginator { return Provider::query() ->where('approval_status', ApprovalStatusEnum::PENDING->value) ->with(['user', 'profileMedia']) ->orderBy('created_at', 'asc') ->paginate($perPage, ['*'], 'page', $page); } // public function register(array $data): array { try { DB::beginTransaction(); $email = data_get($data, 'email'); $phone = data_get($data, 'phone'); $code = data_get($data, 'code'); $user = User::query() ->where('type', UserTypeEnum::PROVIDER->value) ->where('code', $code) ->where(function ($query) use ($email, $phone) { if (! empty($email)) { $query->orWhere('email', $email); } if (! empty($phone)) { $query->orWhere('phone', $phone); } }) ->latest('id') ->first(); if (! $user) { throw new \Exception(__('messages.user_not_found_or_code_not_validated')); } $user->name = data_get($data, 'name'); if (empty($user->email) && ! empty($email)) { $user->email = $email; } if (empty($user->phone) && ! empty($phone)) { $user->phone = $phone; } $user->save(); $provider = Provider::withTrashed()->where('user_id', $user->id)->first(); if (! $provider) { $provider = new Provider; $provider->user_id = $user->id; } elseif ($provider->trashed()) { $provider->restore(); } $provider->rg = data_get($data, 'rg'); $provider->document = $this->sanitizeDigits(data_get($data, 'document')); $provider->birth_date = data_get($data, 'birth_date'); $provider->gender = data_get($data, 'gender'); $provider->daily_price_8h = data_get($data, 'daily_price_8h'); $provider->daily_price_6h = data_get($data, 'daily_price_6h'); $provider->daily_price_4h = data_get($data, 'daily_price_4h'); $provider->daily_price_2h = data_get($data, 'daily_price_2h'); $provider->approval_status = ApprovalStatusEnum::PENDING->value; $provider->save(); $provider->refresh(); $provider->load('profileMedia', 'documentFrontMedia', 'documentBackMedia'); $selfie = $this->mediaService->replaceFile( newFile: data_get($data, 'selfie'), folder: "provider/avatar/{$provider->id}", source: 'provider', sourceId: $provider->id, old: $provider->profileMedia, ); $provider->profile_media_id = $selfie->id; $front = $this->mediaService->replaceFile( newFile: data_get($data, 'document_front'), folder: "provider/documentos/{$provider->id}", source: 'provider_document', sourceId: $provider->id, old: $provider->documentFrontMedia, filename: 'frente.'.data_get($data, 'document_front')->getClientOriginalExtension(), ); $provider->document_front_media_id = $front->id; $back = $this->mediaService->replaceFile( newFile: data_get($data, 'document_back'), folder: "provider/documentos/{$provider->id}", source: 'provider_document', sourceId: $provider->id, old: $provider->documentBackMedia, filename: 'verso.'.data_get($data, 'document_back')->getClientOriginalExtension(), ); $provider->document_back_media_id = $back->id; $provider->save(); $bankAccount = data_get($data, 'recipient_default_bank_account', []); $hasBankData = ! empty(data_get($bankAccount, 'bank')) && ! empty(data_get($bankAccount, 'account_number')); if (! empty(data_get($data, 'recipient_name')) && $hasBankData && empty($provider->recipient_id)) { $this->pagarmeRecipientService->createRecipientForProvider($provider, $data); } Address::where('source', 'provider')->where('source_id', $provider->id)->delete(); $this->createProviderAddress($provider->id, $data); ProviderServicesType::where('provider_id', $provider->id)->delete(); $this->createProviderServicesTypes($provider->id, $data); ProviderWorkingDay::where('provider_id', $provider->id)->delete(); $this->createProviderWorkingDays($provider->id, $data); if ((empty($user->email) && empty($user->phone)) || empty($user->code)) { throw new \Exception(__('messages.user_not_found_or_code_not_validated')); } $user->registration_complete = true; $user->validated_code = true; $user->code = null; $user->save(); $result = $this->authService->createAppSession($user); DB::commit(); return $result; } catch (\Exception $e) { DB::rollBack(); Log::error('Erro ao cadastrar prestador: '.$e->getMessage(), [ 'data' => $data, ]); throw $e; } } public function updateBankAccount(int $id, array $bankAccountData): ?Provider { $provider = Provider::with(['user', 'addresses.city', 'addresses.state'])->find($id); if (! $provider) { return null; } $bankAccountData['holder_name'] = data_get($bankAccountData, 'holder_name', $provider->user?->name); $bankAccountData['holder_document'] = data_get($bankAccountData, 'holder_document', $provider->document); $bankAccountData['holder_type'] = data_get($bankAccountData, 'holder_type', 'individual'); if (empty($provider->recipient_id)) { $this->pagarmeRecipientService->createRecipientForProvider( $provider, $this->buildRecipientDataFromProvider($provider, $bankAccountData), ); } else { $this->pagarmeRecipientService->updateDefaultBankAccount($provider, $bankAccountData); } return $provider->fresh(['user', 'profileMedia']); } // public function approve(int $id): Provider { [$provider, $wasAccepted] = DB::transaction(function () use ($id) { $provider = Provider::query()->lockForUpdate()->findOrFail($id); $wasAccepted = $provider->approval_status === ApprovalStatusEnum::ACCEPTED; $provider->update([ 'approval_status' => ApprovalStatusEnum::ACCEPTED->value, 'selfie_verified' => true, ]); return [$provider->fresh(['user', 'profileMedia']), $wasAccepted]; }); if (! $wasAccepted) { $this->sendApprovedEmail($provider); } return $provider; } public function reject(int $id): Provider { return DB::transaction(function () use ($id) { $provider = Provider::findOrFail($id); $provider->update(['approval_status' => ApprovalStatusEnum::REJECTED->value]); return $provider->fresh(['user', 'profileMedia']); }); } // private function buildRecipientDataFromProvider(Provider $provider, array $bankAccountData): array { $address = $provider->addresses->first(); return [ 'recipient_name' => $provider->user?->name, 'recipient_email' => $provider->user?->email, 'recipient_document' => $provider->document, 'recipient_type' => 'individual', 'recipient_payment_mode' => 'bank_transfer', 'recipient_metadata' => [], 'recipient_default_bank_account' => $bankAccountData, 'birth_date' => $provider->birth_date, 'phone' => $provider->user?->phone, 'address' => $address?->address, 'number' => $address?->number, 'city' => $address?->city?->name, 'state' => $address?->state?->code, 'zip_code' => $address?->zip_code, ]; } private function createProviderAddress(int $providerId, array $data): void { $state = null; $city = null; if (! empty(data_get($data, 'state'))) { $state = State::query() ->whereRaw('LOWER(code) = ?', [mb_strtolower(data_get($data, 'state'))]) ->first(); } if (! empty(data_get($data, 'city'))) { $cityQuery = City::query() ->whereRaw('LOWER(name) = ?', [mb_strtolower(data_get($data, 'city'))]); if ($state) { $cityQuery->where('state_id', $state->id); } $city = $cityQuery->first(); } $address = new Address; $address->source = 'provider'; $address->source_id = $providerId; $address->zip_code = $this->sanitizeDigits(data_get($data, 'zip_code')); $address->address = data_get($data, 'address'); $address->number = data_get($data, 'number'); $address->district = data_get($data, 'district'); $address->has_complement = (bool) data_get($data, 'has_complement', false); $address->complement = data_get($data, 'complement'); $address->nickname = data_get($data, 'nickname'); $address->instructions = data_get($data, 'instructions'); $address->address_type = data_get($data, 'address_type', 'home'); $address->state_id = $state?->id; $address->city_id = $city?->id; $address->latitude = data_get($data, 'latitude'); $address->longitude = data_get($data, 'longitude'); $address->save(); } private function createProviderServicesTypes(int $providerId, array $data): void { $serviceTypeIds = data_get($data, 'services_types_ids', data_get($data, 'service_types_ids', [])); $uniqueIds = array_values(array_unique(array_map('intval', $serviceTypeIds))); foreach ($uniqueIds as $serviceTypeId) { ProviderServicesType::create([ 'provider_id' => $providerId, 'service_type_id' => $serviceTypeId, ]); } } private function createProviderWorkingDays(int $providerId, array $data): void { $workingDays = data_get($data, 'working_days', []); $seen = []; foreach ($workingDays as $workingDay) { $day = (int) data_get($workingDay, 'day', -1); $period = data_get($workingDay, 'period'); if ($day < 0 || $day > 6 || ! in_array($period, ['morning', 'afternoon'], true)) { continue; } $uniqueKey = $day.'-'.$period; if (data_get($seen, $uniqueKey) !== null) { continue; } $seen[$uniqueKey] = true; ProviderWorkingDay::create([ 'provider_id' => $providerId, 'day' => $day, 'period' => $period, ]); } } // private function sanitizeDigits(?string $value): ?string { if ($value === null) { return null; } $digits = preg_replace('/\D+/', '', $value); return $digits === '' ? null : $digits; } private function sendApprovedEmail(Provider $provider): void { if (! empty($provider->user?->email)) { try { $this->emailService->sendProviderApproved( email: $provider->user->email, recipientName: $provider->user->name ?? '', locale: $provider->user->language?->value, ); } catch (\Throwable $exception) { Log::error('Falha ao enviar e-mail de aprovação do prestador', [ 'provider_id' => $provider->id, 'user_id' => $provider->user?->id, 'email' => $provider->user?->email, 'error' => $exception->getMessage(), ]); } return; } Log::warning('E-mail de aprovação do prestador ignorado: usuário não possui e-mail', [ 'provider_id' => $provider->id, 'user_id' => $provider->user?->id, ]); } }