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/Http/Controllers/PaymentConceptConfigController.php
<?php

namespace App\Http\Controllers;

use App\Models\PaymentConcept;
use App\Models\PaymentConceptDetail;
use App\Models\CurrencyType;
use App\Models\PeriodPricingConfig;
use Illuminate\Http\Request;
use Exception;

class PaymentConceptConfigController extends Controller
{
    /**
     * GET /api/payment-concepts
     * Listar todos los conceptos de pago con count de detalles.
     */
    public function listConcepts()
    {
        try {
            $concepts = PaymentConcept::where('deleted', false)
                ->withCount(['details' => fn($q) => $q->where('deleted', false)])
                ->with('currency_type:id,currency_type,currency_symbol')
                ->orderBy('id')
                ->get();

            return genericResponse(['data' => $concepts, 'message' => 'ok', 'code' => 200], 200);
        } catch (Exception $e) {
            return genericResponse(['error' => $e->getMessage()], $e->getCode() ?: 500);
        }
    }

    /**
     * PUT /api/payment-concepts/{id}
     * Actualizar status y code_odoo de un concepto (nombre NO editable).
     */
    public function updateConcept($id, Request $request)
    {
        try {
            $concept = PaymentConcept::where('deleted', false)->findOrFail($id);
            $concept->update($request->only(['status', 'code_odoo']));

            return genericResponse(['data' => $concept, 'message' => 'Concepto actualizado', 'code' => 200], 200);
        } catch (Exception $e) {
            return genericResponse(['error' => $e->getMessage()], $e->getCode() ?: 500);
        }
    }

    /**
     * GET /api/payment-concepts/{id}/details
     * Listar detalles de un concepto.
     */
    public function listDetails($conceptId)
    {
        try {
            $details = PaymentConceptDetail::where('payment_concept_id', $conceptId)
                ->where('deleted', false)
                ->with('currency_type:id,currency_type,currency_symbol')
                ->orderBy('id')
                ->get();

            return genericResponse(['data' => $details, 'message' => 'ok', 'code' => 200], 200);
        } catch (Exception $e) {
            return genericResponse(['error' => $e->getMessage()], $e->getCode() ?: 500);
        }
    }

    /**
     * POST /api/payment-concepts/{id}/details
     * Crear un nuevo detalle de concepto.
     */
    public function storeDetail($conceptId, Request $request)
    {
        $request->validate([
            'description'      => 'required|string|max:255',
            'code'             => 'required|string|max:50',
            'price_regular'    => 'nullable|numeric',
            'currency_type_id' => 'required|integer|exists:currency_types,id',
            'required_element' => 'nullable|boolean',
            'family_payment'   => 'nullable|boolean',
            'status'           => 'nullable|boolean',
            'code_odoo'        => 'nullable|string|max:100',
        ]);

        try {
            // Verificar que no exista otro detalle con el mismo code
            $exists = PaymentConceptDetail::where('code', $request->code)
                ->where('deleted', false)
                ->exists();

            if ($exists) {
                return genericResponse(['error' => "Ya existe un detalle con el código '{$request->code}'"], 422);
            }

            $detail = PaymentConceptDetail::create([
                'payment_concept_id' => $conceptId,
                'description'        => strtoupper($request->description),
                'code'               => strtoupper($request->code),
                'price_regular'      => $request->price_regular,
                'currency_type_id'   => $request->currency_type_id,
                'required_element'   => $request->required_element ?? 1,
                'requires_payment'   => 0,
                'requires_assignment'=> 0,
                'family_payment'     => $request->family_payment ?? 0,
                'status'             => $request->status ?? 1,
                'code_odoo'          => $request->code_odoo,
            ]);

            $detail->load('currency_type:id,currency_type,currency_symbol');

            return genericResponse(['data' => $detail, 'message' => 'Detalle creado', 'code' => 201], 201);
        } catch (Exception $e) {
            return genericResponse(['error' => $e->getMessage()], $e->getCode() ?: 500);
        }
    }

    /**
     * PUT /api/payment-concepts/details/{detailId}
     * Actualizar un detalle.
     */
    public function updateDetail($detailId, Request $request)
    {
        try {
            $detail = PaymentConceptDetail::where('deleted', false)->findOrFail($detailId);

            $fields = $request->only([
                'description', 'code', 'price_regular', 'currency_type_id',
                'required_element', 'family_payment', 'status', 'code_odoo',
            ]);

            if (isset($fields['description'])) $fields['description'] = strtoupper($fields['description']);
            if (isset($fields['code'])) $fields['code'] = strtoupper($fields['code']);

            $detail->update($fields);
            $detail->load('currency_type:id,currency_type,currency_symbol');

            return genericResponse(['data' => $detail, 'message' => 'Detalle actualizado', 'code' => 200], 200);
        } catch (Exception $e) {
            return genericResponse(['error' => $e->getMessage()], $e->getCode() ?: 500);
        }
    }

    /**
     * DELETE /api/payment-concepts/details/{detailId}
     * Eliminar detalle (soft delete). Valida que no tenga period_pricing_configs asociados.
     */
    public function destroyDetail($detailId)
    {
        try {
            $detail = PaymentConceptDetail::where('deleted', false)->findOrFail($detailId);

            // Validar que no tenga configuraciones de precios asociadas
            $hasConfigs = PeriodPricingConfig::where('concept_code', $detail->code)->exists();
            if ($hasConfigs) {
                return genericResponse([
                    'error' => 'No se puede eliminar: este concepto tiene configuraciones de precios por periodo asociadas.',
                ], 422);
            }

            $detail->update([
                'deleted'    => true,
                'deleted_at' => now(),
                'user_deleted' => auth()->id(),
            ]);

            return genericResponse(['data' => null, 'message' => 'Detalle eliminado', 'code' => 200], 200);
        } catch (Exception $e) {
            return genericResponse(['error' => $e->getMessage()], $e->getCode() ?: 500);
        }
    }

    /**
     * GET /api/payment-concepts/meta/currency-types
     * Listar tipos de moneda disponibles.
     */
    public function currencyTypes()
    {
        try {
            $types = CurrencyType::where('status', 1)
                ->select('id', 'currency_type', 'currency_symbol')
                ->orderBy('id')
                ->get();

            return genericResponse(['data' => $types, 'message' => 'ok', 'code' => 200], 200);
        } catch (Exception $e) {
            return genericResponse(['error' => $e->getMessage()], $e->getCode() ?: 500);
        }
    }
}