Skip to content

Commit 70b98d6

Browse files
authored
Merge pull request #83 from EstebanSM85/Tarea4565-informe-proveedores
Tarea4565 informe proveedores
2 parents 561105c + f3745fe commit 70b98d6

4 files changed

Lines changed: 716 additions & 2 deletions

File tree

Controller/ReportSupplier.php

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
<?php
2+
/**
3+
* This file is part of Informes plugin for FacturaScripts
4+
* Copyright (C) 2026 Carlos Garcia Gomez <carlos@facturascripts.com>
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Lesser General Public License as
8+
* published by the Free Software Foundation, either version 3 of the
9+
* License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
namespace FacturaScripts\Plugins\Informes\Controller;
21+
22+
/**
23+
* Controlador para generar un informe de proveedores con diferentes métricas (activos, por país, acreedores, etc.)
24+
*
25+
* @author Esteban Sánchez Martínez
26+
*/
27+
28+
use FacturaScripts\Core\Base\Controller;
29+
use FacturaScripts\Core\Tools;
30+
use FacturaScripts\Dinamic\Model\Empresa;
31+
use FacturaScripts\Dinamic\Model\Pais;
32+
use FacturaScripts\Plugins\Informes\Model\Report;
33+
34+
class ReportSupplier extends Controller
35+
{
36+
/** @var array todas las empresas a listar en el formulario [idEmpresa => nombre empresa] */
37+
public $companies = [];
38+
39+
/** @var int|string|null el idempresa sugerido por el usuario (puede ser 'all') */
40+
public $idempresa;
41+
42+
/** @var int Total de proveedores */
43+
public $totalSuppliers;
44+
45+
/** @var int Proveedores activos en el último año */
46+
public $activeSupplier;
47+
48+
/** @var int Proveedores activos en el año actual */
49+
public $activeSuppliersYear;
50+
51+
/** @var int Proveedores marcados como baja */
52+
public $inactiveSuppliers;
53+
54+
/** @var int Nuevos proveedores en los últimos 30 días */
55+
public $newSuppliers30Days;
56+
57+
/** @var int Proveedores con facturas pendientes de pago */
58+
public $suppliersWithPayables;
59+
60+
/** @var array Reportes*/
61+
public $charts = [];
62+
63+
/** @var array Resultado agrupado por país */
64+
public $suppliersByCountry;
65+
66+
/** @var string Nombre del país de la empresa */
67+
public $companyCountry;
68+
69+
/** @var string Código del país de la empresa */
70+
public $companyCountryCode;
71+
72+
/** @var string Año actual */
73+
protected $currentYear;
74+
75+
/** @var string Filtros WHERE para facturasprov según empresa */
76+
protected $whereEmpresaFacturasProv = '';
77+
78+
/** @var string Filtros WHERE para proveedores según empresa */
79+
protected $whereEmpresaProveedores = '';
80+
81+
82+
public function getPageData(): array
83+
{
84+
$data = parent::getPageData();
85+
$data['menu'] = 'reports';
86+
$data['title'] = 'supplier';
87+
$data['icon'] = 'fa-solid fa-users';
88+
return $data;
89+
}
90+
91+
public function privateCore(&$response, $user, $permissions)
92+
{
93+
parent::privateCore($response, $user, $permissions);
94+
95+
//Cargamos las empresas
96+
$this->loadCompanies();
97+
98+
//Cargamos datos las gráficas
99+
$this->loadData();
100+
$this->loadTotalSuppliers();
101+
$this->loadActiveSupplier();
102+
$this->loadInactiveSupplier();
103+
$this->loadActiveSupplierYear();
104+
$this->loadNewSuppliers30Days();
105+
$this->loadSuppliersWithPayables();
106+
$this->loadSuppliersByCountry();
107+
$this->loadSuppliersByProvince();
108+
$this->loadNewSuppliersByMonth();
109+
$this->loadNewSuppliersByYear();
110+
$this->loadInvoicesByProvince();
111+
$this->loadCreditors();
112+
$this->loadTopCreditors();
113+
}
114+
115+
protected function loadCompanies(): void
116+
{
117+
$this->companies = ['all' => Tools::trans('all-companies')];
118+
foreach (Empresa::all() as $company) {
119+
$this->companies[$company->idempresa] = $company->nombrecorto;
120+
}
121+
// empresa seleccionada
122+
$this->idempresa = $this->request()->queryOrInput('idempresa', null);
123+
if (null === $this->idempresa) {
124+
// seleccionar todas por defecto si no hay nada
125+
$this->idempresa = 'all';
126+
} elseif ($this->idempresa === 'all') {
127+
// seleccionar todas
128+
$this->idempresa = 'all';
129+
} else {
130+
// seleccionar sugerida
131+
$this->idempresa = (int)$this->idempresa;
132+
}
133+
}
134+
135+
protected function loadData(): void
136+
{
137+
$this->currentYear = date('Y');
138+
$this->companyCountryCode = Tools::settings('default', 'codpais');
139+
140+
if ($this->idempresa !== 'all') {
141+
$company = new Empresa();
142+
if ($company->load($this->idempresa) && !empty($company->codpais)) {
143+
$this->companyCountryCode = $company->codpais;
144+
}
145+
}
146+
147+
$country = new Pais();
148+
if ($country->load($this->companyCountryCode)) {
149+
$this->companyCountry = $country->nombre;
150+
} else {
151+
$this->companyCountry = $this->companyCountryCode;
152+
}
153+
154+
if ($this->idempresa !== 'all') {
155+
$this->whereEmpresaFacturasProv = " AND idempresa = " . $this->idempresa;
156+
$this->whereEmpresaProveedores = " WHERE codproveedor IN (SELECT codproveedor FROM facturasprov WHERE idempresa = " . $this->idempresa . ")";
157+
}
158+
}
159+
160+
protected function loadTotalSuppliers(): void
161+
{
162+
$sqlTotal = "SELECT COUNT(*) as total FROM proveedores" . $this->whereEmpresaProveedores;
163+
$this->totalSuppliers = (int)$this->dataBase->select($sqlTotal)[0]['total'];
164+
}
165+
166+
protected function loadActiveSupplier(): void
167+
{
168+
$oneYearAgo = date('Y-m-d', strtotime('-1 year'));
169+
$sqlActive = "SELECT COUNT(DISTINCT codproveedor) as total FROM facturasprov WHERE fecha >= '$oneYearAgo'" . $this->whereEmpresaFacturasProv;
170+
$this->activeSupplier = $this->dataBase->select($sqlActive)[0]['total'];
171+
}
172+
173+
protected function loadActiveSupplierYear(): void
174+
{
175+
$sqlActiveYear = "SELECT COUNT(DISTINCT codproveedor) as total FROM facturasprov WHERE fecha >= '$this->currentYear-01-01'" . $this->whereEmpresaFacturasProv;
176+
$this->activeSuppliersYear = $this->dataBase->select($sqlActiveYear)[0]['total'];
177+
}
178+
179+
protected function loadInactiveSupplier(): void
180+
{
181+
$sqlInactive = "SELECT COUNT(*) as total FROM proveedores";
182+
if ($this->idempresa !== 'all') {
183+
$sqlInactive .= " WHERE debaja = true AND codproveedor IN (SELECT codproveedor FROM facturasprov WHERE idempresa = " . $this->idempresa . ")";
184+
} else {
185+
$sqlInactive .= " WHERE debaja = true";
186+
}
187+
$this->inactiveSuppliers = $this->dataBase->select($sqlInactive)[0]['total'];
188+
}
189+
190+
protected function loadNewSuppliers30Days(): void
191+
{
192+
$sql = "SELECT COUNT(*) as total FROM proveedores WHERE fechaalta >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)";
193+
194+
if ($this->idempresa !== 'all') {
195+
$sql .= " AND codproveedor IN (SELECT codproveedor FROM facturasprov WHERE idempresa = " . (int)$this->idempresa . ")";
196+
}
197+
$this->newSuppliers30Days = (int)$this->dataBase->select($sql)[0]['total'];
198+
}
199+
200+
protected function loadNewSuppliersByMonth(): void
201+
{
202+
$report = new Report();
203+
$report->type = Report::DEFAULT_TYPE;
204+
$report->table = 'proveedores';
205+
$report->xcolumn = 'fechaalta';
206+
$report->ycolumn = 'codproveedor';
207+
$report->xoperation = 'MONTHS';
208+
$report->yoperation = 'COUNT';
209+
$report->addFieldXName('');
210+
$report->addCustomFilter('fechaalta', '>=', '{-1 year}');
211+
$report->addCustomFilter('fechaalta', '<=', '{today}');
212+
213+
// aplicamos el filtro de empresa si no se están mostrando todas
214+
if ($this->idempresa !== 'all') {
215+
Report::activateAdvancedReport(true);
216+
$report->addCustomFilter(
217+
'codproveedor',
218+
'IN',
219+
'SELECT codproveedor FROM facturasprov WHERE idempresa = ' . (int)$this->idempresa
220+
);
221+
}
222+
223+
$this->charts['reportTest'] = $report;
224+
}
225+
226+
protected function loadNewSuppliersByYear(): void
227+
{
228+
$report = new Report();
229+
$report->type = Report::TYPE_BAR;
230+
$report->table = 'proveedores';
231+
$report->xcolumn = 'fechaalta';
232+
$report->ycolumn = 'codproveedor';
233+
$report->xoperation = 'YEAR';
234+
$report->yoperation = 'COUNT';
235+
$report->addFieldXName('');
236+
237+
if ($this->idempresa !== 'all') {
238+
Report::activateAdvancedReport(true);
239+
$report->addCustomFilter(
240+
'codproveedor',
241+
'IN',
242+
'SELECT codproveedor FROM facturasprov WHERE idempresa = ' . (int)$this->idempresa
243+
);
244+
}
245+
246+
$this->charts['newSuppliersByYear'] = $report;
247+
}
248+
249+
protected function loadSuppliersByCountry(): void
250+
{
251+
$sqlCountries = "SELECT c.codpais, p.codiso, p.nombre, COUNT(*) as total
252+
FROM proveedores pv
253+
LEFT JOIN contactos c ON pv.idcontacto = c.idcontacto
254+
LEFT JOIN paises p ON c.codpais = p.codpais";
255+
if ($this->idempresa !== 'all') {
256+
$sqlCountries .= " WHERE pv.codproveedor IN (SELECT codproveedor FROM facturasprov WHERE idempresa = " . $this->idempresa . ")";
257+
}
258+
$sqlCountries .= " GROUP BY c.codpais, p.codiso, p.nombre ORDER BY total DESC";
259+
$this->suppliersByCountry = $this->dataBase->select($sqlCountries);
260+
}
261+
262+
protected function loadSuppliersByProvince(): void
263+
{
264+
$report = new Report();
265+
$report->type = Report::TYPE_TREE_MAP;
266+
$report->table = 'proveedores pv';
267+
$report->xcolumn = "COALESCE(NULLIF(c.provincia, ''), '" . Tools::trans('no-data') . "')";
268+
$report->ycolumn = '*';
269+
$report->yoperation = 'COUNT';
270+
271+
// añadimos el JOIN con contactos para obtener provincia y país
272+
Report::activateAdvancedReport(true);
273+
$report->addCustomJoin('LEFT JOIN contactos c ON pv.idcontacto = c.idcontacto');
274+
275+
// filtramos por el país de la empresa para mostrar solo provincias del país
276+
$report->addCustomFilter('c.codpais', '=', $this->companyCountryCode);
277+
278+
// aplicamos el filtro de empresa si no se están mostrando todas
279+
if ($this->idempresa !== 'all') {
280+
$report->addCustomFilter(
281+
'pv.codproveedor',
282+
'IN',
283+
'SELECT codproveedor FROM facturasprov WHERE idempresa = ' . (int)$this->idempresa
284+
);
285+
}
286+
287+
$this->charts['suppliersByProvince'] = $report;
288+
}
289+
290+
protected function loadInvoicesByProvince(): void
291+
{
292+
$report = new Report();
293+
$report->type = Report::TYPE_TREE_MAP;
294+
$report->table = 'facturasprov f';
295+
$report->xcolumn = "COALESCE(NULLIF(c.provincia, ''), '" . Tools::trans('no-data') . "')";
296+
$report->ycolumn = 'DISTINCT f.codproveedor';
297+
$report->yoperation = 'COUNT';
298+
299+
// añadimos los JOINs necesarios para obtener la provincia del contacto
300+
Report::activateAdvancedReport(true);
301+
$report->addCustomJoin('LEFT JOIN proveedores pv ON f.codproveedor = pv.codproveedor');
302+
$report->addCustomJoin('LEFT JOIN contactos c ON pv.idcontacto = c.idcontacto');
303+
304+
// aplicamos el filtro de empresa si no se están mostrando todas
305+
if ($this->idempresa !== 'all') {
306+
$report->addCustomFilter('f.idempresa', '=', (int)$this->idempresa);
307+
}
308+
309+
$this->charts['invoicesByProvince'] = $report;
310+
}
311+
312+
protected function loadSuppliersWithPayables(): void
313+
{
314+
$sql = "SELECT COUNT(DISTINCT codproveedor) as total FROM facturasprov WHERE pagada = " . $this->dataBase->var2str(false);
315+
316+
if ($this->idempresa !== 'all') {
317+
$sql .= " AND idempresa = " . $this->dataBase->var2str((int)$this->idempresa);
318+
}
319+
320+
$this->suppliersWithPayables = (int)$this->dataBase->select($sql)[0]['total'];
321+
}
322+
323+
protected function loadCreditors(): void
324+
{
325+
$report = new Report();
326+
$report->type = Report::TYPE_TREE_MAP;
327+
$report->table = 'facturasprov f';
328+
$report->xcolumn = 'f.nombre';
329+
$report->ycolumn = 'f.total';
330+
$report->yoperation = 'SUM';
331+
332+
Report::activateAdvancedReport(true);
333+
$report->addCustomSql($this->getCreditorsSql());
334+
335+
$this->charts['creditors'] = $report;
336+
}
337+
338+
protected function loadTopCreditors(): void
339+
{
340+
$report = new Report();
341+
$report->type = Report::TYPE_BAR;
342+
$report->table = 'facturasprov f';
343+
$report->xcolumn = 'f.nombre';
344+
$report->ycolumn = 'f.total';
345+
$report->yoperation = 'SUM';
346+
347+
Report::activateAdvancedReport(true);
348+
$report->addCustomSql($this->getCreditorsSql(10));
349+
350+
$this->charts['topCreditors'] = $report;
351+
}
352+
353+
protected function getCreditorsSql(int $limit = 0): string
354+
{
355+
$sql = "SELECT "
356+
. "f.codproveedor as codproveedor, "
357+
. "COALESCE(NULLIF(f.nombre, ''), f.codproveedor, '" . Tools::trans('no-data') . "') as xcol, "
358+
. "SUM(f.total) as ycol "
359+
. "FROM facturasprov f "
360+
. "WHERE f.pagada = " . $this->dataBase->var2str(false);
361+
362+
if ($this->idempresa !== 'all') {
363+
$sql .= " AND f.idempresa = " . $this->dataBase->var2str((int)$this->idempresa);
364+
}
365+
366+
$sql .= " GROUP BY f.codproveedor, xcol ORDER BY ycol DESC, xcol ASC";
367+
if ($limit > 0) {
368+
$sql .= " LIMIT " . $limit;
369+
}
370+
371+
return $sql . ';';
372+
}
373+
}

Init.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private function createRoleForPlugin(): void
8484
// checks the role permissions
8585
$nameControllers = [
8686
'ReportTreasury','ReportTransport','ReportTaxes','ReportResult',
87-
'ReportProducto','ReportClients','ReportContacts','ReportBreakdown','ReportBooks',
87+
'ReportProducto','ReportClients','ReportSupplier','ReportContacts','ReportBreakdown','ReportBooks',
8888
'ListReportAccounting','ListReport',
8989
'EditReportLedger','EditReportBoard','EditReportBalance','EditReportAmount','EditReport','EditBalanceCode'
9090
];

0 commit comments

Comments
 (0)