Skip to content

Commit 608fa40

Browse files
committed
Harden report board wizard flow
1 parent c33298b commit 608fa40

5 files changed

Lines changed: 106 additions & 80 deletions

File tree

Controller/ListReport.php

Lines changed: 79 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
33
* This file is part of Informes plugin for FacturaScripts
4-
* Copyright (C) 2022-2025 Carlos Garcia Gomez <carlos@facturascripts.com>
4+
* Copyright (C) 2022-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
@@ -21,8 +21,8 @@
2121

2222
use FacturaScripts\Core\Lib\ExtendedController\ListController;
2323
use FacturaScripts\Core\Tools;
24-
use FacturaScripts\Dinamic\Model\CodeModel;
2524
use FacturaScripts\Dinamic\Lib\Informes\ReportGenerator;
25+
use FacturaScripts\Dinamic\Model\CodeModel;
2626
use FacturaScripts\Dinamic\Model\Report;
2727
use FacturaScripts\Dinamic\Model\ReportBoard;
2828

@@ -45,7 +45,7 @@ public function getPageData(): array
4545
return $data;
4646
}
4747

48-
protected function createViews()
48+
protected function createViews(): void
4949
{
5050
$this->createViewsReport();
5151
$this->createViewsReportBoard();
@@ -95,7 +95,7 @@ protected function createViewsReportBoard(string $viewName = 'ListReportBoard'):
9595
->addOrderBy(['featured', 'creationdate'], 'creation-date', 2)
9696
->addSearchFields(['name', 'tag']);
9797

98-
// boton del asistente
98+
// botón del asistente
9999
$this->addButton($viewName, [
100100
'action' => $this->url() . '?action=custom-board-assistant',
101101
'confirm' => false,
@@ -105,16 +105,18 @@ protected function createViewsReportBoard(string $viewName = 'ListReportBoard'):
105105
]);
106106
}
107107

