Skip to content

Commit 4c3833d

Browse files
Carlos Garciaclaude
andcommitted
Encode TXT export as ISO-8859-1 and use Where instead of DataBaseWhere
- Convert export output to ISO-8859-1 in Txt347Export::export() - Stream the .347 file directly in the response without writing to MyFiles - Replace DataBaseWhere with Where::eq/Where::in in the controller - Add testExportEncodingIsISO88591 to verify byte-level encoding Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent aa4d5c7 commit 4c3833d

4 files changed

Lines changed: 49 additions & 27 deletions

File tree

Controller/Modelo347.php

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
33
* This file is part of Modelo347 plugin for FacturaScripts
4-
* Copyright (C) 2020-2025 Carlos Garcia Gomez <carlos@facturascripts.com>
4+
* Copyright (C) 2020-2026 Carlos Garcia Gomez <carlos@facturascripts.com>
55
*
66
* This program is free software: you can redistribute it and/or modify
77
* it under the terms of the GNU Lesser General Public License as
@@ -20,10 +20,10 @@
2020
namespace FacturaScripts\Plugins\Modelo347\Controller;
2121

2222
use FacturaScripts\Core\Base\Controller;
23-
use FacturaScripts\Core\Base\DataBase\DataBaseWhere;
2423
use FacturaScripts\Core\DataSrc\Ejercicios;
2524
use FacturaScripts\Core\DataSrc\Empresas;
2625
use FacturaScripts\Core\Tools;
26+
use FacturaScripts\Core\Where;
2727
use FacturaScripts\Dinamic\Lib\Export\XLSExport;
2828
use FacturaScripts\Dinamic\Model\Cliente;
2929
use FacturaScripts\Dinamic\Model\Cuenta;
@@ -252,29 +252,25 @@ protected function downloadTxtAction(): void
252252
$this->loadCustomersData();
253253
$this->loadSuppliersData();
254254

255-
// creamos el archivo txt
256-
$exportFile = FS_FOLDER . '/MyFiles/modelo_347_' . $this->codejercicio . '.347';
257-
if (false === file_put_contents($exportFile, Txt347Export::export($this->codejercicio, $this->customersData, $this->suppliersData))) {
258-
Tools::log()->error('cant-save-file', ['%fileName%' => $exportFile]);
259-
return;
260-
}
261-
262-
// descargamos el archivo
263-
$this->response->headers->set('Content-Type', 'text/plain; charset=ISO-8859-1');
264-
$this->response->headers->set('Content-Disposition', 'attachment; filename="' . basename($exportFile) . '"');
265-
$this->response->headers->set('Pragma', 'no-cache');
266-
$this->response->headers->set('Expires', '0');
267-
$this->response->setContent(file_get_contents($exportFile));
268-
269-
// eliminamos el archivo
270-
unlink($exportFile);
255+
// generamos el contenido del archivo
256+
$fileName = 'modelo_347_' . $this->codejercicio . '.347';
257+
$content = Txt347Export::export($this->codejercicio, $this->customersData, $this->suppliersData);
258+
259+
// devolvemos el archivo directamente
260+
$this->response
261+
->header('Content-Type', 'text/plain; charset=ISO-8859-1')
262+
->header('Content-Disposition', 'attachment; filename="' . $fileName . '"')
263+
->header('Pragma', 'no-cache')
264+
->header('Expires', '0')
265+
->setContent($content);
271266
}
272267

