File "ActiveTabProjectService.php"

Full Path: /var/www/html/back/app/Services/CashFlow/ActiveTabProjectService.php
File size: 25.57 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace App\Services\CashFlow;

use App\Domain\Article\Enums\ArticleTypeEnum;
use App\Domain\Payment\Enums\PaymentTypeEnum;
use App\Http\Resources\CashFlowCreditResource;
use App\Http\Resources\CashFlowDebitResource;
use App\Http\Resources\CashFlowProjectCreditResource;
use App\Models\PaymentDistribution;
use App\Models\Project;
use App\Repositories\CashFlowIndex\Interfaces\PaymentDistributionsRepositoryInterface;
use App\Repositories\CashFlowIndex\Interfaces\TotalAmountRepositoryInterface;
use App\Responses\ResponseDto;
use App\Services\CashFlowService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;

class ActiveTabProjectService
{
    private $distributionsRepository;
    private $paymentRepository;

    public function __construct(PaymentDistributionsRepositoryInterface $paymentDistributionsRepository,
                                TotalAmountRepositoryInterface          $paymentRepository,
                                protected CashFlowService               $cashFlowService)
    {
        $this->distributionsRepository = $paymentDistributionsRepository;
        $this->paymentRepository = $paymentRepository;
    }

    public function getActivePageProjectToIndex(int $modelID, $filters)
    {
        $paymentDistributionsCredit = $this->distributionsRepository->getPaymentDistributionsProject($modelID, $filters, ArticleTypeEnum::ARTICLE_TYPE_CREDIT->value);
        $paymentDistributionsDebit = $this->distributionsRepository->getPaymentDistributionsProject($modelID, $filters, ArticleTypeEnum::ARTICLE_TYPE_DEBIT->value);

        $totalAmountCreditPayment = $this->paymentRepository->getTotalAmountPayment($modelID, $filters, $credit = true);
        $totalAmountDebitPayment = $this->paymentRepository->getTotalAmountPayment($modelID, $filters, $credit = false);

        $totalCredit = $this->cashFlowService->sumTotalAmount($paymentDistributionsCredit);
        $totalDebit = $this->cashFlowService->sumTotalAmount($paymentDistributionsDebit);

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


        if ($check) {
            $formattedToProjectCredit = $this->sortToProject($paymentDistributionsCredit, true, $check);
            $formattedToProjectDebit = $this->sortToProject($paymentDistributionsDebit, false, $check);
            $formattedOtherData = $this->sortToProjectOtherData($filters, false, $modelID);
        } else {
            $formattedToProjectCredit = $this->sortToProject($paymentDistributionsCredit, true, $check);
            $formattedToProjectDebit = $this->sortToProject($paymentDistributionsDebit, false, $check);
            $formattedOtherData = $this->sortToProjectOtherData($filters, false, $modelID);
        }

        $totalAmountToProject = $this->totalAmountToProject($filters);

        $totalAmountDebitCashPayment = $this->paymentRepository->getTotalAmountCashPayment($modelID, $filters, $credit = false, $debitType = 'cash');
        $totalAmountCashDebitCommissionPayment = $this->paymentRepository->getTotalCommissionPaymentDistribution($modelID, $filters, $debitType);
        $totalMoving = $this->paymentRepository->getTotalAmountCashPayment($modelID, $filters, $credit = false, $debitType = 'moving');

        $totalCash = $this->cashFlowService->getTotalCash($filters, $modelID);

        $totalAmountDebit = $this->totalAmountDebitProject($filters);
        $newFormat = $this->newFormat($filters, false);

        $startPeriod = $this->startPeriod($formattedToProjectCredit, $formattedToProjectDebit, $newFormat, $formattedOtherData);

        return new ResponseDto(
            data: [
                'header' => [
                    'start_period' => [
                        'amount' => $startPeriod,
//                        'cash' => $startCash,
                    ]
                ],
                'credit' => [
                    'static' => [
                        'total_amount' => $totalCredit + $totalAmountCreditPayment,
                        'total_cash' => $totalCash,
                        'articles' => CashFlowCreditResource::collection($paymentDistributionsCredit),
                        'not_distribution_payments_total_amount' => $totalAmountCreditPayment,
                        'other_data' => [
                            'total_cash' => $totalCash,
                        ],
                    ],
                    'dynamic' => [
                        'total_amount' => $totalAmountToProject,
                        'projects' => $formattedToProjectCredit,
                    ],
                ],
                'debit' => [
                    'static' => [
                        'total_amount' => $totalDebit + $totalAmountDebitPayment + $totalAmountDebitCashPayment,
                        'articles' => CashFlowDebitResource::collection($paymentDistributionsDebit),
                        'not_distribution_payments_total_amount' => $totalAmountDebitPayment,
                        'other_data' => [
                            'moving' => $totalMoving,
                            'commission' => $totalAmountCashDebitCommissionPayment,
                            'cash' => $totalAmountDebitCashPayment,
                        ],
                    ],
                    'dynamic' => [
                        'total_amount' => $totalAmountDebit,
//                        'total_cash' => $ttlCash,
                        'projects' => $formattedToProjectDebit,
                        'other_data' => $formattedOtherData,
                    ],
                ],
                'footer' => [
                    'end_period' => [
                        'total_amount' => (($totalCredit + $totalCash) - ($totalDebit + $totalAmountDebitCashPayment)),
                        'amount' => $startPeriod,
//                        'cash' => $startCash,
                    ]
                ],
            ],
            status: true
        );
    }