108-
protected function execPreviousAction($action)
108+
protected function execPreviousAction($action): bool
109109
{
110+
switch ($action) {
111+
case 'custom-board-assistant':
112+
return $this->showCustomBoardAssistant();
110113

111-
switch($action) {
112114
case 'generate-boards':
113115
return $this->generateBoardsAction();
114-
case 'custom-board-assistant':
115-
return $this->showCustomBoardAssistant();
116+
116117
case 'process-custom-board':
117118
return $this->processCustomBoardAction();
119+
118120
default:
119121
return parent::execPreviousAction($action);
120122
}
@@ -136,79 +138,75 @@ protected function generateBoardsAction(): bool
136138
}
137139

138140
/**
139-
* Devuelve una lista con las tablas que tienen almenos una columna de tipo date o timestamp y array con las columnas
140-
* Ej:
141-
* return ['nomTabla' => ['colA', 'colB', 'colC'], ...]
142-
*/
143-
private function getTablesWithDate(): array
141+
* Devuelve las tablas que tienen al menos una columna de tipo date o timestamp.
142+
*/
143+
protected function getTablesWithDate(): array
144144
{
145145
$tablesWithDate = [];
146-
$tables = $this->dataBase->getTables();
147-
foreach ($tables as $table) {
148-
$colsWithDate = [];
149-
$cols = $this->dataBase->getColumns($table);
150-
foreach ($cols as $colName => $colData) {
151-
$type = strtolower($colData['type']);
152-
153-
/**
154-
* En la búsqueda mirando en los tipos que devuelve getColumns para todas las tablas he visto que;
155-
* En mariadb (similar a sql) devuelve:
156-
* - date
157-
* - time
158-
* - timestamp
159-
* En postgresql es diferente:
160-
* - date
161-
* - timestamp without time zone
162-
* - time without time zone
163-
*/
164-
if (in_array($type, ['date', 'timestamp', 'timestamp without time zone'])) {
165-
$colsWithDate[] = $colName;
166-
}
146+
foreach ($this->dataBase->getTables() as $table) {
147+
if (!empty($this->getDateColumns($table))) {
148+
$tablesWithDate[] = $table;
167149
}
150+
}
151+
152+
return $tablesWithDate;
153+
}
168154

169-
if (count($colsWithDate) > 0) {
170-
$tablesWithDate[$table] = $colsWithDate;
155+
/**
156+
* Devuelve las columnas date/timestamp de una tabla concreta.
157+
*/
158+
protected function getDateColumns(string $table): array
159+
{
160+
$colsWithDate = [];
161+
foreach ($this->dataBase->getColumns($table) as $colName => $colData) {
162+
$type = strtolower($colData['type']);
163+
164+
/**
165+
* En mariadb:
166+
* - date
167+
* - timestamp
168+
* En postgresql:
169+
* - date
170+
* - timestamp without time zone
171+
*/
172+
if (in_array($type, ['date', 'timestamp', 'timestamp without time zone'])) {
173+
$colsWithDate[] = $colName;
171174
}
172175
}
173176

174-
return $tablesWithDate;
177+
return $colsWithDate;
175178
}
176179

177180
/**
178-
* Crea una pizarra con varias gráficas relacionadas con un campo tipo fecha o timestamp.
179-
* Se tiene que pasar una tabla y fecha válidos.
180-
* Devuelve el código del tablero si está ok o false.
181-
* Se debe usar una transaction desde fuera (por si no se crea correctamente).
182-
*
183-
* El nombre de la pizarra es "Tablero de $column sobre el campo de fecha $table."
184-
* Se crearán informes con altura 250 y ancho 6 en la pizarra con los siguientes nombres:
185-
* - "$tabla, $campo / hora" (Si es timestamp)
186-
* - "$tabla, $campo / semana"
187-
* - "$tabla, $campo / mese"
188-
* - "$tabla, $campo / año"
181+
* Crea una pizarra con gráficas temporales para una tabla y una columna de fecha válidas.
182+
*
183+
* La pizarra generada incluye informes por semana, mes y año; y también por hora
184+
* cuando la columna es de tipo timestamp.
185+
*
186+
* Devuelve la pizarra creada o null si falla. La transacción debe gestionarse desde fuera.
189187
*/
190-
public function createDateBoard(string $table, string $column): bool|ReportBoard
188+
protected function createDateBoard(string $table, string $column): ?ReportBoard
191189
{
192190
$board = new ReportBoard();
193-
$board->name = Tools::lang()->trans('report-board-title-date', ['%column%' => $column, '%table%' => $table]);
191+
$board->name = Tools::trans('report-board-title-date', ['%column%' => $column, '%table%' => $table]);
194192
if (false === $board->save()) {
195193
Tools::log()->error('error-creating-report-board');
196-
return false;
194+
return null;
197195
}
198196

199197
$reportsToCreate = [];
200198

201199
// revisar si es timestamp para añadir la hora
202-
$cols = $this->dataBase->getColumns($table);
200+
$cols = $this->db()->getColumns($table);
203201
$colType = strtolower($cols[$column]['type'] ?? '');
204202
if (in_array($colType, ['timestamp', 'timestamp without time zone'])) {
205-
$reportsToCreate['HOUR'] = Tools::lang()->trans('report-by-hour', ['%column%' => $column, '%table%' => $table]);
203+
$reportsToCreate['HOUR'] = Tools::trans('report-by-hour', ['%column%' => $column, '%table%' => $table]);
206204
}
207205

208206
// añadir las semanas, meses y años
209-
$reportsToCreate['WEEK'] = Tools::lang()->trans('report-by-week', ['%column%' => $column, '%table%' => $table]);
210-
$reportsToCreate['MONTHS'] = Tools::lang()->trans('report-by-month', ['%column%' => $column, '%table%' => $table]);
211-
$reportsToCreate['YEAR'] = Tools::lang()->trans('report-by-year', ['%column%' => $column, '%table%' => $table]);
207+
$reportsToCreate['WEEK'] = Tools::trans('report-by-week', ['%column%' => $column, '%table%' => $table]);
208+
$reportsToCreate['MONTHS'] = Tools::trans('report-by-month', ['%column%' => $column, '%table%' => $table]);
209+
$reportsToCreate['YEAR'] = Tools::trans('report-by-year', ['%column%' => $column, '%table%' => $table]);
212210

213211
$pos = 1;
214212
foreach ($reportsToCreate as $xOp => $name) {
@@ -232,6 +230,13 @@ public function createDateBoard(string $table, string $column): bool|ReportBoard
232230

233231
protected function processCustomBoardAction(): bool
234232
{
233+
if (false === $this->permissions->allowUpdate) {
234+
Tools::log()->warning('permission-denied');
235+
return true;
236+
} elseif (false === $this->validateFormToken()) {
237+
return true;
238+
}
239+
235240
$table = $this->request->queryOrInput('selectedTable', '');
236241
$column = $this->request->queryOrInput('selectedColumn', '');
237242

@@ -240,13 +245,13 @@ protected function processCustomBoardAction(): bool
240245
return false;
241246
}
242247

243-
if (false === $this->dataBase->tableExists($table)) {
248+
if (false === $this->db()->tableExists($table)) {
244249
Tools::log()->error('table-not-found', ['%tableName%' => $table]);
245250
return false;
246251
}
247252

248253
// revisar que exista la columna
249-
$tableCols = $this->dataBase->getColumns($table);
254+
$tableCols = $this->db()->getColumns($table);
250255
if (false === array_key_exists($column, $tableCols)) {
251256
Tools::log()->error('column-not-found', ['%columnName%' => $column, '%tableName%' => $table]);
252257
return false;
@@ -258,37 +263,35 @@ protected function processCustomBoardAction(): bool
258263
return false;
259264
}
260265

261-
// Está todo ok, procesar la petición con los tados recibidos:
262-
$db = $this->db();
263-
$db->beginTransaction();
266+
// Está todo ok, procesar la petición con los datos recibidos.
267+
$this->db()->beginTransaction();
268+
264269
$newBoard = $this->createDateBoard($table, $column);
265-
if (false === $newBoard) {
266-
$db->rollback();
270+
if (null === $newBoard) {
271+
$this->db()->rollback();
267272
Tools::log()->error('error-creating-report-board');
268273
return false;
269274
}
270275

271276
// aceptar la transacción y redirigir al panel
272-
$db->commit();
277+
$this->db()->commit();
278+
273279
$this->redirect($newBoard->url('edit'));
274280

275281
return true;
276282
}
277283

278284
/**
279-
* Muestra el asistente para escoger columnas date y timestamp de las tablas
280-
*
281-
* 2 fases:
282-
* 1. Tabla a escoger
283-
* 2. Columna de la tabla
285+
* Muestra el asistente para generar pizarras predeterminadas o crear una pizarra temporal.
286+
*
287+
* Si se ha seleccionado una tabla, carga sus columnas de fecha para el segundo paso.
284288
*/
285-
protected function showCustomBoardAssistant()
289+
protected function showCustomBoardAssistant(): bool
286290
{
287291
// preparar el formulario
288-
$tablesWithDate = $this->getTablesWithDate();
292+
$tables = $this->getTablesWithDate();
289293

290294
// tabla de tablas
291-
$tables = array_keys($tablesWithDate); // recoger solo claves
292295
$this->twigData['tables'] = array_combine($tables, $tables); // combine para que sean mismo key/value
293296

294297
$selectedTable = $this->request->queryOrInput('selectedTable', '');
@@ -301,12 +304,14 @@ protected function showCustomBoardAssistant()
301304

302305
// asignar en el twig tabla seleccionada
303306
$this->twigData['selectedTable'] = $selectedTable;
304-
307+
305308
// mostrar columnas que son date o datetime
306-
$columns = $tablesWithDate[$selectedTable];
309+
$columns = $this->getDateColumns($selectedTable);
307310
$this->twigData['columns'] = array_combine($columns, $columns); // combine para que sean mismo key/value
308311
}
309312

310313
$this->setTemplate('CustomBoardAssistant');
314+
315+
return true;
311316
}
312317
}

Translation/en_EN.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@
211211
"rent-office": "Office rent MOD 115",
212212
"report-breakdown": "Breakdown report",
213213
"report-breakdown-p": "This report provides information about purchases or sales of products listed by month.",
214+
"report-board-wizard-column-help": "Select the date column from table %table%.",
215+
"report-board-wizard-date-help": "Choose a table and a date field to create a custom board.",
216+
"report-board-wizard-default-help": "Generate the plugin default boards.",
217+
"report-board-title-date": "Board for %table% by %column%",
218+
"report-board-title-date-created": "Board created for %table% by %column%.",
219+
"report-by-hour": "%table%, %column% by hour",
220+
"report-by-month": "%table%, %column% by month",
221+
"report-by-week": "%table%, %column% by week",
222+
"report-by-year": "%table%, %column% by year",
214223
"report-taxes-solutions": "Usually you only need to open that invoice and save it to fix the error",
215224
"report-transport": "Transport",
216225
"report-transport-p": "This report allows you to obtain a list of quantities and products to be loaded by a selected carrier.",
@@ -240,4 +249,4 @@
240249
"voluntary-reserves": "Voluntary reserves",
241250
"without-opening": "Without opening",
242251
"y-operation": "Y operation"
243-
}
252+
}

Translation/es_ES.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@
211211
"rent-office": "Alquiler oficina MOD 115",
212212
"report-breakdown": "Desgloses",
213213
"report-breakdown-p": "Este informe permite obtener información de compras o ventas de productos desglosadas por mes",
214+
"report-board-wizard-column-help": "Selecciona la columna de fecha de la tabla %table%.",
215+
"report-board-wizard-date-help": "Elige una tabla y un campo de fecha para crear una pizarra personalizada.",
216+
"report-board-wizard-default-help": "Genera las pizarras predeterminadas del plugin.",
217+
"report-board-title-date": "Pizarra de %table% por %column%",
218+
"report-board-title-date-created": "Pizarra creada para %table% por %column%.",
219+
"report-by-hour": "%table%, %column% por hora",
220+
"report-by-month": "%table%, %column% por mes",
221+
"report-by-week": "%table%, %column% por semana",
222+
"report-by-year": "%table%, %column% por año",
214223
"report-taxes-solutions": "Normalmente solo debe ir a dicha factura y guardar para corregir el error",
215224
"report-transport": "Transporte",
216225
"report-transport-p": "Este informe permite obtener el listado de cantidades y productos a cargar por un transportista seleccionado",
@@ -240,4 +249,4 @@
240249
"voluntary-reserves": "Reservas voluntarias",
241250
"without-opening": "Sin apertura",
242251
"y-operation": "Operación sobre Y"
243-
}
252+
}

View/CustomBoardAssistant.html.twig

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ Se podría refactorizar o hacer el componente más reutilizable si se pasan como
2727
{# STEP 2 #}
2828
<div class="card-body">
2929
<h5 class="card-title">{{ trans('column') }}</h5>
30-
<p class="card-text text-muted">Selecciona la columna de fecha de la tabla {{ fsc.twigData.selectedTable }}.</p>
30+
<p class="card-text text-muted">
31+
{{ trans('report-board-wizard-column-help', {'%table%': fsc.twigData.selectedTable}) }}
32+
</p>
3133

3234
<form action="{{ fsc.url() }}" method="post">
35+
{{ formToken() }}
3336
<input type="hidden" name="action" value="process-custom-board">
3437
<input type="hidden" name="selectedTable" value="{{ fsc.twigData.selectedTable }}">
3538

@@ -52,7 +55,7 @@ Se podría refactorizar o hacer el componente más reutilizable si se pasan como
5255
<div class="card-body">
5356
<div class="border rounded p-3 mb-4">
5457
<h5 class="card-title">{{ trans('generate') }} {{ trans('default')|lower }}</h5>
55-
<p class="card-text text-muted">Genera las pizarras predeterminadas del plugin.</p>
58+
<p class="card-text text-muted">{{ trans('report-board-wizard-default-help') }}</p>
5659

5760
<form action="{{ fsc.url() }}" method="post">
5861
{{ formToken() }}
@@ -68,7 +71,7 @@ Se podría refactorizar o hacer el componente más reutilizable si se pasan como
6871

6972
<div class="border rounded p-3">
7073
<h5 class="card-title">{{ trans('create') }} {{ trans('reports-board')|lower }} {{ trans('date')|lower }}</h5>
71-
<p class="card-text text-muted">Elige una tabla y un campo de fecha para crear una pizarra personalizada.</p>
74+
<p class="card-text text-muted">{{ trans('report-board-wizard-date-help') }}</p>
7275

7376
<form action="{{ fsc.url() }}" method="get" id="reportForm">
7477
<input type="hidden" name="action" value="custom-board-assistant">

facturascripts.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = 'Informes'
22
description = 'Muestras informes de resultados anual, tesorería y desgloses de compras y ventas.'
3-
version = 3.82
3+
version = 3.9
44
min_version = 2025.6
55
compatible = 'Modelo303,Modelo115,Modelo111'

0 commit comments

Comments
 (0)