273268
protected function getAccountingInfo(Cuenta $cuenta, string $column): array
274269
{
275270
$ejercicio = Ejercicios::get($this->codejercicio);
276271

277-
if (strtolower(FS_DB_TYPE) == 'postgresql') {
272+
$dbType = Tools::config('db_type');
273+
if (strtolower($dbType) == 'postgresql') {
278274
$sql = "select idsubcuenta, codsubcuenta, to_char(fecha,'FMMM') as mes, sum(" . $column . ") as total from partidas p, asientos a"
279275
. " where idsubcuenta IN (select idsubcuenta from subcuentas where idcuenta = " . $this->dataBase->var2str($cuenta->idcuenta) . ")"
280276
. " and p.idasiento = a.idasiento"
@@ -314,15 +310,15 @@ protected function getCustomersDataAccounting(): array
314310
// buscamos las cuentas especiales de clientes de este ejercicio
315311
$cuentaModel = new Cuenta();
316312
$where = [
317-
new DataBaseWhere('codejercicio', $this->codejercicio),
318-
new DataBaseWhere('codcuentaesp', 'CLIENT')
313+
Where::eq('codejercicio', $this->codejercicio),
314+
Where::eq('codcuentaesp', 'CLIENT'),
319315
];
320316
foreach ($cuentaModel->all($where, [], 0, 0) as $cuenta) {
321317
// buscamos las partidas de las subcuentas de esta cuenta
322318
foreach ($this->getAccountingInfo($cuenta, 'debe') as $row) {
323319
// buscamos el cliente de la subcuenta
324320
$cliente = new Cliente();
325-
$where = [new DataBaseWhere('codsubcuenta', $row['codsubcuenta'])];
321+
$where = [Where::eq('codsubcuenta', $row['codsubcuenta'])];
326322
if (false === $cliente->loadWhere($where)) {
327323
// no se ha encontrado el cliente, saltamos
328324
continue;
@@ -448,15 +444,15 @@ protected function getSuppliersDataAccounting(): array
448444
// buscamos las cuentas especiales de proveedores de este ejercicio
449445
$cuentaModel = new Cuenta();
450446
$where = [
451-
new DataBaseWhere('codejercicio', $this->codejercicio),
452-
new DataBaseWhere('codcuentaesp', 'PROVEE,ACREED', 'IN')
447+
Where::eq('codejercicio', $this->codejercicio),
448+
Where::in('codcuentaesp', ['PROVEE', 'ACREED']),
453449
];
454450
foreach ($cuentaModel->all($where, [], 0, 0) as $cuenta) {
455451
// consultamos las partidas de cada subcuenta hija
456452
foreach ($this->getAccountingInfo($cuenta, 'haber') as $row) {
457453
// buscamos el proveedor de la subcuenta
458454
$proveedor = new Proveedor();
459-
$where = [new DataBaseWhere('codsubcuenta', $row['codsubcuenta'])];
455+
$where = [Where::eq('codsubcuenta', $row['codsubcuenta'])];
460456
if (false === $proveedor->loadWhere($where)) {
461457
// no existe, saltamos
462458
continue;

Lib/Txt347Export.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public static function export(string $codejercicio, array $customersData, array
5353
$supplierData = self::getSupplierData();
5454
$companyData = self::getCompanyData();
5555

56-
return $companyData . $customerData . $supplierData;
56+
return mb_convert_encoding($companyData . $customerData . $supplierData, 'ISO-8859-1', 'UTF-8');
5757
}
5858

5959
protected static function checkCifNif(array $item): string

Test/main/Txt347ExportTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,32 @@ public function testExportEmptyData(): void
356356
$this->assertEquals(500, strlen($lines[0]));
357357
}
358358

359+
// -------------------------------------------------------------------------
360+
// export() — codificación ISO-8859-1
361+
// -------------------------------------------------------------------------
362+
363+
public function testExportEncodingIsISO88591(): void
364+
{
365+
$exercise = $this->getFirstExercise();
366+
if ($exercise === null) {
367+
$this->markTestSkipped('No hay ejercicios disponibles');
368+
}
369+
370+
$customer = $this->sampleCustomer();
371+
$customer['cliente'] = 'Empresa Española SL';
372+
373+
$result = Txt347Export::export($exercise->codejercicio, [$customer], []);
374+
375+
// El contenido no debe ser UTF-8 válido (la Ñ en ISO-8859-1 rompe la secuencia UTF-8)
376+
$this->assertFalse(
377+
mb_check_encoding($result, 'UTF-8'),
378+
'El contenido exportado no debe estar en UTF-8'
379+
);
380+
381+
// La Ñ debe estar codificada como el byte 0xD1 (ISO-8859-1)
382+
$this->assertStringContainsString("\xD1", $result, 'La Ñ debe estar codificada como byte 0xD1 (ISO-8859-1)');
383+
}
384+
359385
protected function tearDown(): void
360386
{
361387
$this->logErrors();

facturascripts.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
name = 'Modelo347'
22
description = 'Permite obtener los datos necesarios para el modelo 347 de la hacienda española.'
3-
version = 3.5
3+
version = 3.51
44
min_version = 2025.6

0 commit comments

Comments
 (0)