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/gestion-formularios.bdfschool/app/Classes/Odoo/Client/OdooSentPayment.php
<?php

namespace App\Classes\Odoo\Client;

use App\Models\ConceptJournal;
use App\Models\IntegrationOdooResult;
use DOMDocument;
use Exception;

class OdooSentPayment {
    private $data;
    private $uid;
    private $models;
    private $odooDb;
    private $odooApiKey;
    private $ref_year;
    private $resource_name;
    public $result = false;
    public $line_ids = [];
    public $success_ids = [];
    public $errorMessage = '';

    public function __construct($data, $uid, $models, $ref_year, $resource_name, $odooDb, $odooApiKey)
    {
        $this->data = $data;
        $this->uid = $uid;
        $this->models = $models;
        $this->odooApiKey = $odooApiKey;
        $this->odooDb = $odooDb;
        $this->ref_year = $ref_year;
        $this->resource_name = $resource_name;
    }

    public function sent()
    {
        $moves_data = [];
        $resource_ids = [];
        $index = 1;

        foreach ($this->data as $detail) {
            try {
                //Asignación de diario contable
                $concept = strUpper(trim($detail->payment_method));

                $assignment = ConceptJournal::where('concept', $concept)->first();

                if (empty($assignment)) {
                    $this->errorMessage = 'No hay un registro de asignación para concepto ' . $concept;
                    return;
                }
        
                $journal = $assignment->journal;
        
                if (empty($journal)) {
                    $this->errorMessage = 'No hay un diario contable asignado a concepto ' . $assignment->concept;
                    return;
                }

                //Formatear Ref
                $ref = isset($detail->guardian_rut) ? explode('-', $detail->guardian_rut)[0] . '/' . $this->ref_year : '(S/I)/' . $this->ref_year;

                // Datos del pago
                $move_data_line = [
                    'ref' => $ref,  // Referencia del asiento
                    'journal_id' => $journal->odoo_id,  // ID del diario contable en Odoo
                    //'x_studio_alumnoa' => $detail->odoo_student_id ? intval($detail->odoo_student_id) : null, //ID del alumno
                    'x_local_id' => $detail->id,
                    'partner_id' => $detail->odoo_parent_id ? intval($detail->odoo_parent_id) : null,//ID del cliente
                    'payment_method_id' => 1, // Método de pago: manual
                    'amount' => (float) $detail->payment_amount,
                    'currency_id' => 45,
                    'payment_type' => 'inbound',
                    'date' => date('Y-m-d', strtotime($detail->payment_date))

                ];

                $moves_data[] = $move_data_line;
                $resource_ids[] = $detail->id;

                $index++;

            } catch (\Exception $e) {
                $this->errorMessage = 'Error al formatear detalle #' . $detail->id . ' para envío: ' . $e->getMessage();
                return;
            }
        }

        if (empty($moves_data)) {
            return;
        }

        //dd($moves_data);

        if ($this->resource_name == 'UPLOAD STUDENT PAYMENTS') {
            $resource_id = $this->data[0]->upload_student_payment_id;
            $multiple_record = 1;
        } else {
            $resource_id = $this->data[0]->id;
            $multiple_record = 0;
        }

        $integration_result = new IntegrationOdooResult;
        $integration_result->create_body = json_encode($moves_data);
        $integration_result->status = 'PENDING';
        $integration_result->user_created = auth()->user()->id;
        $integration_result->created_at = ahoraServidor();
        $integration_result->resource_name = $this->resource_name;
        $integration_result->multiple_record = $multiple_record;
        $integration_result->resource_id = $resource_id;

        try {
            // Insertar los registros en odoo
            $new_move_ids = $this->models->execute_kw($this->odooDb, $this->uid, $this->odooApiKey, 'account.payment', 'create', array($moves_data));
            
            if (!isset($new_move_ids['faultCode'])) {
                $this->result = true;

                if ($multiple_record == 1) {
                    $odoo_id = null;
                    $odoo_ids = json_encode(array_values($new_move_ids));
                } else {
                    $odoo_id = $new_move_ids[0];
                    $odoo_ids = null;
                }

                // Crear registro
                $integration_result->status = 'COMPLETED';
                $integration_result->integration_at = ahoraServidor();
                $integration_result->request_body = base64_encode(gzcompress(htmlspecialchars($this->formatXml($this->models->_request))));
                $integration_result->code = '200';
                $integration_result->odoo_id = $odoo_id;
                $integration_result->odoo_ids = $odoo_ids;

                // Publicar asientos: en el caso de los pagos, action_post devuelve 'none' lo cual provoca error de interpretación en cliente de ripcord. por eso se usa write para pagos, el efecto es el mismo
                $action_post = $this->models->execute_kw($this->odooDb, $this->uid, $this->odooApiKey, 'account.payment', 'write', [$new_move_ids, ['state' => 'posted']]);

                if (!isset($action_post['faultCode'])) {
                    $integration_result->message = 'Asientos contables de deudas enviados y publicados correctamente';
                    $integration_result->response = htmlspecialchars($this->formatXml($this->models->_response));
                } else {
                    $integration_result->message = 'Asientos contables de deudas enviados correctamente, error al publicar';
                    $integration_result->response = htmlspecialchars($this->formatXml($this->models->_response));
                }

                $integration_result->save();
                
            } else {
                $integration_result->status = 'FAILED';
                $integration_result->integration_at = ahoraServidor();
                $integration_result->request_body = base64_encode(gzcompress(htmlspecialchars($this->formatXml($this->models->_request))));
                $integration_result->code = '400/500';
                $integration_result->message = 'Error al enviar asientos contables de pagos';
                $integration_result->response = htmlspecialchars($this->formatXml($this->models->_response));

                $integration_result->save();

                $this->errorMessage = $new_move_ids['faultString'];
                return;
            }

            $formated_odoo_ids = array_values($new_move_ids);

            // Obtener los ids locales que fueron insertados
            $local_ids = $this->models->execute_kw($this->odooDb, $this->uid, $this->odooApiKey, 'account.payment', 'search_read', array(
                array(['id', 'in', $formated_odoo_ids])
            ), array('fields' => array('x_local_id')));

            $formated_local_ids = array_column($local_ids, 'x_local_id');

            // Enviar array con los ids que fueron enviados exitosamente a odoo
            $this->success_ids = $formated_local_ids;

           
        } catch (\Exception $e) {
            $integration_result->status = 'FAILED';
            $integration_result->integration_at = ahoraServidor();
            $integration_result->code = '400/500';
            $integration_result->message = 'Error al enviar asientos contables de pagos: ' . $e->getMessage();

            $integration_result->save();

            $this->errorMessage = 'Error al enviar asientos contables de pagos: ' . $e->getMessage();
            return;
        }

    }

    private function formatXml($xml)
    {
        // Crear un nuevo objeto DOMDocument
        $dom = new DOMDocument();
        
        // Cargar el XML
        $dom->loadXML($xml);
        
        // Establecer la opción para formato
        $dom->preserveWhiteSpace = false;
        $dom->formatOutput = true;

		$dom->formatOutput = true;
		$dom->loadXML($xml);
		return $dom->saveXML();
    }
}