3030use FacturaScripts \Core \Where ;
3131use FacturaScripts \Dinamic \Lib \Accounting \VatRegularizationToAccounting ;
3232use FacturaScripts \Dinamic \Lib \Modelo303 ;
33+ use FacturaScripts \Dinamic \Lib \Txt303Export ;
3334use FacturaScripts \Dinamic \Model \Asiento ;
35+ use FacturaScripts \Dinamic \Model \Empresa ;
3436use FacturaScripts \Dinamic \Model \FacturaCliente ;
3537use FacturaScripts \Dinamic \Model \FacturaProveedor ;
38+ use FacturaScripts \Dinamic \Model \Join \PartidaImpuestoResumen ;
3639use 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
0 commit comments