File "ProjectGroupService.php"

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

<?php

declare(strict_types=1);

namespace App\Domain\Project\Services;

use App\Domain\Article\Enums\ArticleTypeEnum;
use App\Domain\Project\Requests\CreateProjectGroupRequest;
use App\Domain\Project\Requests\UpdateProjectGroupRequest;
use App\Http\Resources\CashFlowCreditResource;
use App\Http\Resources\CashFlowDebitResource;
use App\Http\Resources\SortToProjectCreditResource;
use App\Models\Article;
use App\Models\Counterparty;
use App\Models\PaymentDistribution;
use App\Models\Project;
use App\Models\ProjectGroup;
use App\Repositories\ProjectGroup\PaymentByArticleRepository;
use App\Responses\ResponseDto;
use App\Services\CashFlow\ActiveTabProjectService;
use App\Services\CashFlowService;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ProjectGroupService
{
    private $paymentByArticleRepository;

    public function __construct(protected CashFlowService         $cashFlowService,
                                protected ActiveTabProjectService $tabProjectService,
                                PaymentByArticleRepository        $byArticleRepository)
    {
        $this->paymentByArticleRepository = $byArticleRepository;
    }

    /**
     * Получить список всех групп проектов.
     *
     * @return Collection
     */
    public function getAll(): Collection
    {
        return ProjectGroup::all();
    }

    /**
     * Создать новую группу проектов.
     *
     * @param int $modelId
     * @param CreateProjectGroupRequest $request
     * @return ProjectGroup
     */
    public function create(int $modelId, CreateProjectGroupRequest $request): ProjectGroup
    {
        $data = $request->toArray();
        $ids = $data['projects'] ?? [];
        unset($data['projects']);
        DB::beginTransaction();
        $projectGroup = ProjectGroup::create([
            ...$data,
            'model_id' => $modelId,
        ]);
        if (!empty($ids)) {
            Project::whereIn('id', $ids)
                ->where('model_id', $modelId)
                ->update(['project_group_id' => $projectGroup->id]);
        }
        DB::commit();

        return $projectGroup;
    }

    /**
     * Обновить существующую группу проектов.
     *
     * @param int $id
     * @param UpdateProjectGroupRequest $request
     * @return ProjectGroup
     */
    public function update(int $id, $data)
    {
        $group = ProjectGroup::query()->find($id);

        if (isset($data['name'])) {
            $group->update(['name' => $data['name']]);
        }

        $group->update(['description' => $data['description'] ?? null]);

        if (isset($data['projects'])) {
            $existingProjects = Project::where('project_group_id', $id)->get();
            $array = explode(',', $data['projects']);

            foreach ($existingProjects as $existingProject) {
                $existingProject->update(['project_group_id' => null]);
            }
            foreach ($array as $item) {
                Project::where('id', $item)->update(['project_group_id' => $id]);
            }
        }
        return $group;
    }

    /**
     * Найти группу проектов по ID.
     *
     * @param int $id
     * @return ProjectGroup|null
     */
    public function findById(int $id): ?ProjectGroup
    {
        $projectsGroup = ProjectGroup::query()->where('id', $id)
            ->with(['projects.paymentDistributions.payment.contragent', 'projects.paymentDistributions.article'])
            ->firstOrFail();

        $counterpartyIds = collect();
        $paymentsIds = collect();

        $projectsGroup->projects->each(function ($project) use (&$counterpartyIds, &$paymentsIds) {
            $uniqueCounterparties = $project->paymentDistributions
                ->pluck('payment.contragent')
                ->unique('id')
                ->values();

            $counterpartyIds = $counterpartyIds->concat($uniqueCounterparties->pluck('id'));

            $paymentsIds = $paymentsIds->concat($project->paymentDistributions->pluck('payment_id'));
        });

        $counterparties = Counterparty::query()
            ->whereIn('id', $counterpartyIds)
            ->whereHas('payments', function ($query) use ($paymentsIds, $id) {
                $query->whereIn('id', $paymentsIds)
                    ->whereHas('distributions.project', function ($subQuery) use ($id) {
                        $subQuery->where('project_group_id', $id);
                    });
            })
            ->with(['payments' => function ($query) use ($paymentsIds, $id) {
                $query->whereIn('id', $paymentsIds)
                    ->with([
                        'distributions' => function ($queryDist) use ($id) {
                            $queryDist->whereHas('project', function ($subQuery) use ($id) {
                                $subQuery->where('project_group_id', $id);
                            });
                        },

                        'distributions.project' => function ($subQuery) use ($id) {
                            $subQuery->where('project_group_id', $id);
                        }
                    ]);
            }])
            ->get();

        $projectsGroup->setRelation('counterparties', $counterparties);

        return $projectsGroup;
    }

    /**
     * Удалить группу проектов.
     *
     * @param int $groupID
     * @return bool|null
     */
    public function delete(int $groupID): ?bool
    {
        return $this->findById($groupID)->delete();
    }


    public function getGroupedByArticleForProjetsGroup($modelId, $groupId, $filters)
    {
        $articleCredit = $this->getGroupedByArticle($modelId, $groupId, $filters, ArticleTypeEnum::ARTICLE_TYPE_CREDIT->value);
        $articleDebit = $this->getGroupedByArticle($modelId, $groupId, $filters, ArticleTypeEnum::ARTICLE_TYPE_DEBIT->value);

        $totalCredit = $this->cashFlowService->sumTotalAmount($articleCredit);
        $totalDebit = $this->cashFlowService->sumTotalAmount($articleDebit);

        $totalExtradition = $this->cashFlowService->sumTotalExtradition($articleDebit);

        $formattedToProjectCredit = $this->sortToProject($articleCredit, $credit = true);
        $formattedToProjectDebit = $this->sortToProject($articleDebit, $credit = false);

        $totalAmountCreditToProject = $this->totalAmountToProject($modelId, $groupId, $filters, ArticleTypeEnum::ARTICLE_TYPE_CREDIT->value);
        $totalAmountDebitToProject = $this->totalAmountToProject($modelId, $groupId, $filters, ArticleTypeEnum::ARTICLE_TYPE_DEBIT->value);

        return new ResponseDto(
            data: [
                'credit' => [
                    'static' => [
                        'total_amount' => $totalCredit,
                        'articles' => CashFlowCreditResource::collection($articleCredit),
                    ],
                    'dynamic' => [
                        'projects' => $formattedToProjectCredit,
                    ],
                ],
                'debit' => [
                    'static' => [
                        'total_amount' => $totalDebit,
                        'total_extradition' => $totalExtradition,
                        'articles' => CashFlowDebitResource::collection($articleDebit),
                    ],
                    'dynamic' => [
                        'projects' => $formattedToProjectDebit,
                    ],
                ],
            ],
            status: true
        );
    }

    public function sortToProject($paymentDistributionsCredit, $type)
    {
        if ($type) {
            return SortToProjectCreditResource::collection($paymentDistributionsCredit);
        } else {
            return SortToProjectCreditResource::collection($paymentDistributionsCredit);
        }
    }


    public function getGroupedByArticle($modelId, $groupId, $filters, $type)
    {

        $filters = array_filter($filters, function ($value) {
            return $value !== null;
        });

        $projectIds = $this->paymentByArticleRepository->getProjectIds($modelId, $groupId);

        $articleIds = $this->paymentByArticleRepository->getArticleIds($projectIds);

        $query = Article::query()
            ->where('model_id', $modelId)
            ->whereIn('id', $articleIds)
            ->where('article_type', $type)
            ->whereHas('paymentDistributions', function ($subQuery) use ($projectIds) {
                $subQuery->whereIn('project_id', $projectIds);
            });

        $query->with([
            'paymentDistributions' => function ($q) use ($projectIds, $filters) {
                $q->whereIn('project_id', $projectIds);

                if (!empty($filters)) {
                    $q->whereHas('payment', function ($paymentQuery) use ($filters) {
                        if (!empty($filters['date_from'])) {
                            $dateTo = $filters['date_to'] ?? Carbon::now();
                            $paymentQuery->whereBetween('payment_date', [$filters['date_from'], $dateTo]);
                        }
                        if (!empty($filters['payments_made'])) {
                            $paymentQuery->whereIn('status', $filters['payments_made']);
                        }
                    });
                }
            },
            'paymentDistributions.payment' => function ($q) use ($filters) {
                if (!empty($filters['date_from'])) {
                    $dateTo = $filters['date_to'] ?? Carbon::now();
                    $q->whereBetween('payment_date', [$filters['date_from'], $dateTo]);
                }
                if (!empty($filters['payments_made'])) {
                    $q->whereIn('status', $filters['payments_made']);
                }
            },
            'paymentDistributions.project'
        ]);

        return $query->get();
    }

    public function totalAmountToProject($modelID, $groupId, $filters, $type)
    {
        $projectIds = $this->paymentByArticleRepository->getProjectIds($modelID, $groupId);

        $query = PaymentDistribution::query();

        $query->whereIn('project_id', $projectIds);

        $query->with(['payment', 'article']);

        $filters = array_filter($filters, function ($value) {
            return $value !== null;
        });

        if (!empty($filters)) {
            if (!empty($filters['payments_made'])) {
                $query->whereHas('payment', function ($query) use ($filters) {
                    $query->whereIn('status', $filters['payments_made']);
                });
            }
        }

        $paymentDistributions = $query->get();

        $result = [];

        foreach ($paymentDistributions as $item) {
            if ($item->article && $item->article->article_type == $type && $item->article->id != 1) {
                $amount = floatval($item->amount);

                if (!isset($result[$item->project_id])) {
                    $result[$item->project_id] = 0;
                }

                $result[$item->project_id] += $amount;
            }
        }

        return $result;
    }
}