Mahdee Rajon  subception

File "CashFlowService.php"

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

<?php

declare(strict_types=1);

namespace App\Services;


use App\Domain\Article\Enums\ArticleTypeEnum;
use App\Domain\Payment\Enums\PaymentStatusEnum;
use App\Domain\Payment\Enums\PaymentTypeEnum;
use App\Exports\PaymentsExport;
use App\Http\Resources\CashFlowCreditResource;
use App\Http\Resources\CashFlowDebitResource;
use App\Http\Resources\CashFlowProjectCreditResource;
use App\Http\Resources\ShowUndistributedPaymentsByCounterpartyResource;
use App\Models\Article;
use App\Models\ArticleGroup;
use App\Models\Counterparty;
use App\Models\Payment;
use App\Models\PaymentDistribution;
use App\Repositories\CashFlowIndex\Interfaces\PaymentDistributionsRepositoryInterface;
use App\Repositories\CashFlowIndex\Interfaces\TotalAmountRepositoryInterface;
use App\Responses\ResponseDto;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;

class CashFlowService
{
    private $distributionsRepository;
    private $paymentRepository;

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

    public function getIncomeAndExpensesShow($modelId, $type, $filters)
    {

        $query = PaymentDistribution::query();

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


        $query->where('cashbox', null);

        if (!empty($filters)) {
            if (!empty($filters['date_from'])) {
                $dateFrom = $filters['date_from'];
                $dateTo = empty($filters['date_to']) ? Carbon::now() : $filters['date_to'];
                $query->whereHas('payment', function ($paymentFilterQuery) use ($dateFrom, $dateTo) {
                    $paymentFilterQuery->whereBetween('payment_date', [$dateFrom, $dateTo]);
                });
            }

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

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

                $paymentFilterQuery->where('payment_type', '!=', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                    ->where('payment_type', '!=', PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value);

            });
        }

        $query->with(['payment' => function ($paymentQuery) use ($filters) {
            if (!empty($filters['date_from'])) {
                $dateFrom = $filters['date_from'];
                $dateTo = empty($filters['date_to']) ? Carbon::now() : $filters['date_to'];
                $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']);
            }

            $paymentQuery->where('payment_type', '!=', PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value)
                ->with('contragent');
        }]);

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

