From 93de69ba9c17ef1558f357671b9164d3fde0cea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20S=C3=A1nchez=20Mart=C3=ADnez?= Date: Tue, 12 May 2026 13:10:26 +0200 Subject: [PATCH 1/4] =?UTF-8?q?A=C3=B1adir=20soporte=20para=20subcuentas?= =?UTF-8?q?=20de=20ingresos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://facturascripts.com/roadmap/4480 - Se ha añadido la funcionalidad para gestionar subcuentas de ingresos en el plugin Modelo130. - Se han creado los métodos `getIncomeSubaccounts` y `addIncomeSubaccount` en Modelo130.php. - Se ha implementado la lógica de autocompletado para subcuentas de ingresos en Modelo130.html.twig. - Se ha actualizado el archivo es_ES.json para incluir la traducción de "Cuentas de ingresos". - Se ha modificado la estructura de la base de datos para incluir el nuevo campo 'tipo' en Subcuenta130. Estos cambios permiten una mejor gestión de las subcuentas de ingresos, mejorando la funcionalidad del plugin. --- Controller/Modelo130.php | 118 ++++++++++++++++++++++++++++++++++++--- Model/Subcuenta130.php | 13 ++++- Table/subcuentas_130.xml | 6 ++ Translation/es_ES.json | 1 + View/Modelo130.html.twig | 106 +++++++++++++++++++++++++++++++++-- 5 files changed, 230 insertions(+), 14 deletions(-) diff --git a/Controller/Modelo130.php b/Controller/Modelo130.php index 9ab123a..57010d1 100644 --- a/Controller/Modelo130.php +++ b/Controller/Modelo130.php @@ -20,9 +20,9 @@ namespace FacturaScripts\Plugins\Modelo130\Controller; use FacturaScripts\Core\Base\Controller; -use FacturaScripts\Core\Base\DataBase\DataBaseWhere; use FacturaScripts\Core\DataSrc\Ejercicios; use FacturaScripts\Core\Tools; +use FacturaScripts\Core\Where; use FacturaScripts\Dinamic\Model\Asiento; use FacturaScripts\Dinamic\Model\Ejercicio; use FacturaScripts\Dinamic\Model\FacturaCliente; @@ -59,6 +59,12 @@ class Modelo130 extends Controller /** @var Subcuenta130 */ public $deductibleSubaccount; + /** @var Partida[] */ + public $incomeEntries = []; + + /** @var Subcuenta130 */ + public $incomeSubaccount; + /** @var string */ public $period = 'T1'; @@ -123,6 +129,12 @@ public function getAllExercises(?int $idempresa): array return $list; } + public function getDeductibleSubaccounts(): array + { + $where = [new Where('tipo', Subcuenta130::TIPO_DEDUCIBLE)]; + return (new Subcuenta130())->all($where, ['codsubcuenta' => 'ASC'], 0, 0); + } + /** * @param string|null $codejercicio * @return Ejercicio @@ -135,6 +147,12 @@ public function getExercise(?string $codejercicio): Ejercicio return $exercise; } + public function getIncomeSubaccounts(): array + { + $where = [new Where('tipo', Subcuenta130::TIPO_INGRESO)]; + return (new Subcuenta130())->all($where, ['codsubcuenta' => 'ASC'], 0, 0); + } + public function getPageData(): array { $data = parent::getPageData(); @@ -158,6 +176,7 @@ public function privateCore(&$response, $user, $permissions) { parent::privateCore($response, $user, $permissions); $this->deductibleSubaccount = new Subcuenta130(); + $this->incomeSubaccount = new Subcuenta130(); $this->paymentMethods = FormaPago::all(); @@ -173,6 +192,12 @@ public function privateCore(&$response, $user, $permissions) case 'delete-deductible-subaccount': return $this->deleteDeductibleSubaccount(); + case 'add-income-subaccount': + return $this->addIncomeSubaccount(); + + case 'delete-income-subaccount': + return $this->deleteIncomeSubaccount(); + case 'gen-accounting': return $this->createAccountingEntry(); } @@ -180,6 +205,7 @@ public function privateCore(&$response, $user, $permissions) $this->loadDates(); $this->loadInvoices(); $this->loadAsientos(); + $this->loadIncomeAsientos(); $this->loadResults(); } @@ -202,6 +228,26 @@ protected function addDeductibleSubaccount(): bool return true; } + protected function addIncomeSubaccount(): bool + { + $this->activeTab = 'income-subaccount'; + + if (false === $this->validateFormToken()) { + return false; + } + + $subaccount = new Subcuenta130(); + $subaccount->codsubcuenta = $this->request->request->get('codsubcuenta'); + $subaccount->tipo = Subcuenta130::TIPO_INGRESO; + if (false === $subaccount->save()) { + Tools::log()->error('record-save-error'); + return false; + } + + Tools::log()->notice('record-updated-correctly'); + return true; + } + protected function autocompleteSubaccount(): void { $this->setTemplate(false); @@ -248,6 +294,29 @@ protected function deleteDeductibleSubaccount(): bool return false; } + protected function deleteIncomeSubaccount(): bool + { + $this->activeTab = 'income-subaccount'; + + if (false === $this->validateFormToken()) { + return false; + } + + $subaccount = new Subcuenta130(); + if (false === $subaccount->load($this->request->request->get('id'))) { + Tools::log()->error('record-not-found'); + return false; + } + + if (false === $subaccount->delete()) { + Tools::log()->error('record-deleted-error'); + return false; + } + + Tools::log()->notice('record-deleted-correctly'); + return false; + } + // Traemos del codejercicio y period elegido idempresa, dateStart y dateEnd protected function loadDates(): void { @@ -289,20 +358,20 @@ protected function loadInvoices(): void $whereFtrasProveedores = [ // Para buscar en el margen de fechas del periodo - new DataBaseWhere('fecha', date('Y-m-d', strtotime($this->dateStart)), '>='), - new DataBaseWhere('fecha', date('Y-m-d', strtotime($this->dateEnd)), '<='), + new Where('fecha', date('Y-m-d', strtotime($this->dateStart)), '>='), + new Where('fecha', date('Y-m-d', strtotime($this->dateEnd)), '<='), // Para buscar ftras solo de la empresa/Ejercicio elegido - new DataBaseWhere('idempresa', $this->idempresa), + new Where('idempresa', $this->idempresa), ]; $whereFtrasClientes = [ // Para buscar en el margen de fechas del periodo - new DataBaseWhere('fecha', date('Y-m-d', strtotime($this->dateStart)), '>='), - new DataBaseWhere('fecha', date('Y-m-d', strtotime($this->dateEnd)), '<='), + new Where('fecha', date('Y-m-d', strtotime($this->dateStart)), '>='), + new Where('fecha', date('Y-m-d', strtotime($this->dateEnd)), '<='), // Para buscar ftras solo de la empresa/Ejercicio elegido - new DataBaseWhere('idempresa', $this->idempresa), + new Where('idempresa', $this->idempresa), ]; // Preparamos el orderBy de como vamos a traer las facturas (fecha + numero ftra) @@ -343,13 +412,42 @@ protected function getAccountingEntrySubaccounts(): array { $codsubs = []; $subaccount130 = new Subcuenta130(); - foreach ($subaccount130->all([], [], 0, 0) as $subaccount) { + $where = [new Where('tipo', Subcuenta130::TIPO_DEDUCIBLE)]; + foreach ($subaccount130->all($where, [], 0, 0) as $subaccount) { $codsubs[] = $subaccount->codsubcuenta; } return self::sanitizeSubaccountCodes($codsubs); } + protected function loadIncomeAsientos(): void + { + $codsubs = []; + $sub = new Subcuenta130(); + $where = [new Where('tipo', Subcuenta130::TIPO_INGRESO)]; + foreach ($sub->all($where, [], 0, 0) as $subaccount) { + $codsubs[] = $subaccount->codsubcuenta; + } + $codsubs = self::sanitizeSubaccountCodes($codsubs); + + if (empty($codsubs)) { + return; + } + + $sql = 'SELECT * FROM ' . Partida::tableName() . ' as p' + . ' LEFT JOIN ' . Asiento::tableName() . ' as a ON p.idasiento = a.idasiento' + . ' WHERE ' . $this->getSqlValueCondition('a.idempresa', $this->idempresa) + . ' AND a.fecha BETWEEN ' . $this->dataBase->var2str(date('Y-m-d', strtotime($this->dateStart))) + . ' AND ' . $this->dataBase->var2str(date('Y-m-d', strtotime($this->dateEnd))) + . ' AND p.codsubcuenta IN (' . $this->getSqlValueList($codsubs) . ')' + . ' AND a.operacion IS ' . $this->dataBase->var2str(Asiento::OPERATION_GENERAL) + . ' ORDER BY numero ASC'; + + foreach ($this->dataBase->select($sql) as $row) { + $this->incomeEntries[] = new Partida($row); + } + } + protected function getSqlValueCondition(string $field, $value): string { if (null === $value) { @@ -391,6 +489,10 @@ protected function loadResults(): void $this->taxbaseRetenciones += $invoice->totalirpf; } + foreach ($this->incomeEntries as $partida) { + $this->taxbaseIngresos += $partida->haber; + } + foreach ($this->supplierInvoices as $invoice) { $this->taxbaseGastos += $invoice->neto; } diff --git a/Model/Subcuenta130.php b/Model/Subcuenta130.php index 3521100..9575d99 100644 --- a/Model/Subcuenta130.php +++ b/Model/Subcuenta130.php @@ -19,11 +19,11 @@ namespace FacturaScripts\Plugins\Modelo130\Model; -use FacturaScripts\Core\Base\DataBase\DataBaseWhere; use FacturaScripts\Core\Session; use FacturaScripts\Core\Template\ModelClass; use FacturaScripts\Core\Template\ModelTrait; use FacturaScripts\Core\Tools; +use FacturaScripts\Core\Where; use FacturaScripts\Dinamic\Model\Subcuenta; use FacturaScripts\Dinamic\Model\User; @@ -31,6 +31,9 @@ class Subcuenta130 extends ModelClass { use ModelTrait; + const TIPO_DEDUCIBLE = 'deducible'; + const TIPO_INGRESO = 'ingreso'; + /** @var string */ public $codsubcuenta; @@ -52,10 +55,13 @@ class Subcuenta130 extends ModelClass /** @var string */ public $nick; + /** @var string */ + public $tipo = self::TIPO_DEDUCIBLE; + public function getSubcuenta(): Subcuenta { $subcuenta = new Subcuenta(); - $where = [new DataBaseWhere('codsubcuenta', $this->codsubcuenta)]; + $where = [new Where('codsubcuenta', $this->codsubcuenta)]; $subcuenta->loadWhere($where); return $subcuenta; } @@ -89,6 +95,9 @@ public function test(): bool $this->codsubcuenta = trim(Tools::noHtml((string)$this->codsubcuenta)); $this->name = Tools::noHtml($this->name); + $this->tipo = in_array($this->tipo, [self::TIPO_DEDUCIBLE, self::TIPO_INGRESO], true) + ? $this->tipo + : self::TIPO_DEDUCIBLE; if (strlen($this->codsubcuenta) < 1 || strlen($this->codsubcuenta) > 15) { Tools::log()->warning('invalid-column-lenght', [ diff --git a/Table/subcuentas_130.xml b/Table/subcuentas_130.xml index 191e884..0bd5775 100644 --- a/Table/subcuentas_130.xml +++ b/Table/subcuentas_130.xml @@ -30,6 +30,12 @@ nick character varying(50) + + tipo + character varying(20) + 'deducible' + NO + subcuentas_130_pkey PRIMARY KEY (id) diff --git a/Translation/es_ES.json b/Translation/es_ES.json index 7910578..afd4aed 100644 --- a/Translation/es_ES.json +++ b/Translation/es_ES.json @@ -1,6 +1,7 @@ { "after-deduct": "sobre casilla 04", "deductible-subaccounts": "Cuentas deducibles", + "income-subaccounts": "Cuentas de ingresos", "model-130": "Modelo 130", "model-130-desc": "Se calcula sumando y acumulando cada trimestre, por ejemplo, si visualizamos el 3º trimestre veremos la suma de los trimestres 1, 2 y 3.", "model-130-p": "El Modelo 130 es una declaración trimestral del impuesto de la renta de las personas físicas (IRPF) en el que se liquida el pago fraccionado de este impuesto, a cuenta de la declaración anual que se realiza el año siguiente.", diff --git a/View/Modelo130.html.twig b/View/Modelo130.html.twig index a1fb4db..62be020 100644 --- a/View/Modelo130.html.twig +++ b/View/Modelo130.html.twig @@ -43,6 +43,40 @@ return false; } }); + + $("#findIncomeSubaccount").autocomplete({ + source: function (request, response) { + $.ajax({ + method: "POST", + url: '{{ fsc.url() }}', + data: {action: 'autocomplete-subaccount', term: request.term}, + dataType: "json", + success: function (results) { + let values = []; + results.forEach(function (element) { + if (element.key === null || element.key === element.value) { + values.push(element); + } else { + values.push({key: element.key, value: element.key + " | " + element.value}); + } + }); + response(values); + }, + error: function (msg) { + alert(msg.status + " " + msg.responseText); + } + }); + }, + select: function (event, ui) { + if (ui.item.key !== null) { + $('form[name="add-income-subaccount"] input[name="codsubcuenta"]').val(ui.item.key); + } + }, + open: function (event, ui) { + $(this).autocomplete('widget').css('z-index', 1500); + return false; + } + }); }); {% endblock %} @@ -241,12 +275,28 @@ id="deductible-subaccounts-tab" data-bs-toggle="tab" href="#deductible-subaccounts" role="tab" aria-controls="deductible-subaccounts" - aria-selected="{{ fsc.tabDeductibleSubaccount ? 'true' : 'false' }}" + aria-selected="{{ fsc.activeTab == 'deductible-subaccount' ? 'true' : 'false' }}" title="{{ trans('deductible-subaccounts') }}"> {{ trans('deductible-subaccounts') }} - {% if fsc.deductibleSubaccount.count() %} - {{ fsc.deductibleSubaccount.count() }} + {% set deductibleList = fsc.getDeductibleSubaccounts() %} + {% if deductibleList|length %} + {{ deductibleList|length }} + {% endif %} + + + @@ -469,7 +519,7 @@ - {% for subaccount in fsc.deductibleSubaccount.all({}, {'codsubcuenta':'ASC'}, 0, 0) %} + {% for subaccount in fsc.getDeductibleSubaccounts() %} {{ subaccount.codsubcuenta }} {{ subaccount.getSubcuenta().descripcion }} @@ -505,6 +555,54 @@ +
+
+ + + + + + + + + + {% for subaccount in fsc.getIncomeSubaccounts() %} + + + + + + {% endfor %} + +
{{ trans('code') }}{{ trans('description') }}
{{ subaccount.codsubcuenta }}{{ subaccount.getSubcuenta().descripcion }} +
+ {{ formToken() }} + + + +
+
+
+
+ {{ formToken() }} + + +
+
+
+ + +
+
+
+
+
From 6a143b038a14611d9373513860aa5785d08632f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20S=C3=A1nchez=20Mart=C3=ADnez?= Date: Tue, 12 May 2026 13:43:30 +0200 Subject: [PATCH 2/4] Tests: cubrir subcuentas tipo ingreso y carga de asientos de ingresos en modelo 130 Co-Authored-By: Claude Sonnet 4.6 --- Test/main/Model/Subcuenta130Test.php | 62 ++++++++++++ Test/main/Modelo130ControllerTest.php | 131 ++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) diff --git a/Test/main/Model/Subcuenta130Test.php b/Test/main/Model/Subcuenta130Test.php index eb4a91b..c16b755 100644 --- a/Test/main/Model/Subcuenta130Test.php +++ b/Test/main/Model/Subcuenta130Test.php @@ -82,6 +82,68 @@ public function testCreateEmptySubcuenta130Fails(): void $this->assertFalse($subcuenta130->save(), 'empty-subcuenta130-should-fail'); } + public function testSubcuenta130TieneTipoDeduciblePorDefecto(): void + { + $sub130 = new Subcuenta130(); + $this->assertSame(Subcuenta130::TIPO_DEDUCIBLE, $sub130->tipo); + } + + public function testTipoInvalidoFuerzaTipoDeducible(): void + { + $exercise = $this->getRandomExercise(); + + $account = new Cuenta(); + $account->codcuenta = '9998'; + $account->codejercicio = $exercise->codejercicio; + $account->descripcion = 'Test tipo invalido'; + $this->assertTrue($account->save(), 'cant-save-account'); + + $subaccount = new Subcuenta(); + $subaccount->codcuenta = $account->codcuenta; + $subaccount->codejercicio = $exercise->codejercicio; + $subaccount->codsubcuenta = '9998000000'; + $subaccount->descripcion = 'Test tipo invalido'; + $this->assertTrue($subaccount->save(), 'cant-save-subaccount'); + + $sub130 = new Subcuenta130(); + $sub130->codsubcuenta = $subaccount->codsubcuenta; + $sub130->tipo = 'tipo_invalido_xyz'; + $this->assertTrue($sub130->save(), 'cant-save-subcuenta130'); + $this->assertSame(Subcuenta130::TIPO_DEDUCIBLE, $sub130->tipo, 'invalid-tipo-should-fallback-to-deducible'); + + $this->assertTrue($sub130->delete()); + $this->assertTrue($subaccount->delete()); + $this->assertTrue($account->delete()); + } + + public function testCreateSubcuenta130ConTipoIngreso(): void + { + $exercise = $this->getRandomExercise(); + + $account = new Cuenta(); + $account->codcuenta = '9997'; + $account->codejercicio = $exercise->codejercicio; + $account->descripcion = 'Test ingreso'; + $this->assertTrue($account->save(), 'cant-save-account'); + + $subaccount = new Subcuenta(); + $subaccount->codcuenta = $account->codcuenta; + $subaccount->codejercicio = $exercise->codejercicio; + $subaccount->codsubcuenta = '9997000000'; + $subaccount->descripcion = 'Test ingreso'; + $this->assertTrue($subaccount->save(), 'cant-save-subaccount'); + + $sub130 = new Subcuenta130(); + $sub130->codsubcuenta = $subaccount->codsubcuenta; + $sub130->tipo = Subcuenta130::TIPO_INGRESO; + $this->assertTrue($sub130->save(), 'cant-save-subcuenta130-ingreso'); + $this->assertSame(Subcuenta130::TIPO_INGRESO, $sub130->tipo, 'tipo-should-be-ingreso'); + + $this->assertTrue($sub130->delete()); + $this->assertTrue($subaccount->delete()); + $this->assertTrue($account->delete()); + } + protected function getRandomExercise(): Ejercicio { $model = new Ejercicio(); diff --git a/Test/main/Modelo130ControllerTest.php b/Test/main/Modelo130ControllerTest.php index cbe5e1f..d52115b 100644 --- a/Test/main/Modelo130ControllerTest.php +++ b/Test/main/Modelo130ControllerTest.php @@ -19,6 +19,12 @@ namespace FacturaScripts\Test\Plugins; +use FacturaScripts\Dinamic\Model\Asiento; +use FacturaScripts\Dinamic\Model\Cuenta; +use FacturaScripts\Dinamic\Model\Ejercicio; +use FacturaScripts\Dinamic\Model\Partida; +use FacturaScripts\Dinamic\Model\Subcuenta; +use FacturaScripts\Dinamic\Model\Subcuenta130; use FacturaScripts\Plugins\Modelo130\Controller\Modelo130; use FacturaScripts\Test\Traits\DefaultSettingsTrait; use FacturaScripts\Test\Traits\LogErrorsTrait; @@ -32,6 +38,7 @@ final class Modelo130ControllerTest extends TestCase public static function setUpBeforeClass(): void { self::setDefaultSettings(); + self::installAccountingPlan(); } public function testSanitizeSubaccountCodesRemovesEmptyAndDuplicateValues(): void @@ -62,6 +69,118 @@ public function testGetSqlValueListQuotesValues(): void $this->assertSame("'6400000000','9999.1'", $controller->sqlValueList(['6400000000', '9999.1'])); } + public function testGetDeductibleSubaccountsFiltraPorTipo(): void + { + $sub130Deducible = new Subcuenta130(); + $sub130Deducible->codsubcuenta = 'test_deducible'; + $sub130Deducible->tipo = Subcuenta130::TIPO_DEDUCIBLE; + $this->assertTrue($sub130Deducible->save(), 'cant-save-deducible'); + + $sub130Ingreso = new Subcuenta130(); + $sub130Ingreso->codsubcuenta = 'test_ingreso'; + $sub130Ingreso->tipo = Subcuenta130::TIPO_INGRESO; + $this->assertTrue($sub130Ingreso->save(), 'cant-save-ingreso'); + + $controller = new Modelo130TestAccess(Modelo130TestAccess::class); + $codes = array_map(fn($s) => $s->codsubcuenta, $controller->getDeductibleSubaccounts()); + + $this->assertContains('test_deducible', $codes, 'deducible-should-be-in-list'); + $this->assertNotContains('test_ingreso', $codes, 'ingreso-should-not-be-in-deductible-list'); + + $this->assertTrue($sub130Deducible->delete()); + $this->assertTrue($sub130Ingreso->delete()); + } + + public function testGetIncomeSubaccountsFiltraPorTipo(): void + { + $sub130Deducible = new Subcuenta130(); + $sub130Deducible->codsubcuenta = 'test2_deducible'; + $sub130Deducible->tipo = Subcuenta130::TIPO_DEDUCIBLE; + $this->assertTrue($sub130Deducible->save(), 'cant-save-deducible'); + + $sub130Ingreso = new Subcuenta130(); + $sub130Ingreso->codsubcuenta = 'test2_ingreso'; + $sub130Ingreso->tipo = Subcuenta130::TIPO_INGRESO; + $this->assertTrue($sub130Ingreso->save(), 'cant-save-ingreso'); + + $controller = new Modelo130TestAccess(Modelo130TestAccess::class); + $codes = array_map(fn($s) => $s->codsubcuenta, $controller->getIncomeSubaccounts()); + + $this->assertContains('test2_ingreso', $codes, 'ingreso-should-be-in-list'); + $this->assertNotContains('test2_deducible', $codes, 'deducible-should-not-be-in-income-list'); + + $this->assertTrue($sub130Deducible->delete()); + $this->assertTrue($sub130Ingreso->delete()); + } + + public function testIncomeEntriesSeAcumulanEnTaxbaseIngresos(): void + { + // conseguir un ejercicio del año actual + $ejercicio = new Ejercicio(); + $ejercicio->loadFromDate(date('d-m-Y')); + $this->assertNotEmpty($ejercicio->codejercicio, 'no-exercise-found'); + + // crear cuenta y subcuenta de ingreso (9996 para no colisionar con el plan contable) + $account = new Cuenta(); + $account->codcuenta = '9996'; + $account->codejercicio = $ejercicio->codejercicio; + $account->descripcion = 'Test subvención ingreso modelo130'; + $this->assertTrue($account->save(), 'cant-save-account'); + + $subcuenta = new Subcuenta(); + $subcuenta->codcuenta = '9996'; + $subcuenta->codejercicio = $ejercicio->codejercicio; + $subcuenta->codsubcuenta = '9996000000'; + $subcuenta->descripcion = 'Test subvención ingreso modelo130'; + $this->assertTrue($subcuenta->save(), 'cant-save-subcuenta'); + + // crear asiento con partida haber=1500 (simula una subvención de explotación) + $asiento = new Asiento(); + $asiento->concepto = 'Test subvención Kit Digital modelo130'; + $asiento->fecha = date('Y') . '-02-15'; + $asiento->importe = 1500; + $asiento->idempresa = $ejercicio->idempresa; + $asiento->codejercicio = $ejercicio->codejercicio; + $this->assertTrue($asiento->save(), 'cant-save-asiento'); + + $partida = new Partida(); + $partida->idasiento = $asiento->idasiento; + $partida->codsubcuenta = '9996000000'; + $partida->concepto = 'Test subvención Kit Digital modelo130'; + $partida->haber = 1500.0; + $this->assertTrue($partida->save(), 'cant-save-partida'); + + // registrar la subcuenta como ingreso en el modelo 130 + $sub130 = new Subcuenta130(); + $sub130->codsubcuenta = '9996000000'; + $sub130->tipo = Subcuenta130::TIPO_INGRESO; + $this->assertTrue($sub130->save(), 'cant-save-subcuenta130'); + + // configurar el controlador con el periodo T1 del año actual + $controller = new Modelo130TestAccess(Modelo130TestAccess::class); + $controller->setTestContext( + $ejercicio->idempresa, + date('01-01-Y'), + date('31-03-Y') + ); + + // ejecutar la carga de asientos de ingresos + $controller->callLoadIncomeAsientos(); + + // verificar que la partida aparece en incomeEntries + $this->assertNotEmpty($controller->incomeEntries, 'income-entries-should-not-be-empty'); + + $haberTotal = array_sum(array_map(fn($p) => (float)$p->haber, $controller->incomeEntries)); + $this->assertEquals(1500.0, $haberTotal, 'income-haber-total-should-be-1500'); + + // cleanup + $this->assertTrue($sub130->delete()); + $this->assertTrue($partida->delete()); + $this->assertTrue($asiento->delete()); + $this->assertTrue($subcuenta->delete()); + $this->assertTrue($account->delete()); + } + protected function tearDown(): void { $this->logErrors(); @@ -84,4 +203,16 @@ public function sqlValueList(array $values): string { return $this->getSqlValueList($values); } + + public function setTestContext(int $idempresa, string $dateStart, string $dateEnd): void + { + $this->idempresa = $idempresa; + $this->dateStart = $dateStart; + $this->dateEnd = $dateEnd; + } + + public function callLoadIncomeAsientos(): void + { + $this->loadIncomeAsientos(); + } } From a3b4b240216f0ccffff4af2bfc71ba3e669fdca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20S=C3=A1nchez=20Mart=C3=ADnez?= Date: Tue, 12 May 2026 14:29:07 +0200 Subject: [PATCH 3/4] =?UTF-8?q?Refactoriza=20prueba=20de=20acumulaci=C3=B3?= =?UTF-8?q?n=20de=20ingresos=20en=20Modelo130ControllerTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Modifica la forma de obtener el ejercicio actual en la prueba testIncomeEntriesSeAcumulanEnTaxbaseIngresos. - Cambia la carga del ejercicio de un método basado en la fecha a uno que garantiza que el ejercicio existe en la base de datos. --- Test/main/Modelo130ControllerTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Test/main/Modelo130ControllerTest.php b/Test/main/Modelo130ControllerTest.php index d52115b..472fbe7 100644 --- a/Test/main/Modelo130ControllerTest.php +++ b/Test/main/Modelo130ControllerTest.php @@ -115,10 +115,10 @@ public function testGetIncomeSubaccountsFiltraPorTipo(): void public function testIncomeEntriesSeAcumulanEnTaxbaseIngresos(): void { - // conseguir un ejercicio del año actual - $ejercicio = new Ejercicio(); - $ejercicio->loadFromDate(date('d-m-Y')); - $this->assertNotEmpty($ejercicio->codejercicio, 'no-exercise-found'); + // conseguir un ejercicio existente (all() garantiza que el codejercicio está en BD) + $ejercicios = (new Ejercicio())->all([], ['codejercicio' => 'DESC'], 0, 1); + $this->assertNotEmpty($ejercicios, 'no-exercise-found'); + $ejercicio = $ejercicios[0]; // crear cuenta y subcuenta de ingreso (9996 para no colisionar con el plan contable) $account = new Cuenta(); From 8aa170c77052a76ef79cb274b6b4bfefdd896684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20S=C3=A1nchez=20Mart=C3=ADnez?= Date: Tue, 12 May 2026 15:00:39 +0200 Subject: [PATCH 4/4] =?UTF-8?q?A=C3=B1adir=20m=C3=A9todo=20clear()=20en=20?= =?UTF-8?q?Subcuenta130?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se ha añadido el método clear() en la clase Subcuenta130 para restablecer el valor de la propiedad tipo a TIPO_DEDUCIBLE al limpiar la instancia. --- Model/Subcuenta130.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Model/Subcuenta130.php b/Model/Subcuenta130.php index 9575d99..adc305a 100644 --- a/Model/Subcuenta130.php +++ b/Model/Subcuenta130.php @@ -58,6 +58,12 @@ class Subcuenta130 extends ModelClass /** @var string */ public $tipo = self::TIPO_DEDUCIBLE; + public function clear(): void + { + parent::clear(); + $this->tipo = self::TIPO_DEDUCIBLE; + } + public function getSubcuenta(): Subcuenta { $subcuenta = new Subcuenta();