HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux Bradford-Sitios 6.14.0-1017-azure #17~24.04.1-Ubuntu SMP Mon Dec 1 20:10:50 UTC 2025 x86_64
User: www-data (33)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/matriculas_api_dev/app/Repositories/ContractRepository.php
<?php

namespace App\Repositories;

use App\Models\Contract;
use App\Models\ContractDetail;
use App\Models\StatusContract;
use App\Models\Parents;
use App\Models\StatusPayment;
use App\Models\StatusSignature;
use Database\Seeders\StatusPaymentsSeeder;
use Exception;

class ContractRepository
{
    /**
     * Obtener todos los contratos
     */
    public function getAll($periodId = null, $parentId = null, $limit = null)
    {
        $query = Contract::orderBy('id', 'desc');

        if ($periodId > 0) {
            $query->where('period_id', $periodId);
        }

        if (auth()->check() && auth()->user()->profile->code === 'parent') {
            $parentId = auth()->user()->parent->id ?? 0;
        }

        if ($parentId > 0) {
            $query->where('financial_parent_id', $parentId);
        }

        if ($limit > 0) {
            $query->limit($limit);
        }

        return $query->with(['period.enrollmentPeriods', 'statusContract', 'statusPayment', 'statusSignature'])->get();
    }

    /**
     * Buscar contrato por ID
     */
    public function getById($id, $parentId = null)
    {
        $query = Contract::where('id', $id);


        if (auth()->check() && auth()->user()->profile->code === 'parent') {
            $parentId = auth()->user()->parent->id ?? 0;
        }


        if ($parentId > 0) {
            $query->where('financial_parent_id', $parentId);
        }


        $register = $query->first();
        if (!$register) {
            throw new Exception("Contrato no existe o fue eliminado", 404);
        }

        return $register;
    }

    /**
     * Buscar contrato por ID
     */
    public function getByCode($code, $parentId = null)
    {
        $query = Contract::with('period.enrollmentPeriods')->where('code_contract', $code);

        if (auth()->check() && auth()->user()->profile->code === 'parent') {
            $parentId = auth()->user()->parent->id ?? 0;
        }

        if ($parentId > 0) {
            $query->where('financial_parent_id', $parentId);
        }

        $register = $query->first();
        if (!$register) {
            throw new Exception("Contrato no existe o fue eliminado", 404);
        }

        return $register;
    }

