Skip to content

Commit 8260438

Browse files
Daniel Fernández Giménezclaude
andcommitted
feat: 4731 añadir descarga del fichero del modelo 303 para la AEAT
Genera el fichero de texto de posiciones fijas según el diseño de registro oficial DR303 (2026 v1.01) para la presentación telemática. - Lib/Txt303Export: construye cabecera, página 1, página 3 y página DID (domiciliación/devolución, solo si hay IBAN) con el mapa exacto de campos. - Lib/Modelo303: cálculo de la cadena de resultado del régimen general (casillas 64, 66, 69, 71, 87), getSquares() y setCasilla(). - Extension/Table: columnas manuales en regularizacionimpuestos (tipo de declaración, IBAN, casillas sin origen automático en la contabilidad). - Nueva pestaña "Presentación AEAT" (EditPresentacion303) visible solo cuando el registro existe, con las casillas manuales y el botón de descarga. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 148910f commit 8260438

8 files changed

Lines changed: 1193 additions & 54 deletions

File tree

Controller/EditRegularizacionImpuesto.php

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@
3030
use FacturaScripts\Core\Where;
3131
use FacturaScripts\Dinamic\Lib\Accounting\VatRegularizationToAccounting;
3232
use FacturaScripts\Dinamic\Lib\Modelo303;
33+
use FacturaScripts\Dinamic\Lib\Txt303Export;
3334
use FacturaScripts\Dinamic\Model\Asiento;
35+
use FacturaScripts\Dinamic\Model\Empresa;
3436
use FacturaScripts\Dinamic\Model\FacturaCliente;
3537
use FacturaScripts\Dinamic\Model\FacturaProveedor;
38+
use FacturaScripts\Dinamic\Model\Join\PartidaImpuestoResumen;
3639
use FacturaScripts\Dinamic\Model\RegularizacionImpuesto;
3740