    public function totalAmountDebitProject($filters)
    {
        $query = PaymentDistribution::query();
        $query->with(['payment', 'article']);

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

        if (!empty($filters)) {
            if (!empty($filters['projects'])) {
                $query->whereIn('project_id', $filters['projects']);
            }

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

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

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

        $paymentDistributions = $query->get();

        $result = [];

        foreach ($paymentDistributions as $item) {
            if ($item->article && $item->article->article_type == 'debit' && $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;
    }

    public function newFormat($filters, $check)
    {
        $query = PaymentDistribution::query();

        if ($check) {
            if (!empty($filters['date_from'])) {
                if (empty($filters['date_to'])) {
                    $dateTo = Carbon::now();
                } else {
                    $dateTo = $filters['date_to'];
                }

                $query->with(['payment' => function ($query) use ($filters, $dateTo) {
                    $query->where(function ($q) use ($filters, $dateTo) {
                        // Для moving учитываем actual_date
                        $q->where(function ($qMoving) use ($filters, $dateTo) {
                            $qMoving->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                ->where(function ($qActual) use ($filters, $dateTo) {
                                    $qActual->whereNotNull('actual_date')
                                        ->whereBetween('actual_date', [$filters['date_from'], $dateTo]);
                                })->orWhere(function ($qPayment) use ($filters, $dateTo) {
                                    $qPayment->whereNull('actual_date')
                                        ->whereBetween('payment_date', [$filters['date_from'], $dateTo]);
                                });
                        })->orWhere(function ($qOther) use ($filters, $dateTo) {
                            $qOther->where('payment_type', '!=', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                ->whereBetween('payment_date', [$filters['date_from'], $dateTo]);
                        });
                    });
                }]);
            }

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

        } else {
            $query->with('payment');
        }

        $paymentDistributions = $query->get();

        $projectCollection = [];

        foreach ($paymentDistributions as $distribution) {
            if (isset($distribution->payment->payment_date) && isset($distribution->project_id)) {
                $projectId = $distribution->project_id;

                if (!isset($projectCollection[$projectId])) {
                    $projectCollection[$projectId] = [
                        'total_cash_debit' => 0,
                    ];
                }

                if ($distribution->payment->payment_type == PaymentTypeEnum::PAYMENT_TYPE_MOVING->value) {
                    // Используем cashbox для moving
                    if (isset($distribution->cashbox)) {
                        $projectCollection[$projectId]['total_cash_debit'] += $distribution->cashbox;
                    }
                }

            }
        }

        if ($check) {
            foreach ($projectCollection as $projectId => $items) {
                if ($items['total_cash_debit'] == 0) {
                    unset($projectCollection[$projectId]);
                }
            }
        }

        return $projectCollection;
    }

    public function startPeriod($formattedDataCredit, $formattedDataDebit, $newFormat, $formattedOtherData)
    {

        $newCollection = [];

        foreach ($formattedDataCredit as $item) {
            $itemArray = $item->resolve();
            if (!empty($itemArray['project'])) {
                foreach ($itemArray['project'] as $project) {
                    $projectId = $project['id'];

                    if (!isset($newCollection[$projectId])) {
                        $newCollection[$projectId] = [];
                    }

                    $newCollection[$projectId][] = [
                        'amount' => $project['payment']['amount'],
                        'total_extradition' => $project['total_extradition'],
                        'type' => 'credit',
                    ];
                }
            }
        }

        foreach ($formattedDataDebit as $item) {
            $itemArray = $item->resolve();
            if (!empty($itemArray['project'])) {
                foreach ($itemArray['project'] as $project) {
                    $projectId = $project['id'];

                    if (!isset($newCollection[$projectId])) {
                        $newCollection[$projectId] = [];
                    }

                    $newCollection[$projectId][] = [
                        'amount' => $project['payment']['amount'],
                        'total_extradition' => $project['total_extradition'],
                        'type' => 'debit',
                    ];
                }
            }
        }

        Log::info('NewCOllect' . json_encode($newCollection));

        $collection = [];
        $previousBalance = 0;

        foreach ($newCollection as $projectId => $records) {
            $income = 0;
            $expense = 0;

            foreach ($records as $key => $record) {
                if ((!is_object($record) && ($key == 'total_cash'))) {
                    $income += $record;
                }

                if ((!is_object($record) && ($key == 'total_moving'))) {
                    $expense += $record;
                }

                if ($record) {
                    if ($record['type'] == ArticleTypeEnum::ARTICLE_TYPE_CREDIT->value) {
                        $income += intval($record['amount']);
                    } else {
                        $expense += intval($record['amount']);
                    }
                }
            }

            $startBalance = 0;
            $endBalance = $startBalance + $income - $expense;

            if (!isset($collection[$projectId])) {
                $collection[$projectId] = [];
            }

            $collection[$projectId] = [
                'start_balance' => $startBalance,
                'end_balance' => $endBalance
            ];

            $previousBalance = $endBalance;
        }

        return $collection;
    }


    public function totalAmountToProject($filters)
    {
        $query = PaymentDistribution::query();
        $query->with(['payment', 'article']);

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

        if (!empty($filters)) {
            if (!empty($filters['projects'])) {
                $query->whereIn('project_id', $filters['projects']);
            }

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

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

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

        $paymentDistributions = $query->get();

        $result = [];

        foreach ($paymentDistributions as $item) {
            if ($item->article && $item->article->article_type == 'credit' && $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;
    }


    public function sortToProjectOtherData($filters, $check, $modelId)
    {
        $query = PaymentDistribution::query();

        if ($check) {
            if (!empty($filters['date_from'])) {
                if (empty($filters['date_to'])) {
                    $dateTo = Carbon::now();
                } else {
                    $dateTo = $filters['date_to'];
                }

                // Для moving и commission нужно учитывать actual_date
                $query->with(['payment' => function ($query) use ($filters, $dateTo) {
                    $query->where(function ($q) use ($filters, $dateTo) {
                        // Для moving: если есть actual_date, фильтруем по нему, иначе по payment_date
                        $q->where(function ($qMoving) use ($filters, $dateTo) {
                            $qMoving->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                ->where(function ($qActual) use ($filters, $dateTo) {
                                    $qActual->whereNotNull('actual_date')
                                        ->whereBetween('actual_date', [$filters['date_from'], $dateTo]);
                                })->orWhere(function ($qPayment) use ($filters, $dateTo) {
                                    $qPayment->whereNull('actual_date')
                                        ->whereBetween('payment_date', [$filters['date_from'], $dateTo]);
                                });
                        })->orWhere(function ($qOther) use ($filters, $dateTo) {
                            // Для других типов - только payment_date
                            $qOther->where('payment_type', '!=', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                ->whereBetween('payment_date', [$filters['date_from'], $dateTo]);
                        });
                    });
                }]);
            }

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

        } else {
            $query->with('payment');
        }

        $paymentDistributions = $query->get();

        $projectCollection = [];

        foreach ($paymentDistributions as $distribution) {
            if ($distribution->payment->model_id == $modelId) {
                // Определяем дату для фильтрации
                $paymentDate = null;
                if ($distribution->payment->payment_type == PaymentTypeEnum::PAYMENT_TYPE_MOVING->value) {
                    // Для moving используем actual_date если есть, иначе payment_date
                    $paymentDate = $distribution->payment->actual_date ?? $distribution->payment->payment_date;
                } else {
                    $paymentDate = $distribution->payment->payment_date;
                }

                if (isset($paymentDate) && isset($distribution->project_id)) {
                    $projectId = $distribution->project_id;

                    if (!isset($projectCollection[$projectId])) {
                        $projectCollection[$projectId] = [
                            'total_cash' => 0,
                            'total_commission' => 0,
                            'total_moving' => 0
                        ];
                    }

                    if (isset($distribution->comission)) {
                        $projectCollection[$projectId]['total_commission'] += $distribution->comission;
                    }

                    if ($distribution->payment->payment_type == PaymentTypeEnum::PAYMENT_TYPE_MOVING->value) {
                        // Используем cashbox для moving вместо amount
                        if (isset($distribution->cashbox)) {
                            $projectCollection[$projectId]['total_cash'] += $distribution->cashbox;
                        }
                    }

                    if ((isset($distribution->cashbox)) && ($distribution->payment->payment_type == PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)) {
                        $projectCollection[$projectId]['total_moving'] += $distribution->cashbox;
                    }
                }
            }
        }

        if ($check) {
            foreach ($projectCollection as $projectId => $items) {
                if (($items['total_cash'] == 0) && ($items['total_commission'] == 0) && ($items['total_moving'] == 0)) {
                    unset($projectCollection[$projectId]);
                }
            }
        }

        return $projectCollection;

    }

    public function getProjectShow($modelId, $projectId, $articleId, $filters)
    {
        $query = Project::query()
            ->where('model_id', $modelId);

        if (!empty($filters['projects'])) {
            $query->whereIn('id', $filters['projects']);
        }

        $query->with([
            'paymentDistributions' => function ($pdQuery) use ($articleId, $filters) {

                $pdQuery->where('article_id', $articleId);

                $pdQuery->whereHas('payment', function ($paymentQuery) use ($filters) {

                    if (isset($filters['date_from'])) {
                        $dateFrom = $filters['date_from'];
                        $dateTo = $filters['date_to'] ?? Carbon::now();
                        $paymentQuery->whereBetween('payment_date', [$dateFrom, $dateTo]);
                    }

                    if (!empty($filters['accounts'])) {
                        $paymentQuery->whereIn('account_id', $filters['accounts']);
                    }

                    if (!empty($filters['payments_made'])) {
                        $paymentQuery->whereIn('status', $filters['payments_made']);
                    }

                    if (!empty($filters['cash'])) {
                        $paymentQuery->whereIn('payment_type', $filters['cash']);
                    }
                });
            },
            'paymentDistributions.project',
            'paymentDistributions.payment.contragent',
            'paymentDistributions.article'
        ]);

        $project = $query->find($projectId);

        $counterparties = $this->cashFlowService->getProjectCounterparties($project);

        $project->counterparties = $counterparties;

        return $project;
    }

    public function getArticleGroupProjectShow($modelId, $projectId, $articleGroupId, $filters)
    {
        $query = Project::query()
            ->where('model_id', $modelId);


        if (!empty($filters['projects'])) {
            $query->whereIn('id', $filters['projects']);
        }

        $query->with([
            'paymentDistributions' => function ($query) use ($articleGroupId, $filters) {

                $query->whereHas('article', function ($query) use ($articleGroupId) {
                    $query->where('article_group_id', $articleGroupId);
                });

                if (isset($filters['date_from'])) {
                    $dateFrom = $filters['date_from'];
                    $dateTo = $filters['date_to'] ?? Carbon::now();

                    $query->whereHas('payment', function ($query) use ($dateFrom, $dateTo) {
                        $query->whereBetween('payment_date', [$dateFrom, $dateTo]);
                    });
                }

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

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

                if (!empty($filters['cash'])) {
                    $query->whereHas('payment', function ($query) use ($filters) {
                        $query->whereIn('payment_type', $filters['cash']);
                    });
                }
            },
            'paymentDistributions.project',
            'paymentDistributions.payment.contragent',
            'paymentDistributions.article' => function ($query) use ($articleGroupId) {
                $query->where('article_group_id', $articleGroupId);
            }
        ]);

        $project = $query->find($projectId);

        $counterparties = $this->cashFlowService->getProjectCounterparties($project);

        $project->counterparties = $counterparties;

        return $project;
    }

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

    public function getIncomeAndExpensesToProjectShow($modelId, $type, $projectId)
    {
        $query = PaymentDistribution::query();

        $query->where('project_id', $projectId);

        switch ($type) {
            case 'credit':
                $query->with(['article' => function ($query) use ($modelId) {
                    $query->where([
                        'article_type' => ArticleTypeEnum::ARTICLE_TYPE_CREDIT->value,
                        'model_id' => $modelId
                    ]);
                }, 'payment.contragent'])
                    ->whereHas('article', function ($query) use ($modelId) {
                        $query->where([
                            'article_type' => ArticleTypeEnum::ARTICLE_TYPE_CREDIT->value,
                            'model_id' => $modelId
                        ]);
                    });
                break;

            case 'debit':
                $query->with(['article' => function ($query) use ($modelId) {
                    $query
                        ->where([
                            'article_type' => ArticleTypeEnum::ARTICLE_TYPE_DEBIT->value,
                            'model_id' => $modelId
                        ]);
                }, 'payment.contragent'])
                    ->whereHas('article', function ($query) use ($modelId) {
                        $query->where([
                            'article_type' => ArticleTypeEnum::ARTICLE_TYPE_DEBIT->value,
                            'model_id' => $modelId
                        ]);
                    });
                break;

            default:
                break;
        }


        return $query->get();
    }
}