<?php declare(strict_types=1); namespace App\Domain\Project\Services; use App\Domain\Project\Enums\ProjectStatusEnum; use App\Domain\Project\Requests\CreateProjectRequest; use App\Domain\Project\Requests\UpdateProjectRequest; use App\Models\Article; use App\Models\ArticleToProject; use App\Models\Counterparty; use App\Models\Payment; use App\Models\PaymentDistribution; use App\Models\Project; use App\Models\ProjectGroup; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; class ProjectService { /** * Получить список всех проектов. * * @param int $modelID * @param Request $request * @param bool $withPayments * @return Builder */ public function getAll(int $modelID, Request $request, bool $withPayments = false): Builder { $query = Project::query() ->with(['paymentDistributions.article.group', 'paymentDistributions.payment', 'projectGroup']) ->where('model_id', $modelID) ->orderBy('created_at', 'desc'); if ($request->has('project_group_id')) { $query->where('project_group_id', $request->get('project_group_id')); } if ($request->has('search')) { $search = mb_convert_case($request->get('search'), MB_CASE_LOWER); $query->where(function ($q) use ($search) { if (!empty($search)) { $q->whereRaw('LOWER(short_description) LIKE ?', ["%{$search}%"]) ->orWhereRaw('LOWER(offer_number) LIKE ?', ["%{$search}%"]) ->orWhereRaw('LOWER(object_address) LIKE ?', ["%{$search}%"]); $q->orWhereHas('projectGroup', function ($qInner) use ($search) { $qInner->whereRaw('LOWER(name) LIKE ?', ["%{$search}%"]); }); $q->orWhereHas('paymentDistributions.article.group', function ($qInner) use ($search) { $qInner->whereRaw('LOWER(name) LIKE ?', ["%{$search}%"]); }); } }); } if ($request->has('payment_id')) { if ($request->has('payment_id')) { /** @var Payment $payment */ $payment = Payment::find($request->get('payment_id'))->with('contragent'); $ids = []; foreach ($payment->distributions as $distribution) { $ids[] = $distribution->project->id; } $query->whereIn('id', $ids); } $query->whereIn('id', $ids); } if ($withPayments) { $query->with(['paymentDistributions', 'paymentDistributions.payment']); } return $query; } /** * Получить список всех проектов с группировкой + проекты вне групп * * @param int $modelID * @return Collection */ public function getProjectsWithoutGroupsList(int $modelID, $request): Collection { $activeTab = $request->query('type'); $statusCondition = $activeTab == ProjectStatusEnum::PROJECT_STATUS_ARCHIVE->value ? '=' : '!='; return Project::query()->where('model_id', $modelID) ->with(['paymentDistributions.article', 'paymentDistributions.payment']) ->where('status', $statusCondition, ProjectStatusEnum::PROJECT_STATUS_FINISHED->value) ->where(function ($query) use ($request) { $query->whereNull('project_group_id') ->when($request->filled('search'), function ($query) use ($request) { $search = mb_convert_case($request->get('search'), MB_CASE_LOWER); $query->where(function ($query) use ($search) { $query->whereRaw('LOWER(offer_number) like ?', ["%{$search}%"]) ->orWhereRaw('LOWER(short_description) like ?', ["%{$search}%"]); }); }); })->get(); } public function getProjectGroupList(int $modelID, $request) { $activeTab = $request->query('type'); $statusCondition = $activeTab == ProjectStatusEnum::PROJECT_STATUS_ARCHIVE->value ? '=' : '!='; if ($statusCondition == '=') { return ProjectGroup::query() ->with(['projects' => function ($query) use ($statusCondition) { $query->where('status', $statusCondition, ProjectStatusEnum::PROJECT_STATUS_FINISHED->value) ->with(['paymentDistributions.article', 'paymentDistributions.payment']); }]) ->where('model_id', $modelID) ->where(function ($query) use ($request) { $query->when($request->filled('search'), function ($query) use ($request) { $search = mb_convert_case($request->get('search'), MB_CASE_LOWER); $query->whereRaw('LOWER(name) like ?', ["%{$search}%"]) ->orWhereHas('projects', function ($q) use ($search) { $q->whereRaw('LOWER(offer_number) like ?', ["%{$search}%"]) ->orWhereRaw('LOWER(short_description) like ?', ["%{$search}%"]); }); }); }) ->get() ->filter(function ($group) { return $group->projects->isNotEmpty(); }) ->values(); } return ProjectGroup::query() ->with(['projects' => function ($query) use ($statusCondition) { $query->where('status', $statusCondition, ProjectStatusEnum::PROJECT_STATUS_FINISHED->value) ->with(['paymentDistributions.article', 'paymentDistributions.payment']); }]) ->where('model_id', $modelID) ->where(function ($query) use ($request) { $query->when($request->filled('search'), function ($query) use ($request) { $search = mb_convert_case($request->get('search'), MB_CASE_LOWER); $query->whereRaw('LOWER(name) like ?', ["%{$search}%"]) ->orWhereHas('projects', function ($q) use ($search) { $q->whereRaw('LOWER(offer_number) like ?', ["%{$search}%"]) ->orWhereRaw('LOWER(short_description) like ?', ["%{$search}%"]); }); }); })->get(); } public function create( int $modelId, CreateProjectRequest $request ): Project { /** @var Project $project */ $project = Project::create(array_merge(['model_id' => $modelId], $request->toArray())); $this->syncDefaultArticles($project); return $project; } /** * Обновить проект. * * @param int $projectID * @param UpdateProjectRequest $data * @return Project */ public function update(int $projectID, UpdateProjectRequest $data): Project { $project = Project::query()->find($projectID); $project->update($data->toArray()); return $project; } public function getListExceptDelete(int $modelID, $projectId) { return Project::query() ->with(['paymentDistributions.article', 'paymentDistributions.payment', 'projectGroup']) ->where('model_id', $modelID) ->where('id', '!=', $projectId) ->get(); } /** * Найти проект по ID. * * @param int $id * @return Project|null */ public function findById(int $id) { $project = Project::query() ->where('id', $id) ->with(['paymentDistributions.payment.contragent', 'articleProjectLinks.article']) ->first(); $uniqueCounterparties = $project->paymentDistributions ->pluck('payment.contragent') ->unique('id') ->values(); $counterpartyIds = $uniqueCounterparties->pluck('id')->toArray(); $paymentsIds = PaymentDistribution::query()->where('project_id', $project->id)->get('payment_id')->toArray(); $counterparties = Counterparty::query() ->whereIn('id', $counterpartyIds) ->with(['payments' => function ($query) use ($paymentsIds, $project) { $query->whereIn('id', $paymentsIds) ->with(['distributions' => function ($subQuery) use ($project) { $subQuery->where('project_id', $project->id); }]); }]) ->get(); $articles = PaymentDistribution::query()->where('project_id', $project->id)->with(['article.articleProjectLinks', 'payment'])->get(); $project->articleProjects = $articles; $project->counterparties = $counterparties; return $project; } public function updatePaymentArticle($projectId, $amountLimit): bool { $project = PaymentDistribution::query()->where('project_id', $projectId); return (bool)$project->update(['amount_limit' => $amountLimit]); } public function payments(int $id) { return PaymentDistribution::query()->where('project_id', $id)->get(); } /** * Удалить проект. * * @param int $projectID * @param int|string|null $newProjectId * @return bool|null */ public function delete(int $projectID, int|string $newProjectId = null): ?bool { $project = $this->findById($projectID); $project->paymentDistributions->each(function (PaymentDistribution $pd) use ($newProjectId): void { $pd->update([ 'project_id' => $newProjectId, ]); }); return $project->delete(); } public function updateLimitsProject($id, $operationType, $limit): void { $project = Project::query()->find($id); if ($operationType == 'credit') { $project->update(['project_limits_credit' => $limit]); } else { $project->update(['project_limits_debit' => $limit]); } } public function addArticleToProject($projectId, $articleId) { $project = Project::query()->find($projectId); return $project->articles()->attach($articleId, ['amount_limit' => 1]); } public function deletePaymentArticle($projectId, $articleId, $newArticleId) { $article = Article::query()->with('paymentDistributions')->where('id', $articleId)->first(); if ($article) { $article->paymentDistributions ->where('project_id', $projectId) ->each(function (PaymentDistribution $pd) use ($newArticleId) { $pd->update([ 'article_id' => $newArticleId, ]); }); $articleToProject = ArticleToProject::query()->where(['article_id' => $articleId, 'project_id' => $projectId])->first(); if ($articleToProject) { $articleToProject->delete(); } return true; } return false; } private function syncDefaultArticles(Project $project): void { $articleIds = $project->model->articles() ->where('default_in_project', true) ->pluck('id') ->values() ->all(); $project->articles()->sync($articleIds); } }