File "ProjectService.php"

Full Path: /var/www/html/back/app/Domain/Project/Services/ProjectService.php
File size: 11.54 KB
MIME-type: text/x-php
Charset: utf-8

<?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);
    }
}