    /**
     * Crear nuevo contrato
     */
    public function create($data)
    {
        // === Validar y obtener datos del apoderado financiero ===
        $financialParentData = null;
        if (!empty($data->financial_parent_id)) {
            $financialParentData = Parents::find($data->financial_parent_id);
            if (!$financialParentData) {
                throw new Exception("Apoderado financiero no existe o fue eliminado", 404);
            }
        }

        // === Validar y obtener datos del apoderado académico ===
        $academicParentData = null;
        if (!empty($data->academic_parent_id)) {
            $academicParentData = Parents::find($data->academic_parent_id);
            if (!$academicParentData) {
                throw new Exception("Apoderado académico no existe o fue eliminado", 404);
            }
        }

        $createData = [
            'code_contract'       => $data->code_contract ?? null,
            'date_contract'       => $data->date_contract ?? now()->format('Y-m-d'),
            'registration_year'   => $data->registration_year ?? date('Y'),
            'status_contract_id'  => $this->getStatus(($data->status_contract ?? 'in_course'), 'contract'),
            'status_payment_id'   => $this->getStatus(($data->status_payment ?? 'pending'), 'payment'),
            'status_signature_id' => $this->getStatus(($data->status_signature ?? 'pending'), 'signature'),
            'total_amount'        => $data->total_amount ?? 0,
            'period_id'           => $data->period_id ?? null,
            'parent_identical'    => $data->parent_identical ?? false,
            'contract_format_id'  => $data->contract_format_id ?? null,
            'contract_format_data' => $data->contract_format_data ?? null,
            'observation'         => $data->observation ?? null,
            'file_code'           => $data->file_code ?? null,
            'file_data'           => $data->file_data ?? null,
            'created_at'          => now(),
            'updated_at'          => now(),
        ];

        // === Datos apoderado financiero ===
        if ($financialParentData) {
            $createData = array_merge($createData, [
                'financial_parent_id'               => $financialParentData->id,
                'financial_parent_rut'              => $financialParentData->rut,
                'financial_parent_first_name'       => strUpper($financialParentData->first_name),
                'financial_parent_second_name'      => strUpper($financialParentData->second_name ?? ''),
                'financial_parent_last_name'        => strUpper($financialParentData->last_name),
                'financial_parent_second_last_name' => strUpper($financialParentData->second_last_name ?? ''),
                'financial_parent_birth_date'       => $financialParentData->birth_date ?? null,
                'financial_parent_email'            => strLower($financialParentData->email ?? null),
                'financial_parent_mobile'           => $financialParentData->mobile ?? null,
                'financial_parent_relationship_id'  => $financialParentData->relationship_id ?? null,
                // 'financial_parent_gender_id'        => $financialParentData->gender_id ?? null,
                'financial_parent_country_id'       => $financialParentData->country_id ?? null,
                'financial_parent_region_id'        => $financialParentData->region_id ?? null,
                'financial_parent_commune_id'       => $financialParentData->commune_id ?? null,
                'financial_parent_address'          => $financialParentData->address ?? null,
                // 'financial_parent_profession'       => $financialParentData->profession ?? null,
            ]);
        }

        // === Datos apoderado académico ===
        if ($academicParentData) {
            $createData = array_merge($createData, [
                'academic_parent_id'               => $academicParentData->id,
                'academic_parent_rut'              => $academicParentData->rut,
                'academic_parent_first_name'       => strUpper($academicParentData->first_name),
                'academic_parent_second_name'      => strUpper($academicParentData->second_name ?? ''),
                'academic_parent_last_name'        => strUpper($academicParentData->last_name),
                'academic_parent_second_last_name' => strUpper($academicParentData->second_last_name ?? ''),
                'academic_parent_birth_date'       => $academicParentData->birth_date ?? null,
                'academic_parent_email'            => strLower($academicParentData->email ?? null),
                'academic_parent_mobile'           => $academicParentData->mobile ?? null,
                'academic_parent_relationship_id'  => $academicParentData->relationship_id ?? null,
                // 'academic_parent_gender_id'        => $academicParentData->gender_id ?? null,
                'academic_parent_country_id'       => $academicParentData->country_id ?? null,
                'academic_parent_region_id'        => $academicParentData->region_id ?? null,
                'academic_parent_commune_id'       => $academicParentData->commune_id ?? null,
                'academic_parent_address'          => $academicParentData->address ?? null,
            ]);
        }

        if (auth()->check()) {
            $createData['user_created'] = auth()->id();
            $createData['user_updated'] = auth()->id();
        }

        return Contract::create($createData);
    }

    public function findOrcreate($code, $data, $periodId)
    {
        $createData = [
            'date_contract' => now()->toDateString(),
            'registration_year' => date('Y'),
            'status_contract_id'  => $this->getStatus('pending_payment', 'contract'),
            'status_payment_id'   => $this->getStatus('pending', 'payment'),
            'status_signature_id' => $this->getStatus('pending', 'signature'),

            // Financial Parent
            'financial_parent_id'               => $data->id,
            'financial_parent_rut'              => $data->rut,
            'financial_parent_first_name'       => strUpper($data->first_name),
            'financial_parent_second_name'      => strUpper($data->second_name ?? ''),
            'financial_parent_last_name'        => strUpper($data->last_name),
            'financial_parent_second_last_name' => strUpper($data->second_last_name ?? ''),
            'financial_parent_birth_date'       => $data->birth_date ?? null,
            'financial_parent_email'            => strLower($data->email ?? null),
            'financial_parent_mobile'           => $data->mobile ?? null,
            'financial_parent_relationship_id'  => $data->relationship_id ?? null,
            'financial_parent_country_id'       => $data->country_id ?? null,
            'financial_parent_region_id'        => $data->region_id ?? null,
            'financial_parent_commune_id'       => $data->commune_id ?? null,
            'financial_parent_address'          => $data->address ?? null,

            // Academic Parent (identical to financial)
            'academic_parent_id'               => $data->id,
            'academic_parent_rut'              => $data->rut,
            'academic_parent_first_name'       => strUpper($data->first_name),
            'academic_parent_second_name'      => strUpper($data->second_name ?? ''),
            'academic_parent_last_name'        => strUpper($data->last_name),
            'academic_parent_second_last_name' => strUpper($data->second_last_name ?? ''),
            'academic_parent_birth_date'       => $data->birth_date ?? null,
            'academic_parent_email'            => strLower($data->email ?? null),
            'academic_parent_mobile'           => $data->mobile ?? null,
            'academic_parent_relationship_id'  => $data->relationship_id ?? null,
            'academic_parent_country_id'       => $data->country_id ?? null,
            'academic_parent_region_id'        => $data->region_id ?? null,
            'academic_parent_commune_id'       => $data->commune_id ?? null,
            'academic_parent_address'          => $data->address ?? null,

            'parent_identical' => true,
            'observation' => 'Contrato generado automáticamente desde carga masiva de deudas.',
            'period_id' => $periodId,
        ];

        if (auth()->check()) {
            $createData['user_created'] = auth()->id();
            $createData['user_updated'] = auth()->id();
        }

        return Contract::firstOrCreate(
            ['code_contract' => $code, 'period_id' => $periodId],
            $createData
        );
    }

