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