Issues (459)

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

1
<?php
2
/**
3
 * This file is part of FacturaScripts
4
 * Copyright (C) 2021-2024 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\Calculator;
23
use FacturaScripts\Core\Base\DataBase\DataBaseWhere;
24
use FacturaScripts\Core\DataSrc\Series;
25
use FacturaScripts\Core\Lib\ExtendedController\BaseView;
26
use FacturaScripts\Core\Lib\ExtendedController\DocFilesTrait;
27
use FacturaScripts\Core\Lib\ExtendedController\LogAuditTrait;
28
use FacturaScripts\Core\Lib\ExtendedController\PanelController;
29
use FacturaScripts\Core\Model\Base\PurchaseDocument;
30
use FacturaScripts\Core\Model\Base\PurchaseDocumentLine;
31
use FacturaScripts\Core\Tools;
32
use FacturaScripts\Dinamic\Lib\AssetManager;
33
use FacturaScripts\Dinamic\Model\Proveedor;
34
use FacturaScripts\Dinamic\Model\Variante;
35
36
/**
37
 * Description of PurchasesController
38
 *
39
 * @author Carlos Garcia Gomez           <[email protected]>
40
 * @author Jose Antonio Cuello Principal <[email protected]>
41
 */
42
abstract class PurchasesController extends PanelController
43
{
44
    use DocFilesTrait;
45
    use LogAuditTrait;
46
47
    const MAIN_VIEW_NAME = 'main';
48
    const MAIN_VIEW_TEMPLATE = 'Tab/PurchasesDocument';
49
50
    private $logLevels = ['critical', 'error', 'info', 'notice', 'warning'];
51
52
    abstract public function getModelClassName();
53
54
    public function getModel(bool $reload = false): PurchaseDocument
55
    {
56
        if ($reload) {
57
            $this->views[static::MAIN_VIEW_NAME]->model->clear();
58
        }
59
60
        // loaded record? just return it
61
        if ($this->views[static::MAIN_VIEW_NAME]->model->primaryColumnValue()) {
62
            return $this->views[static::MAIN_VIEW_NAME]->model;
63
        }
64
65
        // get the record identifier
66
        $code = $this->request->get('code');
67
        if (empty($code)) {
68
            // empty identifier? Then sets initial parameters to the new record and return it
69
            $formData = $this->request->query->all();
70
            PurchasesHeaderHTML::apply($this->views[static::MAIN_VIEW_NAME]->model, $formData, $this->user);
71
            return $this->views[static::MAIN_VIEW_NAME]->model;
72
        }
73
74
        // existing record
75
        $this->views[static::MAIN_VIEW_NAME]->model->loadFromCode($code);
76
        return $this->views[static::MAIN_VIEW_NAME]->model;
77
    }
78
79
    /**
80
     * @param PurchaseDocument $model
81
     * @param PurchaseDocumentLine[] $lines
82
     *
83
     * @return string
84
     */
85
    public function renderPurchasesForm(PurchaseDocument $model, array $lines): string
86
    {
87
        return '<div id="purchasesFormHeader">' . PurchasesHeaderHTML::render($model) . '</div>'
88
            . '<div id="purchasesFormLines">' . PurchasesLineHTML::render($lines, $model) . '</div>'
89
            . '<div id="purchasesFormFooter">' . PurchasesFooterHTML::render($model) . '</div>'
90
            . PurchasesModalHTML::render($model, $this->url());
91
    }
92
93
    public function series(string $type = ''): array
94
    {
95
        if (empty($type)) {
96
            return Series::all();
97
        }
98
99
        $list = [];
100
        foreach (Series::all() as $serie) {
101
            if ($serie->tipo == $type) {
102
                $list[] = $serie;
103
            }
104
        }
105
106
        return $list;
107
    }
108
109
    protected function autocompleteProductAction(): bool
110
    {
111
        $this->setTemplate(false);
112
113
        $list = [];
114
        $variante = new Variante();
115
        $query = (string)$this->request->get('term');
116
        $where = [
117
            new DataBaseWhere('p.bloqueado', 0),
118
            new DataBaseWhere('p.secompra', 1)
119
        ];
120
        foreach ($variante->codeModelSearch($query, 'referencia', $where) as $value) {
121
            $list[] = [
122
                'key' => Tools::fixHtml($value->code),
123
                'value' => Tools::fixHtml($value->description)
124
            ];
125
        }
126
127
        if (empty($list)) {
128
            $list[] = ['key' => null, 'value' => Tools::lang()->trans('no-data')];
129
        }
130
131
        $this->response->setContent(json_encode($list));
132
        return false;
133
    }
134
135
    protected function createViews()
136
    {
137
        $this->setTabsPosition('top');
138
        $this->createViewsDoc();
139
        $this->createViewDocFiles();
140
        $this->createViewLogAudit();
141
    }
142
143
    protected function createViewsDoc()
144
    {
145
        $pageData = $this->getPageData();
146
        $this->addHtmlView(static::MAIN_VIEW_NAME, static::MAIN_VIEW_TEMPLATE, $this->getModelClassName(), $pageData['title'], 'fas fa-file');
147
        AssetManager::addCss(FS_ROUTE . '/node_modules/jquery-ui-dist/jquery-ui.min.css', 2);
148
        AssetManager::addJs(FS_ROUTE . '/node_modules/jquery-ui-dist/jquery-ui.min.js', 2);
149
        PurchasesHeaderHTML::assets();
150
        PurchasesLineHTML::assets();
151
        PurchasesFooterHTML::assets();
152
    }
153
154
    protected function deleteDocAction(): bool
155
    {
156
        $this->setTemplate(false);
157
158
        // comprobamos los permisos
159
        if (false === $this->permissions->allowDelete) {
160
            Tools::log()->warning('not-allowed-delete');
161
            $this->sendJsonWithLogs(['ok' => false]);
162
            return false;
163
        }
164
165
        $model = $this->getModel();
166
        if (false === $model->delete()) {
167
            $this->sendJsonWithLogs(['ok' => false]);
168
            return false;
169
        }
170
171
        $this->sendJsonWithLogs(['ok' => true, 'newurl' => $model->url('list')]);
172
        return false;
173
    }
174
175
    /**
176
     * @param string $action
177
     *
178
     * @return bool
179
     */
180
    protected function execPreviousAction($action)
181
    {
182
        switch ($action) {
183
            case 'add-file':
184
                return $this->addFileAction();
185
186
            case 'autocomplete-product':
187
                return $this->autocompleteProductAction();
188
189
            case 'add-product':
190
            case 'fast-line':
191
            case 'fast-product':
192
            case 'new-line':
193
            case 'recalculate':
194
            case 'rm-line':
195
            case 'set-supplier':
196
                return $this->recalculateAction(true);
197
198
            case 'delete-doc':
199
                return $this->deleteDocAction();
200
201
            case 'delete-file':
202
                return $this->deleteFileAction();
203
204
            case 'edit-file':
205
                return $this->editFileAction();
206
207
            case 'find-supplier':
208
                return $this->findSupplierAction();
209
210
            case 'find-product':
211
                return $this->findProductAction();
212
213
            case 'recalculate-line':
214
                return $this->recalculateAction(false);
215
216
            case 'save-doc':
217
                $this->saveDocAction();
218
                return false;
219
220
            case 'save-paid':
221
                return $this->savePaidAction();
222
223
            case 'save-status':
224
                return $this->saveStatusAction();
225
226
            case 'unlink-file':
227
                return $this->unlinkFileAction();
228
        }
229
230
        return parent::execPreviousAction($action);
231
    }
232
233
    protected function exportAction()
234
    {
235
        $this->setTemplate(false);
236
237
        $subjectLang = $this->views[static::MAIN_VIEW_NAME]->model->getSubject()->langcode;
0 ignored issues
show
The method getSubject() does not exist on FacturaScripts\Core\Model\Base\ModelClass. It seems like you code against a sub-type of said class. However, the method does not exist in FacturaScripts\Core\Model\Base\Contact or FacturaScripts\Core\Model\Base\Address or FacturaScripts\Dinamic\Model\Base\ModelClass or FacturaScripts\Core\Model\Base\ModelOnChangeClass or FacturaScripts\Core\Model\Base\BankAccount or FacturaScripts\Core\Model\Base\Payment or FacturaScripts\Dinamic\Model\Base\Contact or FacturaScripts\Core\Model\Base\ComercialContact or FacturaScripts\Dinamic\Model\Base\ComercialContact or FacturaScripts\Dinamic\Model\Base\Address or FacturaScripts\Core\Model\Base\Receipt or FacturaScripts\Core\Mode...se\BusinessDocumentLine or FacturaScripts\Dinamic\M...Base\ModelOnChangeClass or FacturaScripts\Dinamic\Model\Base\Receipt or FacturaScripts\Dinamic\M...se\BusinessDocumentLine or FacturaScripts\Core\Mode...se\PurchaseDocumentLine or FacturaScripts\Core\Model\Base\SalesDocumentLine or FacturaScripts\Dinamic\M...se\PurchaseDocumentLine or FacturaScripts\Dinamic\M...\Base\SalesDocumentLine or FacturaScripts\Dinamic\Model\Base\BankAccount or FacturaScripts\Dinamic\Model\Base\Payment. Are you sure you never get one of those? ( Ignorable by Annotation )

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

237
        $subjectLang = $this->views[static::MAIN_VIEW_NAME]->model->/** @scrutinizer ignore-call */ getSubject()->langcode;
Loading history...
238
        $requestLang = $this->request->request->get('langcode');
239
        $langCode = $requestLang ?? $subjectLang ?? '';
240
241
        $this->exportManager->newDoc(
242
            $this->request->get('option', ''),
243
            $this->title,
244
            (int)$this->request->request->get('idformat', ''),
245
            $langCode
246
        );
247
        $this->exportManager->addBusinessDocPage($this->views[static::MAIN_VIEW_NAME]->model);
248
        $this->exportManager->show($this->response);
249
    }
250
251
    protected function findSupplierAction(): bool
252
    {
253
        $this->setTemplate(false);
254
        $supplier = new Proveedor();
255
        $list = [];
256
        $term = $this->request->get('term');
257
        foreach ($supplier->codeModelSearch($term) as $item) {
258
            $list[$item->code] = $item->code . ' | ' . Tools::fixHtml($item->description);
259
        }
260
        $this->response->setContent(json_encode($list));
261
        return false;
262
    }
263
264
    protected function findProductAction(): bool
265
    {
266
        $this->setTemplate(false);
267
        $model = $this->getModel();
268
        $formData = json_decode($this->request->request->get('data'), true);
269
        PurchasesHeaderHTML::apply($model, $formData, $this->user);
270
        PurchasesFooterHTML::apply($model, $formData, $this->user);
271
        PurchasesModalHTML::apply($model, $formData);
272
        $content = [
273
            'header' => '',
274
            'lines' => '',
275
            'linesMap' => [],
276
            'footer' => '',
277
            'products' => PurchasesModalHTML::renderProductList()
278
        ];
279
        $this->sendJsonWithLogs($content);
280
        return false;
281
    }
282
283
    /**
284
     * @param string $viewName
285
     * @param BaseView $view
286
     */
287
    protected function loadData($viewName, $view)
288
    {
289
        $code = $this->request->get('code');
290
291
        switch ($viewName) {
292
            case 'docfiles':
293
                $this->loadDataDocFiles($view, $this->getModelClassName(), $code);
294
                break;
295
296
            case 'ListLogMessage':
297
                $this->loadDataLogAudit($view, $this->getModelClassName(), $code);
298
                break;
299
300
            case static::MAIN_VIEW_NAME:
301
                if (empty($code)) {
302
                    $this->getModel(true);
303
                    break;
304
                }
305
306
                // data not found?
307
                $view->loadData($code);
308
                $action = $this->request->request->get('action', '');
309
                if ('' === $action && empty($view->model->primaryColumnValue())) {
310
                    Tools::log()->warning('record-not-found');
311
                    break;
312
                }
313
314
                $this->title .= ' ' . $view->model->primaryDescription();
315
                $view->settings['btnPrint'] = true;
316
                $this->addButton($viewName, [
317
                    'action' => 'CopyModel?model=' . $this->getModelClassName() . '&code=' . $view->model->primaryColumnValue(),
318
                    'icon' => 'fas fa-cut',
319
                    'label' => 'copy',
320
                    'type' => 'link'
321
                ]);
322
                break;
323
        }
324
    }
325
326
    protected function recalculateAction(bool $renderLines): bool
327
    {
328
        $this->setTemplate(false);
329
        $model = $this->getModel();
330
        $lines = $model->getLines();
331
        $formData = json_decode($this->request->request->get('data'), true);
332
        PurchasesHeaderHTML::apply($model, $formData, $this->user);
333
        PurchasesFooterHTML::apply($model, $formData, $this->user);
334
        PurchasesLineHTML::apply($model, $lines, $formData);
335
        Calculator::calculate($model, $lines, false);
336
337
        $content = [
338
            'header' => PurchasesHeaderHTML::render($model),
339
            'lines' => $renderLines ? PurchasesLineHTML::render($lines, $model) : '',
340
            'linesMap' => $renderLines ? [] : PurchasesLineHTML::map($lines, $model),
341
            'footer' => PurchasesFooterHTML::render($model),
342
            'products' => ''
343
        ];
344
        $this->sendJsonWithLogs($content);
345
        return false;
346
    }
347
348
    protected function saveDocAction(): bool
349
    {
350
        $this->setTemplate(false);
351
352
        // comprobamos los permisos
353
        if (false === $this->permissions->allowUpdate) {
354
            Tools::log()->warning('not-allowed-modify');
355
            $this->sendJsonWithLogs(['ok' => false]);
356
            return false;
357
        }
358
359
        $this->dataBase->beginTransaction();
360
361
        $model = $this->getModel();
362
        $formData = json_decode($this->request->request->get('data'), true);
363
        PurchasesHeaderHTML::apply($model, $formData, $this->user);
364
        PurchasesFooterHTML::apply($model, $formData, $this->user);
365
366
        if (false === $model->save()) {
367
            $this->sendJsonWithLogs(['ok' => false]);
368
            $this->dataBase->rollback();
369
            return false;
370
        }
371
372
        $lines = $model->getLines();
373
        PurchasesLineHTML::apply($model, $lines, $formData);
374
        Calculator::calculate($model, $lines, false);
375
376
        foreach ($lines as $line) {
377
            if (false === $line->save()) {
378
                $this->sendJsonWithLogs(['ok' => false]);
379
                $this->dataBase->rollback();
380
                return false;
381
            }
382
        }
383
384
        // remove missing lines
385
        foreach ($model->getLines() as $oldLine) {
386
            if (in_array($oldLine->idlinea, PurchasesLineHTML::getDeletedLines()) && false === $oldLine->delete()) {
387
                $this->sendJsonWithLogs(['ok' => false]);
388
                $this->dataBase->rollback();
389
                return false;
390
            }
391
        }
392
393
        $lines = $model->getLines();
394
        if (false === Calculator::calculate($model, $lines, true)) {
395
            $this->sendJsonWithLogs(['ok' => false]);
396
            $this->dataBase->rollback();
397
            return false;
398
        }
399
400
        $this->sendJsonWithLogs(['ok' => true, 'newurl' => $model->url() . '&action=save-ok']);
401
        $this->dataBase->commit();
402
        return true;
403
    }
404
405
    protected function savePaidAction(): bool
406
    {
407
        $this->setTemplate(false);
408
409
        // comprobamos los permisos
410
        if (false === $this->permissions->allowUpdate) {
411
            Tools::log()->warning('not-allowed-modify');
412
            $this->sendJsonWithLogs(['ok' => false]);
413
            return false;
414
        }
415
416
        // guardamos el documento
417
        if ($this->getModel()->editable && false === $this->saveDocAction()) {
418
            return false;
419
        }
420
421
        // si la factura es de 0 €, la marcamos como pagada
422
        $model = $this->getModel();
423
        if (empty($model->total) && property_exists($model, 'pagada')) {
424
            $model->pagada = (bool)$this->request->request->get('selectedLine');
425
            $model->save();
426
            $this->sendJsonWithLogs(['ok' => true, 'newurl' => $model->url() . '&action=save-ok']);
427
            return false;
428
        }
429
430
        // comprobamos si tiene recibos
431
        $receipts = $model->getReceipts();
0 ignored issues
show
The method getReceipts() does not exist on FacturaScripts\Core\Model\Base\PurchaseDocument. It seems like you code against a sub-type of said class. However, the method does not exist in FacturaScripts\Dinamic\Model\Base\PurchaseDocument. Are you sure you never get one of those? ( Ignorable by Annotation )

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

431
        /** @scrutinizer ignore-call */ 
432
        $receipts = $model->getReceipts();
Loading history...
432
        if (empty($receipts)) {
433
            Tools::log()->warning('invoice-has-no-receipts');
434
            $this->sendJsonWithLogs(['ok' => false]);
435
            return false;
436
        }
437
438
        // marcamos los recibos como pagados, eso marcará la factura como pagada
439
        foreach ($receipts as $receipt) {
440
            $receipt->nick = $this->user->nick;
441
            $receipt->pagado = (bool)$this->request->request->get('selectedLine');
442
            if (false === $receipt->save()) {
443
                $this->sendJsonWithLogs(['ok' => false]);
444
                return false;
445
            }
446
        }
447
448
        $this->sendJsonWithLogs(['ok' => true, 'newurl' => $model->url() . '&action=save-ok']);
449
        return false;
450
    }
451
452
    protected function saveStatusAction(): bool
453
    {
454
        $this->setTemplate(false);
455
456
        // comprobamos los permisos
457
        if (false === $this->permissions->allowUpdate) {
458
            Tools::log()->warning('not-allowed-modify');
459
            $this->sendJsonWithLogs(['ok' => false]);
460
            return false;
461
        }
462
463
        if ($this->getModel()->editable && false === $this->saveDocAction()) {
464
            return false;
465
        }
466
467
        $model = $this->getModel();
468
        $model->idestado = (int)$this->request->request->get('selectedLine');
469
        if (false === $model->save()) {
470
            $this->sendJsonWithLogs(['ok' => false]);
471
            return false;
472
        }
473
474
        $this->sendJsonWithLogs(['ok' => true, 'newurl' => $model->url() . '&action=save-ok']);
475
        return false;
476
    }
477
478
    private function sendJsonWithLogs(array $data): void
479
    {
480
        $data['messages'] = [];
481
        foreach (Tools::log()::read('', $this->logLevels) as $message) {
482
            if ($message['channel'] != 'audit') {
483
                $data['messages'][] = $message;
484
            }
485
        }
486
487
        $this->response->setContent(json_encode($data));
488
    }
489
}
490