Issues (461)

Core/Base/AjaxForms/PurchasesModalHTML.php (2 issues)

1
<?php
2
/**
3
 * This file is part of FacturaScripts
4
 * Copyright (C) 2021-2023 Carlos Garcia Gomez <[email protected]>
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\Core\Base\AjaxForms;
21
22
use FacturaScripts\Core\Base\DataBase;
23
use FacturaScripts\Core\Base\DataBase\DataBaseWhere;
24
use FacturaScripts\Core\Base\Translator;
25
use FacturaScripts\Core\Model\Base\PurchaseDocument;
26
use FacturaScripts\Core\Tools;
27
use FacturaScripts\Dinamic\Model\AtributoValor;
28
use FacturaScripts\Dinamic\Model\Fabricante;
29
use FacturaScripts\Dinamic\Model\Familia;
30
use FacturaScripts\Dinamic\Model\Proveedor;
31
32
/**
33
 * Description of PurchasesModalHTML
34
 *
35
 * @author Carlos Garcia Gomez           <[email protected]>
36
 * @author Jose Antonio Cuello Principal <[email protected]>
37
 */
38
class PurchasesModalHTML
39
{
40
    /** @var string */
41
    protected static $codalmacen;
42
43
    /** @var string */
44
    protected static $coddivisa;
45
46
    /** @var string */
47
    protected static $codfabricante;
48
49
    /** @var string */
50
    protected static $codfamilia;
51
52
    /** @var string */
53
    protected static $codproveedor;
54
55
    /** @var bool */
56
    protected static $comprado;
57
58
    /** @var array */
59
    protected static $idatributovalores = [];
60
61
    /** @var string */
62
    protected static $orden;
63
64
    /** @var string */
65
    protected static $query;
66
67
    public static function apply(PurchaseDocument &$model, array $formData): void
68
    {
69
        self::$codalmacen = $model->codalmacen;
70
        self::$coddivisa = $model->coddivisa;
71
        self::$codfabricante = $formData['fp_codfabricante'] ?? '';
72
        self::$codfamilia = $formData['fp_codfamilia'] ?? '';
73
        self::$codproveedor = $model->codproveedor;
74
        self::$orden = $formData['fp_orden'] ?? 'ref_asc';
75
        self::$comprado = (bool)($formData['fp_comprado'] ?? false);
76
        self::$query = isset($formData['fp_query']) ?
77
            Tools::noHtml(mb_strtolower($formData['fp_query'], 'UTF8')) : '';
78
    }
79
80
    public static function render(PurchaseDocument $model, string $url = ''): string
81
    {
82
        self::$codalmacen = $model->codalmacen;
83
        self::$coddivisa = $model->coddivisa;
84
        self::$codproveedor = $model->codproveedor;
85
86
        $i18n = new Translator();
0 ignored issues
show
Deprecated Code introduced by
The class FacturaScripts\Core\Base\Translator has been deprecated: since FacturaScripts 2023.06 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

86
        $i18n = /** @scrutinizer ignore-deprecated */ new Translator();
Loading history...
87
        return $model->editable ? static::modalProveedores($i18n, $url) . static::modalProductos($i18n) : '';
88
    }
89
90
    public static function renderProductList(): string
91
    {
92
        $tbody = '';
93
        $i18n = new Translator();
0 ignored issues
show
Deprecated Code introduced by
The class FacturaScripts\Core\Base\Translator has been deprecated: since FacturaScripts 2023.06 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

93
        $i18n = /** @scrutinizer ignore-deprecated */ new Translator();
Loading history...
94
        foreach (static::getProducts() as $row) {
95
            $cssClass = $row['nostock'] ? 'table-info clickableRow' : ($row['disponible'] > 0 ? 'clickableRow' : 'table-warning clickableRow');
96
            $cost = $row['neto'] ?? $row['coste'];
97
            $label = empty($row['refproveedor']) || $row['refproveedor'] === $row['referencia'] ?
98
                '<b>' . $row['referencia'] . '</b>' :
99
                '<b>' . $row['referencia'] . '</b> <span class="badge badge-light">' . $row['refproveedor'] . '</span>';
100
            $description = Tools::textBreak($row['descripcion'], 120)
101
                . static::idatributovalor($row['idatributovalor1'])
102
                . static::idatributovalor($row['idatributovalor2'])
103
                . static::idatributovalor($row['idatributovalor3'])
104
                . static::idatributovalor($row['idatributovalor4']);
105
            $tbody .= '<tr class="' . $cssClass . '" onclick="$(\'#findProductModal\').modal(\'hide\');'
106
                . ' return purchasesFormAction(\'add-product\', \'' . $row['referencia'] . '\');">'
107
                . '<td>' . $label . ' ' . $description . '</td>'
108
                . '<td class="text-right">' . str_replace(' ', '&nbsp;', Tools::money($cost)) . '</td>'
109
                . '<td class="text-right">' . str_replace(' ', '&nbsp;', Tools::money($row['precio'])) . '</td>'
110
                . '<td class="text-right">' . $row['disponible'] . '</td>'
111
                . '</tr>';
112
        }
113
114
        if (empty($tbody)) {
115
            $tbody .= '<tr class="table-warning"><td colspan="4">' . $i18n->trans('no-data') . '</td></tr>';
116
        }
117
118
        return '<table class="table table-hover mb-0">'
119
            . '<thead>'
120
            . '<tr>'
121
            . '<th>' . $i18n->trans('product') . '</th>'
122
            . '<th class="text-right">' . $i18n->trans('cost-price') . '</th>'
123
            . '<th class="text-right">' . $i18n->trans('price') . '</th>'
124
            . '<th class="text-right">' . $i18n->trans('stock') . '</th>'
125
            . '</tr>'
126
            . '</thead>'
127
            . '<tbody>' . $tbody . '</tbody>'
128
            . '</table>';
129
    }
130
131
    protected static function fabricantes(Translator $i18n): string
132
    {
133
        $fabricante = new Fabricante();
134
        $options = '<option value="">' . $i18n->trans('manufacturer') . '</option>'
135
            . '<option value="">------</option>';
136
        foreach ($fabricante->all([], ['nombre' => 'ASC'], 0, 0) as $man) {
137
            $options .= '<option value="' . $man->codfabricante . '">' . $man->nombre . '</option>';
138
        }
139
140
        return '<select name="fp_codfabricante" class="form-control" onchange="return purchasesFormAction(\'find-product\', \'0\');">'
141
            . $options . '</select>';
142
    }
143
144
    protected static function familias(Translator $i18n): string
145
    {
146
        $options = '<option value="">' . $i18n->trans('family') . '</option>'
147
            . '<option value="">------</option>';
148
149
        $familia = new Familia();
150
        $where = [new DataBaseWhere('madre', null, 'IS')];
151
        $orderBy = ['descripcion' => 'ASC'];
152
        foreach ($familia->all($where, $orderBy, 0, 0) as $fam) {
153
            $options .= '<option value="' . $fam->codfamilia . '">' . $fam->descripcion . '</option>';
154
155
            // añadimos las subfamilias de forma recursiva
156
            $options .= static::subfamilias($fam, $i18n);
157
        }
158
159
        return '<select name="fp_codfamilia" class="form-control" onchange="return purchasesFormAction(\'find-product\', \'0\');">'
160
            . $options . '</select>';
161
    }
162
163
    protected static function getProducts(): array
164
    {
165
        $dataBase = new DataBase();
166
        $sql = 'SELECT v.referencia, pp.refproveedor, p.descripcion, v.idatributovalor1, v.idatributovalor2, v.idatributovalor3,'
167
            . ' v.idatributovalor4, v.coste, v.precio, pp.neto, COALESCE(s.disponible, 0) as disponible, p.nostock'
168
            . ' FROM variantes v'
169
            . ' LEFT JOIN productos p ON v.idproducto = p.idproducto'
170
            . ' LEFT JOIN stocks s ON v.referencia = s.referencia'
171
            . ' AND s.codalmacen = ' . $dataBase->var2str(self::$codalmacen)
172
            . ' LEFT JOIN productosprov pp ON pp.referencia = p.referencia'
173
            . ' AND pp.codproveedor = ' . $dataBase->var2str(self::$codproveedor)
174
            . ' AND pp.coddivisa = ' . $dataBase->var2str(self::$coddivisa)
175
            . ' WHERE p.secompra = true AND p.bloqueado = false';
176
177
        if (self::$codfabricante) {
178
            $sql .= ' AND codfabricante = ' . $dataBase->var2str(self::$codfabricante);
179
        }
180
181
        if (self::$codfamilia) {
182
            $codFamilias = [$dataBase->var2str(self::$codfamilia)];
183
184
            // buscamos las subfamilias
185
            $familia = new Familia();
186
            if ($familia->loadFromCode(self::$codfamilia)) {
187
                foreach ($familia->getSubfamilias() as $fam) {
188
                    $codFamilias[] = $dataBase->var2str($fam->codfamilia);
189
                }
190
            }
191
192
            $sql .= ' AND codfamilia IN (' . implode(',', $codFamilias) . ')';
193
        }
194
195
        if (self::$comprado) {
196
            $sql .= ' AND pp.codproveedor = ' . $dataBase->var2str(self::$codproveedor);
197
        }
198
199
        if (self::$query) {
200
            $words = explode(' ', self::$query);
201
            if (count($words) === 1) {
202
                $sql .= " AND (LOWER(v.codbarras) = " . $dataBase->var2str(self::$query)
203
                    . " OR LOWER(v.referencia) LIKE '%" . self::$query . "%'"
204
                    . " OR LOWER(pp.refproveedor) LIKE '%" . self::$query . "%'"
205
                    . " OR LOWER(p.descripcion) LIKE '%" . self::$query . "%')";
206
            } elseif (count($words) > 1) {
207
                $sql .= " AND (LOWER(v.referencia) LIKE '%" . self::$query . "%'"
208
                    . " OR LOWER(pp.refproveedor) LIKE '%" . self::$query . "%' OR (";
209
                foreach ($words as $wc => $word) {
210
                    $sql .= $wc > 0 ?
211
                        " AND LOWER(p.descripcion) LIKE '%" . $word . "%'" :
212
                        "LOWER(p.descripcion) LIKE '%" . $word . "%'";
213
                }
214
                $sql .= "))";
215
            }
216
        }
217
218
        switch (self::$orden) {
219
            case 'desc_asc':
220
                $sql .= " ORDER BY 3 ASC";
221
                break;
222
223
            case 'price_desc':
224
                $sql .= " ORDER BY 9 DESC";
225
                break;
226
227
            case 'ref_asc':
228
                $sql .= " ORDER BY 1 ASC";
229
                break;
230
231
            case 'stock_desc':
232
                $sql .= " ORDER BY 11 DESC";
233
                break;
234
        }
235
236
        return $dataBase->selectLimit($sql);
237
    }
238
239
    protected static function idatributovalor(?int $id): string
240
    {
241
        if (empty($id)) {
242
            return '';
243
        }
244
245
        if (!isset(self::$idatributovalores[$id])) {
246
            $attValor = new AtributoValor();
247
            $attValor->loadFromCode($id);
248
            self::$idatributovalores[$id] = $attValor->descripcion;
249
        }
250
251
        return ', ' . self::$idatributovalores[$id];
252
    }
253
254
    protected static function modalProductos(Translator $i18n): string
255
    {
256
        return '<div class="modal" id="findProductModal" tabindex="-1" aria-hidden="true">'
257
            . '<div class="modal-dialog modal-xl">'
258
            . '<div class="modal-content">'
259
            . '<div class="modal-header">'
260
            . '<h5 class="modal-title"><i class="fas fa-cubes fa-fw"></i> ' . $i18n->trans('products') . '</h5>'
261
            . '<button type="button" class="close" data-dismiss="modal" aria-label="Close">'
262
            . '<span aria-hidden="true">&times;</span>'
263
            . '</button>'
264
            . '</div>'
265
            . '<div class="modal-body">'
266
            . '<div class="form-row">'
267
            . '<div class="col-sm mb-2">'
268
            . '<div class="input-group">'
269
            . '<input type="text" name="fp_query" class="form-control" id="productModalInput" placeholder="' . $i18n->trans('search')
270
            . '" onkeyup="return purchasesFormActionWait(\'find-product\', \'0\', event);"/>'
271
            . '<div class="input-group-append">'
272
            . '<button class="btn btn-primary btn-spin-action" type="button" onclick="return purchasesFormAction(\'find-product\', \'0\');">'
273
            . '<i class="fas fa-search"></i></button>'
274
            . '</div>'
275
            . '</div>'
276
            . '</div>'
277
            . '<div class="col-sm mb-2">' . static::fabricantes($i18n) . '</div>'
278
            . '<div class="col-sm mb-2">' . static::familias($i18n) . '</div>'
279
            . '<div class="col-sm mb-2">' . static::orden($i18n) . '</div>'
280
            . '</div>'
281
            . '<div class="form-row">'
282
            . '<div class="col-sm">'
283
            . '<div class="form-check">'
284
            . '<input type="checkbox" name="fp_comprado" value="1" class="form-check-input" id="comprado" onchange="return purchasesFormAction(\'find-product\', \'0\');">'
285
            . '<label class="form-check-label" for="comprado">' . $i18n->trans('previously-purchased-from-supplier') . '</label>'
286
            . '</div>'
287
            . '</div>'
288
            . '</div>'
289
            . '</div>'
290
            . '<div class="table-responsive" id="findProductList">' . static::renderProductList() . '</div>'
291
            . '</div>'
292
            . '</div>'
293
            . '</div>';
294
    }
295
296
    protected static function modalProveedores(Translator $i18n, string $url): string
297
    {
298
        $trs = '';
299
        $proveedor = new Proveedor();
300
        $where = [new DataBaseWhere('fechabaja', null, 'IS')];
301
        foreach ($proveedor->all($where, ['LOWER(nombre)' => 'ASC']) as $pro) {
302
            $name = ($pro->nombre === $pro->razonsocial) ? $pro->nombre : $pro->nombre . ' <small>(' . $pro->razonsocial . ')</span>';
303
            $trs .= '<tr class="clickableRow" onclick="document.forms[\'purchasesForm\'][\'codproveedor\'].value = \''
304
                . $pro->codproveedor . '\'; $(\'#findSupplierModal\').modal(\'hide\'); purchasesFormAction(\'set-supplier\', \'0\'); return false;">'
305
                . '<td><i class="fas fa-user fa-fw"></i> ' . $name . '</td>'
306
                . '</tr>';
307
        }
308
309
        return '<div class="modal" id="findSupplierModal" tabindex="-1" aria-hidden="true">'
310
            . '<div class="modal-dialog modal-dialog-scrollable">'
311
            . '<div class="modal-content">'
312
            . '<div class="modal-header">'
313
            . '<h5 class="modal-title"><i class="fas fa-users fa-fw"></i> ' . $i18n->trans('suppliers') . '</h5>'
314
            . '<button type="button" class="close" data-dismiss="modal" aria-label="Close">'
315
            . '<span aria-hidden="true">&times;</span>'
316
            . '</button>'
317
            . '</div>'
318
            . '<div class="modal-body p-0">'
319
            . '<div class="p-3">'
320
            . '<div class="input-group">'
321
            . '<input type="text" id="findSupplierInput" class="form-control" placeholder="' . $i18n->trans('search') . '" />'
322
            . '<div class="input-group-apend">'
323
            . '<button type="button" class="btn btn-primary"><i class="fas fa-search"></i></button>'
324
            . '</div>'
325
            . '</div>'
326
            . '</div>'
327
            . '<table class="table table-hover mb-0">' . $trs . '</table></div>'
328
            . '<div class="modal-footer bg-light">'
329
            . '<a href="EditProveedor?return=' . urlencode($url) . '" class="btn btn-block btn-success">'
330
            . '<i class="fas fa-plus fa-fw"></i> ' . $i18n->trans('new')
331
            . '</a>'
332
            . '</div>'
333
            . '</div>'
334
            . '</div>'
335
            . '</div>';
336
    }
337
338
    protected static function orden(Translator $i18n): string
339
    {
340
        return '<div class="input-group">'
341
            . '<div class="input-group-prepend"><span class="input-group-text"><i class="fas fa-sort-amount-down-alt"></i></span></div>'
342
            . '<select name="fp_orden" class="form-control" onchange="return purchasesFormAction(\'find-product\', \'0\');">'
343
            . '<option value="">' . $i18n->trans('sort') . '</option>'
344
            . '<option value="">------</option>'
345
            . '<option value="ref_asc">' . $i18n->trans('reference') . '</option>'
346
            . '<option value="desc_asc">' . $i18n->trans('description') . '</option>'
347
            . '<option value="price_desc">' . $i18n->trans('price') . '</option>'
348
            . '<option value="stock_desc">' . $i18n->trans('stock') . '</option>'
349
            . '</select>'
350
            . '</div>';
351
    }
352
353
    private static function subfamilias(Familia $family, Translator $i18n, int $level = 1): string
354
    {
355
        $options = '';
356
        foreach ($family->getSubfamilias() as $fam) {
357
            $options .= '<option value="' . $fam->codfamilia . '">'
358
                . str_repeat('-', $level) . ' ' . $fam->descripcion
359
                . '</option>';
360
361
            // añadimos las subfamilias de forma recursiva
362
            $options .= static::subfamilias($fam, $i18n, $level + 1);
363
        }
364
365
        return $options;
366
    }
367
}
368