    /**
     * Actualizar contrato existente
     */
    public function update($registerData, $data)
    {
        // Si llega texto de apoderado financiero, se actualiza directamente
        if (!empty($data->financial_parent_first_name)) {
            $registerData->financial_parent_first_name = strUpper($data->financial_parent_first_name);
            $registerData->financial_parent_second_name = strUpper($data->financial_parent_second_name ?? '');
            $registerData->financial_parent_last_name = strUpper($data->financial_parent_last_name);
            $registerData->financial_parent_second_last_name = strUpper($data->financial_parent_second_last_name ?? '');
            $registerData->financial_parent_email = strLower($data->financial_parent_email ?? null);
            $registerData->financial_parent_mobile = $data->financial_parent_mobile ?? null;
            $registerData->financial_parent_address = $data->financial_parent_address ?? null;
            // $registerData->financial_parent_profession = $data->financial_parent_profession ?? null;
        }

        // Si llega texto de apoderado académico, se actualiza directamente
        if (!empty($data->academic_parent_first_name)) {
            $registerData->academic_parent_first_name = strUpper($data->academic_parent_first_name);
            $registerData->academic_parent_second_name = strUpper($data->academic_parent_second_name ?? '');
            $registerData->academic_parent_last_name = strUpper($data->academic_parent_last_name);
            $registerData->academic_parent_second_last_name = strUpper($data->academic_parent_second_last_name ?? '');
            $registerData->academic_parent_email = strLower($data->academic_parent_email ?? null);
            $registerData->academic_parent_mobile = $data->academic_parent_mobile ?? null;
            $registerData->academic_parent_address = $data->academic_parent_address ?? null;
        }

        // Otros datos
        $registerData->status_contract_id  = $this->getStatus($data->status_contract ?? 'in_course') ?? $registerData->status_contract_id;
        $registerData->status_payment_id   = $data->status_payment_id ?? $registerData->status_payment_id;
        $registerData->status_signature_id = $data->status_signature_id ?? $registerData->status_signature_id;
        $registerData->total_amount        = $data->total_amount ?? $registerData->total_amount;
        $registerData->observation         = $data->observation ?? $registerData->observation;
        $registerData->updated_at          = now();
        $registerData->user_updated        = auth()->check() ? auth()->id() : $registerData->user_updated;

        $registerData->save();

        return $registerData;
    }

    public function updateParent($registerData, $data)
    {
        // Datos personales
        $registerData->financial_parent_first_name       = strUpper($data->first_name ?? '');
        $registerData->financial_parent_second_name      = strUpper($data->second_name ?? '');
        $registerData->financial_parent_last_name        = strUpper($data->last_name ?? '');
        $registerData->financial_parent_second_last_name = strUpper($data->second_last_name ?? '');

        // Contacto
        $registerData->financial_parent_email   = strLower($data->email ?? null);
        $registerData->financial_parent_mobile  = $data->mobile ?? null;

        // Dirección y profesión
        $registerData->financial_parent_address    = $data->address ?? null;
        // $registerData->financial_parent_profession = $data->profession ?? null;

        // Relaciones e identificadores
        // prepareForValidation() flattens {id:1,name:"X"} → 1, so handle both formats
        $registerData->financial_parent_relationship_id = is_array($data->relationship ?? null) ? ($data->relationship['id'] ?? null) : ($data->relationship ?? $data->relationship_id ?? null);
        // $registerData->financial_parent_gender_id       = is_array($data->gender ?? null)       ? ($data->gender['id'] ?? null)       : ($data->gender ?? $data->gender_id ?? null);
        $registerData->financial_parent_country_id      = is_array($data->country ?? null)      ? ($data->country['id'] ?? null)      : ($data->country ?? $data->country_id ?? null);
        $registerData->financial_parent_region_id       = is_array($data->region ?? null)       ? ($data->region['id'] ?? null)       : ($data->region ?? $data->region_id ?? null);
        $registerData->financial_parent_commune_id      = is_array($data->commune ?? null)      ? ($data->commune['id'] ?? null)      : ($data->commune ?? $data->commune_id ?? null);

        // Guardar contrato
        $registerData->updated_at = now();
        $registerData->save();

        // Sincronizar datos a la tabla parents
        if ($registerData->financial_parent_id) {
            $parent = Parents::find($registerData->financial_parent_id);
            if ($parent) {
                $parent->update(array_filter([
                    'first_name'       => $data->first_name ?? null,
                    'second_name'      => $data->second_name ?? null,
                    'last_name'        => $data->last_name ?? null,
                    'second_last_name' => $data->second_last_name ?? null,
                    'email'            => $data->email ?? null,
                    'mobile'           => $data->mobile ?? null,
                    'address'          => $data->address ?? null,
                    'relationship_id'  => is_array($data->relationship ?? null) ? ($data->relationship['id'] ?? null) : ($data->relationship ?? $data->relationship_id ?? null),
                    'country_id'       => is_array($data->country ?? null) ? ($data->country['id'] ?? null) : ($data->country ?? $data->country_id ?? null),
                    'region_id'        => is_array($data->region ?? null) ? ($data->region['id'] ?? null) : ($data->region ?? $data->region_id ?? null),
                    'commune_id'       => is_array($data->commune ?? null) ? ($data->commune['id'] ?? null) : ($data->commune ?? $data->commune_id ?? null),
                ], fn($v) => $v !== null));
            }
        }

        return $registerData;
    }