            default:
                break;
        }


        return $query->get();
    }


    public function getCashIncomeShow($modelId)
    {
        $query = PaymentDistribution::query()
            ->whereHas('payment', function ($query) use ($modelId) {
                $query
                    ->where('model_id', $modelId)
                    ->where('status', PaymentStatusEnum::STATUS_RECEIVED->value)
                    ->whereIn('payment_type', [
                        PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value,
                        PaymentTypeEnum::PAYMENT_TYPE_MOVING->value,
                    ]);
            })
            ->with([
                'payment.contragent',
                'article',
            ]);

        $collection = $query->get();

        $collection->each(function ($distribution) {
            if (
                in_array(
                    $distribution->payment->payment_type,
                    [
                        PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value,
                        PaymentTypeEnum::PAYMENT_TYPE_MOVING->value,
                    ],
                    true
                )
            ) {
                $distribution->amount = $distribution->cashbox ?? $distribution->amount;
            }
        });

        return $collection;
    }


    public function getReceptionIncomeShow($modelId, $statuses)
    {
        $query = PaymentDistribution::query()
            ->whereHas('payment', function ($query) use ($modelId, $statuses) {
                $query
                    ->where('model_id', $modelId)
                    ->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value);

                if (!empty($statuses)) {
                    $query->whereIn('status', $statuses);
                }
            })
            ->with([
                'payment.contragent',
                'article',
            ]);

        $collection = $query->get();

        $collection->each(function ($distribution) {
            $distribution->amount = $distribution->cashbox ?? $distribution->amount;
        });

        return $collection;
    }


    public function getUndistributedIncomeShow($modelId, $paymentsMade)
    {
        $query = PaymentDistribution::query()
            ->whereHas('payment', function ($query) use ($modelId, $paymentsMade) {
                $query
                    ->where('model_id', $modelId)
                    ->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_RECEPTION_FROM_1C->value)
                    ->whereNull('article_id');

                if (!empty($paymentsMade)) {
                    $query->whereIn('status', $paymentsMade);
                }
            })
            ->with([
                'payment.contragent',
                'article',
            ]);

        $collection = $query->get();

        return $collection;
    }


    public function getIssueanceShow($modelId)
    {
        $query = PaymentDistribution::query()
            ->whereHas('payment', function ($query) use ($modelId) {
                $query
                    ->where('model_id', $modelId)
                    ->where('status', PaymentStatusEnum::STATUS_RECEIVED->value)
                    ->whereIn('payment_type', [
                        PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value,
                        PaymentTypeEnum::PAYMENT_TYPE_PAYMENT->value,
                    ]);
            })
            ->with([
                'payment.contragent',
                'article',
            ]);

        $collection = $query->get();

        $collection->each(function ($distribution) {
            if (
                in_array(
                    $distribution->payment->payment_type,
                    [
                        PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value,
                        PaymentTypeEnum::PAYMENT_TYPE_PAYMENT->value,
                    ],
                    true
                )
            ) {
                $distribution->amount = $distribution->cashbox ?? $distribution->amount;
            }
        });

        return $collection;
    }


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

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

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

            $paymentQuery->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value)
                ->where('model_id', $modelId);

            if (!empty($filters['date_from'])) {
                $dateFrom = $filters['date_from'];
                $dateTo = !empty($filters['date_to']) ? $filters['date_to'] : Carbon::now()->toDateString();
                $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']);
            }
        });

        $query->with(['payment' => function ($q) use ($modelId) {
            $q->where('model_id', $modelId)
                ->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value);
        }]);

        $distributions = $query->get();

        $totalCash = $distributions->sum(function ($distribution) use ($modelId) {
            return (float)($distribution->amount ?? 0);
        });

        $totalCash += $this->getTotalCashFromMoving($filters, $modelId);

        return $totalCash;
    }

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

        $query->whereNotNull('cashbox');

        $query->whereHas('payment', function ($paymentQuery) use ($modelId, $filters) {
            $paymentQuery->where('model_id', $modelId)
                ->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                ->where('status', PaymentStatusEnum::STATUS_RECEIVED->value)
                ->whereNotNull('actual_date');

            if (!empty($filters['date_from'])) {
                $dateFrom = $filters['date_from'];
                $dateTo = $filters['date_to'] ?? Carbon::now()->toDateString();
                $paymentQuery->whereBetween('actual_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']);
            }
        });

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

        $query->with(['payment' => function ($q) use ($modelId) {
            $q->where('model_id', $modelId)
                ->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                ->where('status', PaymentStatusEnum::STATUS_RECEIVED->value)
                ->whereNotNull('actual_date');
        }]);

        $distributions = $query->get();

        return $distributions->sum(function ($distribution) {
            return (float)($distribution->cashbox ?? 0);
        });
    }


    public function getOtherDataShow($modelId, $type, $filters)
    {
        $baseQuery = function ($query) {
            $query->with('contragent');
        };

        $query = PaymentDistribution::query();

        $query->whereHas('payment', function ($query) use ($modelId) {
            $query->where('model_id', $modelId);
        });


        switch ($type) {
            case 'moving':
                $query->with(['payment' => function ($query) use ($baseQuery, $modelId, $filters) {
                    $query->where(['payment_type' => PaymentTypeEnum::PAYMENT_TYPE_MOVING->value, 'model_id' => $modelId]);

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

                        $query->where(function ($q) use ($dateFrom, $dateTo) {
                            $q->where(function ($qActual) use ($dateFrom, $dateTo) {
                                $qActual->whereNotNull('actual_date')
                                    ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                            })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                $qPayment->whereNull('actual_date')
                                    ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                            });
                        });
                    }

                    if (!empty($filters['projects'])) {
                        $query->whereHas('distributions', function ($query) use ($filters) {
                            $query->whereIn('project_id', $filters['projects']);
                        })->with(['distributions' => function ($query) use ($filters) {
                            $query->whereIn('project_id', $filters['projects']);
                        }]);
                    }

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

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

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

                    $baseQuery($query);
                }])
                    ->whereHas('payment', function ($query) use ($baseQuery, $modelId, $filters) {
                        $query->where(['payment_type' => PaymentTypeEnum::PAYMENT_TYPE_MOVING->value, 'model_id' => $modelId]);

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

                            $query->where(function ($q) use ($dateFrom, $dateTo) {
                                $q->where(function ($qActual) use ($dateFrom, $dateTo) {
                                    $qActual->whereNotNull('actual_date')
                                        ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                                })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                    $qPayment->whereNull('actual_date')
                                        ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                });
                            });
                        }

                        if (!empty($filters['projects'])) {
                            $query->whereHas('distributions', function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            })->with(['distributions' => function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            }]);
                        }

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

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

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


                        $baseQuery($query);
                    });
                break;
            case 'commission':
                $query->where('comission', '!=', null)
                    ->with(['payment' => function ($query) use ($baseQuery, $modelId, $filters) {
                        $query->where('model_id', $modelId);

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

                            $query->where(function ($q) use ($dateFrom, $dateTo) {
                                $q->where(function ($qMoving) use ($dateFrom, $dateTo) {
                                    $qMoving->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                        ->where(function ($qActual) use ($dateFrom, $dateTo) {
                                            $qActual->whereNotNull('actual_date')
                                                ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                                        })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                            $qPayment->whereNull('actual_date')
                                                ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                        });
                                })->orWhere(function ($qOther) use ($dateFrom, $dateTo) {
                                    $qOther->where('payment_type', '!=', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                        ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                });
                            });
                        }

                        if (!empty($filters['projects'])) {
                            $query->whereHas('distributions', function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            })->with(['distributions' => function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            }]);
                        }

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

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

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


                        $baseQuery($query);
                    }])
                    ->whereHas('payment', function ($query) use ($baseQuery, $modelId, $filters) {
                        $query->where('model_id', $modelId);

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

                            $query->where(function ($q) use ($dateFrom, $dateTo) {
                                $q->where(function ($qMoving) use ($dateFrom, $dateTo) {
                                    $qMoving->where('payment_type', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                        ->where(function ($qActual) use ($dateFrom, $dateTo) {
                                            $qActual->whereNotNull('actual_date')
                                                ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                                        })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                            $qPayment->whereNull('actual_date')
                                                ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                        });
                                })->orWhere(function ($qOther) use ($dateFrom, $dateTo) {
                                    $qOther->where('payment_type', '!=', PaymentTypeEnum::PAYMENT_TYPE_MOVING->value)
                                        ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                });
                            });
                        }

                        if (!empty($filters['projects'])) {
                            $query->whereHas('distributions', function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            })->with(['distributions' => function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            }]);
                        }

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

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

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


                        $baseQuery($query);
                    });
                break;
            case 'cash':
                $query->where('cashbox', '!=', null)
                    ->with(['payment' => function ($query) use ($baseQuery, $modelId, $filters) {
                        if (!empty($filters['date_from'])) {
                            $dateFrom = $filters['date_from'];
                            $dateTo = !empty($filters['date_to']) ? $filters['date_to'] : Carbon::now();

                            $query->where(function ($q) use ($dateFrom, $dateTo) {
                                $q->where(function ($qActual) use ($dateFrom, $dateTo) {
                                    $qActual->whereNotNull('actual_date')
                                        ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                                })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                    $qPayment->whereNull('actual_date')
                                        ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                });
                            });
                        }

                        if (!empty($filters['projects'])) {
                            $query->whereHas('distributions', function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            })->with(['distributions' => function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            }]);
                        }

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

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

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

                        $baseQuery($query);
                    }])->whereHas('payment', function ($query) use ($baseQuery, $modelId, $filters) {
                        if (!empty($filters['date_from'])) {
                            $dateFrom = $filters['date_from'];
                            $dateTo = !empty($filters['date_to']) ? $filters['date_to'] : Carbon::now();

                            $query->where(function ($q) use ($dateFrom, $dateTo) {
                                $q->where(function ($qActual) use ($dateFrom, $dateTo) {
                                    $qActual->whereNotNull('actual_date')
                                        ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                                })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                    $qPayment->whereNull('actual_date')
                                        ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                });
                            });
                        }

                        if (!empty($filters['projects'])) {
                            $query->whereHas('distributions', function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            })->with(['distributions' => function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            }]);
                        }

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

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

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


                        $baseQuery($query);
                    });
                break;
            case 'cash-credit':
                $query->with(['payment' => function ($query) use ($baseQuery, $modelId, $filters) {
                    $query->where(function ($q) use ($modelId, $filters) {
                        $q->where([
                            'payment_type' => PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value,
                            'model_id' => $modelId])
                            ->orWhere(function ($q2) use ($modelId) {
                                $q2->where([
                                    'payment_type' => PaymentTypeEnum::PAYMENT_TYPE_MOVING->value,
                                    'model_id' => $modelId,
                                    'status' => PaymentStatusEnum::STATUS_RECEIVED->value])
                                    ->whereNotNull('actual_date');
                            });
                    });

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

                        $query->where(function ($q) use ($dateFrom, $dateTo) {
                            $q->where(function ($qActual) use ($dateFrom, $dateTo) {
                                $qActual->whereNotNull('actual_date')
                                    ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                            })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                $qPayment->whereNull('actual_date')
                                    ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                            });
                        });
                    }

                    if (!empty($filters['projects'])) {
                        $query->whereHas('distributions', function ($query) use ($filters) {
                            $query->whereIn('project_id', $filters['projects']);
                        })->with(['distributions' => function ($query) use ($filters) {
                            $query->whereIn('project_id', $filters['projects']);
                        }]);
                    }

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

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

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

                    $baseQuery($query);
                }])
                    ->whereHas('payment', function ($query) use ($baseQuery, $modelId, $filters) {
                        $query->where(function ($q) use ($modelId) {
                            $q->where([
                                'payment_type' => PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value,
                                'model_id' => $modelId])
                                ->orWhere(function ($q2) use ($modelId) {
                                    $q2->where([
                                        'payment_type' => PaymentTypeEnum::PAYMENT_TYPE_MOVING->value,
                                        'model_id' => $modelId,
                                        'status' => PaymentStatusEnum::STATUS_RECEIVED->value])
                                        ->whereNotNull('actual_date');
                                });
                        });

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

                            $query->where(function ($q) use ($dateFrom, $dateTo) {
                                $q->where(function ($qActual) use ($dateFrom, $dateTo) {
                                    $qActual->whereNotNull('actual_date')
                                        ->whereBetween('actual_date', [$dateFrom, $dateTo]);
                                })->orWhere(function ($qPayment) use ($dateFrom, $dateTo) {
                                    $qPayment->whereNull('actual_date')
                                        ->whereBetween('payment_date', [$dateFrom, $dateTo]);
                                });
                            });
                        }

                        if (!empty($filters['projects'])) {
                            $query->whereHas('distributions', function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            })->with(['distributions' => function ($query) use ($filters) {
                                $query->whereIn('project_id', $filters['projects']);
                            }]);
                        }

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

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

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

                        $baseQuery($query);
                    });
                break;
            default:
                break;
        }

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

        return $query->get();
    }


    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->sumTotalAmount($paymentDistributionsCredit);
        $totalDebit = $this->sumTotalAmount($paymentDistributionsDebit);

        $formattedToProjectCredit = $this->sortToProject($paymentDistributionsCredit, $credit = true);
        $formattedToProjectDebit = $this->sortToProject($paymentDistributionsDebit, $credit = false);

        $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->getTotalCash($filters, $modelID);

        return new ResponseDto(
            data: [
                'credit' => [
                    'static' => [
                        'total_amount' => $totalCredit + $totalAmountCreditPayment,
                        'articles' => CashFlowCreditResource::collection($paymentDistributionsCredit),
                        'not_distribution_payments_total_amount' => $totalAmountCreditPayment,
                        'other_data' => [
                            'total_cash' => $totalCash,
                        ],
                    ],
                    'dynamic' => [
                        'projects' => $formattedToProjectCredit,
                    ],
                ],
                'debit' => [
                    'static' => [
                        'total_amount' => $totalDebit + $totalAmountDebitPayment,
                        'articles' => CashFlowDebitResource::collection($paymentDistributionsDebit),
                        'not_distribution_payments_total_amount' => $totalAmountDebitPayment,
                        'other_data' => [
                            'moving' => $totalMoving,
                            'commission' => $totalAmountCashDebitCommissionPayment,
                            'cash' => $totalAmountDebitCashPayment,
                        ],
                    ],
                    'dynamic' => [
                        'projects' => $formattedToProjectDebit,
                    ],
                ],
            ],
            status: true
        );
    }


    public function getShowArticlesGroup($modelId, $articleGroupId, $filters)
    {
        $query = ArticleGroup::query()
            ->where('model_id', $modelId)
            ->where('id', $articleGroupId);

        $articlesCallback = function ($query) use ($filters) {

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

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

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

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

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

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

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

                'paymentDistributions.payment' => function ($query) use ($filters) {
                    if (!empty($filters['accounts'])) {
                        $query->whereIn('account_id', $filters['accounts']);
                    }
                    if (!empty($filters['payments_made'])) {
                        $query->whereIn('status', $filters['payments_made']);
                    }
                    if (!empty($filters['cash'])) {
                        $query->whereIn('payment_type', $filters['cash']);
                    }
                },

                'paymentDistributions.project',
            ]);


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

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

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

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

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

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

        $query->with(['articles' => $articlesCallback]);

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

        $articleGroups = $query->first();

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

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

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

        $counterparties = Counterparty::query()
            ->whereIn('id', $counterpartyIds)
            ->with(['payments' => function ($query) use ($paymentsIds) {
                $query->whereIn('id', $paymentsIds)
                    ->with('distributions.project');
            }])
            ->get();

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


        return $articleGroups;
    }

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


    public function getArticleShow($modelId, $articleId, $filters)
    {
        $query = Article::query()
            ->where('model_id', $modelId)
            ->where('id', $articleId);

        $applyPaymentFilters = function ($query) use ($filters) {

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

                $query->whereBetween('payment_date', [$dateFrom, $dateTo]);
            }

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

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

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

        $query->whereHas('paymentDistributions.payment', $applyPaymentFilters);

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

        $query->with([
            'paymentDistributions.project',
            'paymentDistributions' => function ($query) use ($filters, $applyPaymentFilters) {
                if (!empty($filters['projects'])) {
                    $query->whereIn('project_id', $filters['projects']);
                }
                $query->whereHas('payment', $applyPaymentFilters);
            },
            'paymentDistributions.payment' => function ($query) use ($applyPaymentFilters) {
                $applyPaymentFilters($query);
                $query->with('contragent');
            }
        ]);

        $article = $query->first();

        $counterparties = $this->getUniqueCounterparties($article);

        $article->counterparties = $counterparties;

        return $article;
    }


    public function getUniqueCounterparties($article)
    {
        $uniqueCounterparties = $article->paymentDistributions
            ->pluck('payment.contragent')
            ->unique('id')
            ->values();

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

        $paymentsIds = PaymentDistribution::query()->where('article_id', $article->id)->get('payment_id')->toArray();

        return Counterparty::query()
            ->whereIn('id', $counterpartyIds)
            ->with(['payments' => function ($query) use ($paymentsIds) {
                $query->whereIn('id', $paymentsIds)
                    ->with('distributions');
            }])
            ->get();
    }

    public function getProjectCounterparties($project)
    {
        return $project->paymentDistributions
            ->filter(fn ($pd) => $pd->payment && $pd->payment->contragent)
            ->groupBy(fn ($pd) => $pd->payment->contragent->id)
            ->map(function ($distributions) {
                $counterparty = $distributions->first()->payment->contragent;

                $counterparty->payments = $distributions->map(fn ($pd) => $pd->payment);
                $counterparty->total_amount = $distributions->sum('amount');

                return $counterparty;
            })
            ->values();
    }



    public function getUniqueCounterpariesToPayment($data)
    {
        $counterpartyIds = collect();
        $paymentsIds = collect();

        $data->each(function ($item) use (&$counterpartyIds, &$paymentsIds) {
            if ($item->payment) {
                $counterpartyIds->push($item->payment->counterparty_id);
                $paymentsIds->push($item->payment->id);
            }
        });

        $counterpartyIds = $counterpartyIds->unique();
        $paymentsIds = $paymentsIds->unique();

        $counterparties = Counterparty::query()
            ->whereIn('id', $counterpartyIds)
            ->with(['payments' => function ($query) use ($paymentsIds) {
                $query->whereIn('id', $paymentsIds);
            }])
            ->get();

        return $counterparties;
    }


    public function sumTotalAmount($paymentDistributions)
    {
        $total = 0;

        foreach ($paymentDistributions as $article) {
            foreach ($article->paymentDistributions as $distribution) {
                if ($distribution->payment == null) {
                    continue;
                }

                if (($distribution->payment->payment_type != PaymentTypeEnum::PAYMENT_TYPE_MOVING->value) &&
                    ($distribution->payment->payment_type != PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value)) {
                    $amount = (float)$distribution->amount;
                    $total += $amount;
                }
            }
        }

        return $total;
    }


    public function getPaymentsMade($paymentsMadeInput)
    {
        $mapping = [
            'draft' => [
                PaymentStatusEnum::STATUS_DRAFT->value,
                PaymentStatusEnum::STATUS_FINALIZE->value,
                PaymentStatusEnum::STATUS_FINALIZE_ONE_TWO->value,
                PaymentStatusEnum::STATUS_FINALIZE_TWO_TWO->value,
            ],
            'approve' => [
                PaymentStatusEnum::STATUS_APPROVE->value,
                PaymentStatusEnum::STATUS_AGREE_ONE_TWO->value,
                PaymentStatusEnum::STATUS_AGREE_TWO_TWO->value,
            ],
            'awaiting_payment' => [
                PaymentStatusEnum::STATUS_PAY->value,
                PaymentStatusEnum::STATUS_ISSUE->value,
            ],
            'paid' => [
                PaymentStatusEnum::STATUS_PAID->value,
            ],
            'verified' => [
                PaymentStatusEnum::STATUS_RECEIVED->value,
                PaymentStatusEnum::STATUS_ISSUED->value,
            ],
            'awaiting_deposit' => [
                PaymentStatusEnum::STATUS_SENT->value,
                PaymentStatusEnum::STATUS_MOVING->value,
            ],
        ];

        $paymentsMade = [];
        if (isset($paymentsMadeInput)) {
            if (is_array($paymentsMadeInput)) {
                foreach ($paymentsMadeInput as $input) {
                    if (isset($mapping[$input])) {
                        $paymentsMade = array_merge($paymentsMade, $mapping[$input]);
                    }
                }
                $paymentsMade = array_unique($paymentsMade);
            } elseif (isset($mapping[$paymentsMadeInput])) {
                $paymentsMade = $mapping[$paymentsMadeInput];
            }
        }

        return array_values($paymentsMade);
    }


    public function getPaymentsMadeForApi(array $input): array
    {
        return array_values(array_unique($input));
    }


    public function getScoreAndCashFilter($scoreAndCashInput)
    {
        $paymentTypes = [];
        if (isset($scoreAndCashInput)) {
            if ($scoreAndCashInput == 'cash') {
                $paymentTypes = [
                    PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value
                ];
            } elseif ($scoreAndCashInput == 'score') {
                $paymentTypes = [
                    PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value,
                    PaymentTypeEnum::PAYMENT_TYPE_PAYMENT->value,
                    PaymentTypeEnum::PAYMENT_TYPE_MOVING->value,
                ];
            } elseif ($scoreAndCashInput == 'score_and_cash') {
                $paymentTypes = [
                    PaymentTypeEnum::PAYMENT_TYPE_RECEPTION->value,
                    PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value,
                    PaymentTypeEnum::PAYMENT_TYPE_PAYMENT->value,
                    PaymentTypeEnum::PAYMENT_TYPE_MOVING->value
                ];
            }
        }
        return $paymentTypes;
    }

    public function getFilter($projectsInput)
    {
        if (is_array($projectsInput)) {
            $flattened = array_reduce($projectsInput, function ($carry, $item) {
                if (is_string($item)) {
                    $carry = array_merge($carry, explode(',', $item));
                } elseif (is_array($item)) {
                    $carry = array_merge($carry, $item);
                } elseif ($item !== null) {
                    $carry[] = $item;
                }
                return $carry;
            }, []);

            return array_filter(array_map('trim', $flattened), 'is_numeric');

        } elseif (is_string($projectsInput)) {
            return array_filter(array_map('trim', explode(',', $projectsInput)), 'is_numeric');

        } elseif ($projectsInput !== null) {
            return [(string)$projectsInput];
        }

    }

    public function excelExport($ids)
    {
        $export = new PaymentsExport(Payment::whereHas('distributions', function ($query) use ($ids): void {
            $query->whereIn('article_id', $ids);
        }), $ids);

        $filename = 'payments-' . date('Y-m-d-H-i-s') . '.xlsx';
        Excel::store($export, $filename, 'public');

        return $filename;
    }

    public function moveArticleOrArticleGroupInCashFlow($articleId, $newLine, $articleGroupId): void
    {
        if (isset($articleId) && isset($newLine)) {
            $article = Article::find($articleId);
            if (!$article) {
                return;
            }

            DB::transaction(function () use ($article, $newLine, $articleGroupId) {
                $oldGroupId = $article->article_group_id;
                $oldSort = $article->sort;

                if ($oldGroupId == (int)$articleGroupId) {
                    if ($oldSort == $newLine) {
                        $this->sortArticleAfterCreated($article->article_type, $articleGroupId, $newLine, $article);
                    }

                    if ($oldSort < $newLine) {
                        $this->decrementSortArticleInGroup($article->article_type, $articleGroupId, $oldSort, $newLine);
                    } else {
                        $this->incrementSortArticleInGroup($article->article_type, $articleGroupId, $oldSort, $newLine);
                    }
                } else {
                    $query = Article::where('article_type', $article->article_type);
                    if ($articleGroupId === null) {
                        $query->whereNull('article_group_id');
                    } else {
                        $query->where('article_group_id', $articleGroupId);
                    }
                    $query->where('sort', '>=', $newLine)
                        ->increment('sort');

                    $this->sortArticleInGroupAfterDelete($oldGroupId, $oldSort);
                }

                $article->update([
                    'article_group_id' => $articleGroupId,
                    'sort' => $newLine
                ]);
            });
        }

        if (isset($articleGroupId) && isset($newLine)) {
            $articleGroup = ArticleGroup::find($articleGroupId);

            if (!$articleGroup) {
                return;
            }

            DB::transaction(function () use ($articleGroup, $newLine) {
                $where = [
                    'article_type' => $articleGroup->article_type,
                    'sort' => $newLine,
                ];

                $prevArticle = Article::where('article_group_id', $articleGroup->article_group_id)
                    ->where($where)
                    ->first();


                if ($prevArticle) {
                    $prevArticle->update(['sort' => $articleGroup->sort]);
                    $articleGroup->update(['sort' => $newLine]);
                } else {
                    $articleGroup->update(['sort' => $newLine]);
                }
            });
        }
    }

    public function sumTotalExtradition($articleDebit)
    {
        $totalExtradition = 0.0;

        foreach ($articleDebit as $item) {
            foreach ($item->paymentDistributions as $distribution) {
                $amount = (float)$distribution->amount;
                if ($distribution->payment->payment_type == PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value) {
                    $totalExtradition += $amount;
                }
            }
        }
        return $totalExtradition;
    }


    public function sortArticleAfterCreated($articleType, $articleGroupId, $newLine, $article)
    {
        Article::where('article_type', $articleType)
            ->where('article_group_id', $articleGroupId)
            ->where('sort', '>=', $newLine)
            ->where('id', '!=', $article->id)
            ->increment('sort');
    }


    public function decrementSortArticleInGroup($articleType, $articleGroupId, $oldSort, $newLine)
    {
        return Article::where('article_type', $articleType)
            ->where('article_group_id', $articleGroupId)
            ->where('sort', '>', $oldSort)
            ->where('sort', '<=', $newLine)
            ->decrement('sort');
    }

    public function incrementSortArticleInGroup($articleType, $articleGroupId, $oldSort, $newLine)
    {
        return Article::where('article_type', $articleType)
            ->where('article_group_id', $articleGroupId)
            ->where('sort', '<', $oldSort)
            ->where('sort', '>=', $newLine)
            ->increment('sort');
    }

    public function sortArticleInGroupAfterDelete($oldGroupId, $oldSort)
    {
        return Article::where('article_group_id', $oldGroupId)
            ->where('sort', '>', $oldSort)
            ->decrement('sort');
    }

    public function sortArticleWithoutGroupAfterDelete($oldSort)
    {
        return Article::where('article_group_id', null)
            ->where('sort', '>', $oldSort)
            ->decrement('sort');
    }

    public function getUndistributedPayments($modelId, $type, $filters = [])
    {
        $query = Payment::query();

        $query->where('model_id', $modelId);

        switch ($type) {
            case 'income':
                $query->whereIn('payment_type', [
                    PaymentTypeEnum::PAYMENT_TYPE_RECEPTION_FROM_1C->value]);

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

                break;
            case 'expenses':
                $query->whereIn('payment_type', [
                    PaymentTypeEnum::PAYMENT_TYPE_PAYMENT->value,
                    PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value
                ]);

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

            default :
                break;
        }

        $this->applyPaymentDateFilter($query, $filters);

        $query->with(['organization', 'contragent', 'creator', 'account']);

        $query->whereHas('distributions', function ($subQuery) {
            $subQuery->whereNull('article_id');
        });

        $payments = $query->get();

        $counterparties = Counterparty::whereHas(
            'payments',
            function ($paymentQuery) use ($modelId, $type, $filters) {
                $paymentQuery->where('model_id', $modelId);

                switch ($type) {
                    case 'income':
                        $paymentQuery->whereIn('payment_type', [
                            PaymentTypeEnum::PAYMENT_TYPE_RECEPTION_FROM_1C->value
                        ]);
                        break;
                    case 'expenses':
                        $paymentQuery->whereIn('payment_type', [
                            PaymentTypeEnum::PAYMENT_TYPE_PAYMENT->value,
                            PaymentTypeEnum::PAYMENT_TYPE_ISSUEANCE->value
                        ]);
                        break;
                    default:
                        break;
                }

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

                $this->applyPaymentDateFilter($paymentQuery, $filters);
            }
        )->whereHas(
            'payments.distributions',
            function ($distributionQuery) {
                $distributionQuery->whereNull('article_id');
            }
        )->with('payments.distributions')->get();


        return [
            'count' => $query->count(),
            'total_amount' => $query->sum('amount'),
            'data' => $payments,
            'by_counterparty' => ShowUndistributedPaymentsByCounterpartyResource::make($counterparties),
        ];
    }

    public function applyPaymentDateFilter($query, $filters)
    {
        if (empty($filters['year'])) {
            return;
        }

        $year    = (int) $filters['year'];
        $month   = !empty($filters['month']) ? (int) $filters['month'] : null;
        $day     = !empty($filters['day']) ? (int) $filters['day'] : null;
        $quarter = !empty($filters['quarter']) ? (int) $filters['quarter'] : null;

        $query->whereYear('payment_date', $year);

        if ($month && $day) {
            $query->whereMonth('payment_date', $month)
                ->whereDay('payment_date', $day);
            return;
        }

        if ($month) {
            $query->whereMonth('payment_date', $month);
            return;
        }

        if ($quarter) {
            $quarters = [
                1 => [1, 2, 3],
                2 => [4, 5, 6],
                3 => [7, 8, 9],
                4 => [10, 11, 12],
            ];

            if (isset($quarters[$quarter])) {
                $query->whereIn(
                    DB::raw('EXTRACT(MONTH FROM payment_date)'),
                    $quarters[$quarter]
                );
            }
        }
    }
}