3841
/**
@@ -66,6 +69,49 @@ public function getPageData(): array
6669
return $data;
6770
}
6871

72+
/**
73+
* Builds and configures a Modelo303 instance for the given tax settlement record,
74+
* computing the general regime squares plus the manually-entered ones and the result chain.
75+
*
76+
* @param RegularizacionImpuesto $reg
77+
* @return Modelo303
78+
*/
79+
private function buildModelo303(RegularizacionImpuesto $reg): Modelo303
80+
{
81+
$modelo = new Modelo303();
82+
83+
// mismo criterio que las pestañas Compras/Ventas y que el asiento de regularización
84+
$where = $this->commonTaxWhere(SubAccountTools::SPECIAL_GROUP_TAX_ALL, $reg);
85+
$resumen = PartidaImpuestoResumen::all($where, [
86+
'COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)' => 'ASC',
87+
'partidas.codsubcuenta' => 'ASC',
88+
], 0, 0);
89+
90+
// casillas del régimen general (devengado, deducible y resultado 46)
91+
$modelo->loadFromResumen($resumen);
92+
93+
// bases exentas/informativas de ventas (casillas 59, 60 y 122)
94+
$modelo->loadFromSalesInvoices(
95+
(int)$reg->idempresa,
96+
(string)$reg->codejercicio,
97+
(string)$reg->fechainicio,
98+
(string)$reg->fechafin
99+
);
100+
101+
// casillas de introducción manual (sin origen automático en la contabilidad)
102+
foreach (['65', '68', '70', '76', '77', '78', '108', '109', '110', '111', '112'] as $box) {
103+
$value = $reg->{'c' . $box} ?? null;
104+
if ($value !== null) {
105+
$modelo->setCasilla($box, (float)$value);
106+
}
107+
}
108+
109+
// cadena de resultado (casillas 64, 66, 69, 71, 87)
110+
$modelo->computeResultado();
111+
112+
return $modelo;
113+
}
114+
69115
protected function checkInvoicesWithoutAccounting($model): void
70116
{
71117
// si el modelo no existe, no hacemos nada
@@ -100,9 +146,9 @@ protected function checkInvoicesWithoutAccounting($model): void
100146
* @param int $group grupo de cuentas especiales (TAX_ALL, TAX_INPUT, TAX_OUTPUT)
101147
* @return array
102148
*/
103-
private function commonTaxWhere(int $group): array
149+
private function commonTaxWhere(int $group, ?RegularizacionImpuesto $model = null): array
104150
{
105-
$model = $this->getModel();
151+
$model = $model ?? $this->getModel();
106152
$excludedOperations = implode(',', [Asiento::OPERATION_OPENING, Asiento::OPERATION_CLOSING]);
107153

108154
// ids de los asientos de TODAS las regularizaciones (para excluirlos)
@@ -179,6 +225,7 @@ protected function createViews(): void
179225
$this->createViewsTaxLine('ListPartidaImpuesto-1', 'purchases', 'fas fa-sign-in-alt');
180226
$this->createViewsTaxLine('ListPartidaImpuesto-2', 'sales', 'fas fa-sign-out-alt');
181227
$this->createViewsEntryLine();
228+
$this->createViewsPresentacion();
182229
}
183230

184231
/**
@@ -193,6 +240,19 @@ protected function createViewsEntryLine(string $viewName = 'ListPartida'): void
193240
$this->disableButtons($viewName, true);
194241
}
195242

243+
/**
244+
* Add view for the AEAT .303 file presentation data (manual boxes + download button).
245+
*
246+
* @param string $viewName
247+
* @return void
248+
*/
249+
protected function createViewsPresentacion(string $viewName = 'EditPresentacion303'): void
250+
{
251+
$this->addEditView($viewName, 'RegularizacionImpuesto', 'aeat-file-303', 'fa-solid fa-file-arrow-down');
252+
$this->setSettings($viewName, 'btnNew', false);
253+
$this->setSettings($viewName, 'btnDelete', false);
254+
}
255+
196256
/**
197257
* Add view for tax detail list.
198258
*
@@ -253,6 +313,42 @@ private function disableButtons(string $viewName, bool $clickable = false): void
253313
->setSettings('btnPrint', true);
254314
}
255315

316+
/**
317+
* Generates and sends the AEAT .303 text file for download.
318+
*
319+
* @return bool
320+
*/
321+
protected function downloadTxtAction(): bool
322+
{
323+
$reg = new RegularizacionImpuesto();
324+
$code = $this->request->input('code');
325+
if (false === $reg->load($code)) {
326+
Tools::log()->warning('record-not-found');
327+
return true;
328+
}
329+
330+
$empresa = new Empresa();
331+
if (false === $empresa->load($reg->idempresa)) {
332+
Tools::log()->warning('company-not-found');
333+
return true;
334+
}
335+
336+
$modelo = $this->buildModelo303($reg);
337+
$content = Txt303Export::export($reg, $modelo->getSquares(), $empresa);
338+
339+
$fileName = $empresa->cifnif . '_' . date('Y', strtotime((string)$reg->fechainicio))
340+
. '_' . $reg->periodo . '.303';
341+
342+
$this->setTemplate(false);
343+
$this->response->headers->set('Content-Type', 'text/plain; charset=ISO-8859-1');
344+
$this->response->headers->set('Content-Disposition', 'attachment; filename="' . $fileName . '"');
345+
$this->response->headers->set('Pragma', 'no-cache');
346+
$this->response->headers->set('Expires', '0');
347+
$this->response->setContent($content);
348+
349+
return false;
350+
}
351+
256352
/**
257353
* Run the actions that alter data before reading it.
258354
*
@@ -266,6 +362,10 @@ protected function execPreviousAction($action): bool
266362
return true;
267363
}
268364

365+
if ($action === 'download-303') {
366+
return $this->downloadTxtAction();
367+
}
368+
269369
return parent::execPreviousAction($action);
270370
}
271371

@@ -423,6 +523,26 @@ protected function loadData($viewName, $view): void
423523
case 'ListPartidaImpuesto-2':
424524
$this->getListPartidaImpuesto($view, SubAccountTools::SPECIAL_GROUP_TAX_OUTPUT);
425525
break;
526+
527+
case 'EditPresentacion303':
528+
// solo mostramos la pestaña cuando el registro ya existe
529+
$id = $this->getViewModelValue($this->getMainViewName(), 'idregiva');
530+
if (empty($id)) {
531+
$view->setSettings('active', false);
532+
break;
533+
}
534+
535+
$view->loadData($id);
536+
537+
// botón para descargar el fichero .303
538+
$view->addButton([
539+
'action' => 'download-303',
540+
'color' => 'success',
541+
'icon' => 'fa-solid fa-file-arrow-down',
542+
'label' => 'download-file-303',
543+
'type' => 'action',
544+
]);
545+
break;
426546
}
427547
}
428548

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Document : regularizacionimpuestos
4+
Description:
5+
Columnas adicionales para la generación del fichero del modelo 303 (AEAT).
6+
Se fusionan con la tabla del core. Almacenan los datos sin origen automático
7+
en FacturaScripts (tipo de declaración, IBAN y casillas de introducción manual).
8+
-->
9+
<table>
10+
<column>
11+
<name>tipodeclaracion</name>
12+
<type>character varying(1)</type>
13+
</column>
14+
<column>
15+
<name>iban</name>
16+
<type>character varying(34)</type>
17+
</column>
18+
<column>
19+
<name>sinactividad</name>
20+
<type>boolean</type>
21+
<default>false</default>
22+
</column>
23+
<column>
24+
<name>rectificativa</name>
25+
<type>boolean</type>
26+
<default>false</default>
27+
</column>
28+
<column>
29+
<name>justificanteant</name>
30+
<type>character varying(13)</type>
31+
</column>
32+
<!-- Casillas de introducción manual (sin origen automático en la contabilidad) -->
33+
<column>
34+
<name>c65</name>
35+
<type>double precision</type>
36+
<default>100</default>
37+
</column>
38+
<column>
39+
<name>c68</name>
40+
<type>double precision</type>
41+
<default>0</default>
42+
</column>
43+
<column>
44+
<name>c70</name>
45+
<type>double precision</type>
46+
<default>0</default>
47+
</column>
48+
<column>
49+
<name>c76</name>
50+
<type>double precision</type>
51+
<default>0</default>
52+
</column>
53+
<column>
54+
<name>c77</name>
55+
<type>double precision</type>
56+
<default>0</default>
57+
</column>
58+
<column>
59+
<name>c78</name>
60+
<type>double precision</type>
61+
<default>0</default>
62+
</column>
63+
<column>
64+
<name>c108</name>
65+
<type>double precision</type>
66+
<default>0</default>
67+
</column>
68+
<column>
69+
<name>c109</name>
70+
<type>double precision</type>
71+
<default>0</default>
72+
</column>
73+
<column>
74+
<name>c110</name>
75+
<type>double precision</type>
76+
<default>0</default>
77+
</column>
78+
<column>
79+
<name>c111</name>
80+
<type>double precision</type>
81+
<default>0</default>
82+
</column>
83+
<column>
84+
<name>c112</name>
85+
<type>double precision</type>
86+
<default>0</default>
87+
</column>
88+
</table>

0 commit comments

Comments
 (0)