    /**
     * Obtener ID de estado por código
     */
    public function getStatus($code = 'in_course', $type = '', $getFirst = false)
    {
        switch ($type) {
            case 'contract':
                $statusData = $getFirst ? StatusContract::orderby('id', 'asc')->first() : StatusContract::where('code', $code)->first();
                break;
            case 'payment':
                $statusData = $getFirst ? StatusPayment::orderby('id', 'asc')->first() : StatusPayment::where('code', $code)->first();
                break;
            case 'signature':
                $statusData = $getFirst ? StatusSignature::orderby('id', 'asc')->first() : StatusSignature::where('code', $code)->first();
                break;
            default:
                throw new Exception("Estado no existe ${type}", 404);
        }

        if (!$statusData) {
            throw new Exception("Estado de contrato no existe o fue eliminado", 404);
        }

        return $statusData->id;
    }


    public function getByPeriodId($periodId)
    {
        $parentId = 0;
        if (auth()->check() && auth()->user()->profile->code === 'parent') {
            $parentId = auth()->user()->parent->id ?? 0;
        }

        if ($parentId < 1) {
            throw new Exception("Usuario debe ser apoderado", 404);
        }

        $query = Contract::where('period_id', $periodId)->where('financial_parent_id', $parentId);

        $register = $query->first();
        if (!$register) {
            false;
        }

        return $register;
    }


    public function getDetailByCode($productCode)
    {
        $register = ContractDetail::where('code_toku', $productCode)->first();
        if (!$register) {
            return false;
        }
        return $register;
    }

    public function validateDetails($contractId): int
    {
        return ContractDetail::query()
            ->join('payment_concept_details as pcd', 'pcd.id', '=', 'contracts_detail.payment_concept_detail_id')
            ->where('contracts_detail.contract_id', $contractId)
            ->where('contracts_detail.paid', false)
            ->where(function ($q) {
                $q->where('pcd.requires_payment', 1)
                  ->orWhere('pcd.description', 'LIKE', 'COLEGIATURA%');
            })
            ->count();
    }

    /**
     * Obtener contratos con colegiatura pendiente del apoderado financiero,
     * para períodos aún no finalizados y contratos no cancelados.
     *
     * Se considera "pendiente" tanto cuando no hay pago iniciado como
     * cuando el pago está en curso en Toku (subscription creada pero aún
     * no activada/cobrada). En ambos casos contracts_detail.paid = false.
     */
    public function getIncompleteEnrollmentsByParent($parentId)
    {
        return Contract::where('financial_parent_id', $parentId)
            ->whereHas('period', function ($q) {
                $q->where('finished', false);
            })
            ->whereHas('statusContract', function ($q) {
                $q->where('code', '!=', 'canceled');
            })
            ->whereHas('details', function ($q) {
                $q->where('paid', false)
                    ->whereHas('paymentConceptDetail.payment_concept', function ($q2) {
                        $q2->where('code', 'colegiatura');
                    });
            })
            ->with([
                'details' => function ($q) {
                    $q->where('paid', false)
                        ->whereHas('paymentConceptDetail.payment_concept', function ($q2) {
                            $q2->where('code', 'colegiatura');
                        })
                        ->with(['student', 'paymentConceptDetail.payment_concept']);
                },
            ])
            ->get();
    }
}