Issues (459)

Core/Base/AjaxForms/SalesController.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\SalesDocument;
30
use FacturaScripts\Core\Model\Base\SalesDocumentLine;
31
use FacturaScripts\Core\Tools;
32
use FacturaScripts\Dinamic\Lib\AssetManager;
33
use FacturaScripts\Dinamic\Model\Cliente;
34
use FacturaScripts\Dinamic\Model\RoleAccess;
35
use FacturaScripts\Dinamic\Model\Variante;
36
37
/**
38
 * Description of SalesController
39
 *
40
 * @author Carlos Garcia Gomez <[email protected]>
41
 */
42
abstract class SalesController extends PanelController
43
{
44
    use DocFilesTrait;
45
    use LogAuditTrait;
46
47
    const MAIN_VIEW_NAME = 'main';
48
    const MAIN_VIEW_TEMPLATE = 'Tab/SalesDocument';
49
50
    private $logLevels = ['critical', 'error', 'info', 'notice', 'warning'];
51
52
    abstract public function getModelClassName();
53
54
    public function getModel(bool $reload = false): SalesDocument
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
            SalesHeaderHTML::apply($this->views[static::MAIN_VIEW_NAME]->model, $formData, $this->user);
71
            SalesFooterHTML::apply($this->views[static::MAIN_VIEW_NAME]->model, $formData, $this->user);
72
            return $this->views[static::MAIN_VIEW_NAME]->model;
73
        }
74
75
        // existing record
76
        $this->views[static::MAIN_VIEW_NAME]->model->loadFromCode($code);
77
        return $this->views[static::MAIN_VIEW_NAME]->model;
78
    }
79
80
    /**
81
     * @param SalesDocument $model
82
     * @param SalesDocumentLine[] $lines
83
     *
84
     * @return string
85
     */
86
    public function renderSalesForm(SalesDocument $model, array $lines): string
87
    {
88
        return '<div id="salesFormHeader">' . SalesHeaderHTML::render($model) . '</div>'
89
            . '<div id="salesFormLines">' . SalesLineHTML::render($lines, $model) . '</div>'
90
            . '<div id="salesFormFooter">' . SalesFooterHTML::render($model) . '</div>'
91
            . SalesModalHTML::render($model, $this->url(), $this->user, $this->permissions);
92
    }
93
94
    public function series(string $type = ''): array
95
    {
96
        if (empty($type)) {
97
            return Series::all();
98
        }
99
100
        $list = [];
101
        foreach (Series::all() as $serie) {
102
            if ($serie->tipo == $type) {
103
                $list[] = $serie;
104
            }
105
        }
106
107
        return $list;
108
    }
109
110
    protected function autocompleteProductAction(): bool
111
    {
112
        $this->setTemplate(false);
113
114
        $list = [];
115
        $variante = new Variante();
116
        $query = (string)$this->request->get('term');
117
        $where = [
118
            new DataBaseWhere('p.bloqueado', 0),
119
            new DataBaseWhere('p.sevende', 1)
120
        ];
121
        foreach ($variante->codeModelSearch($query, 'referencia', $where) as $value) {
122
            $list[] = [
123
                'key' => Tools::fixHtml($value->code),
124
                'value' => Tools::fixHtml($value->description)
125
            ];
126
        }
127
128
        if (empty($list)) {
129
            $list[] = ['key' => null, 'value' => Tools::lang()->trans('no-data')];
130
        }
131
132
        $this->response->setContent(json_encode($list));
133
        return false;
134
    }
135
136
    protected function createViews()
137
    {
138
        $this->setTabsPosition('top');
139
        $this->createViewsDoc();
140
        $this->createViewDocFiles();
141
        $this->createViewLogAudit();
142
    }
143
144
    protected function createViewsDoc()
145
    {
146
        $pageData = $this->getPageData();
147
        $this->addHtmlView(static::MAIN_VIEW_NAME, static::MAIN_VIEW_TEMPLATE, $this->getModelClassName(), $pageData['title'], 'fas fa-file');
148
        AssetManager::addCss(FS_ROUTE . '/node_modules/jquery-ui-dist/jquery-ui.min.css', 2);
149
        AssetManager::addJs(FS_ROUTE . '/node_modules/jquery-ui-dist/jquery-ui.min.js', 2);
150
        SalesHeaderHTML::assets();
151
        SalesLineHTML::assets();
152
        SalesFooterHTML::assets();
153
    }
154
155
    protected function deleteDocAction(): bool
156
    {
157
        $this->setTemplate(false);
158
159
        // comprobamos los permisos
160
        if (false === $this->permissions->allowDelete) {
161
            Tools::log()->warning('not-allowed-delete');
162
            $this->sendJsonWithLogs(['ok' => false]);
163
            return false;
164
        }
165
166
        $model = $this->getModel();
167
        if (false === $model->delete()) {
168
            $this->sendJsonWithLogs(['ok' => false]);
169
            return false;
170
        }
171
172
        $this->sendJsonWithLogs(['ok' => true, 'newurl' => $model->url('list')]);
173
        return false;
174
    }
175
176
    /**
177
     * @param string $action
178
     *
179
     * @return bool
180
     */
181
    protected function execPreviousAction($action)
182
    {
183
        switch ($action) {
184
            case 'add-file':
185
                return $this->addFileAction();
186
187
            case 'autocomplete-product':
188
                return $this->autocompleteProductAction();
189
190
            case 'add-product':
191
            case 'fast-line':
192
            case 'fast-product':
193
            case 'new-line':
194
            case 'recalculate':
195
            case 'rm-line':
196
            case 'set-customer':
197
                return $this->recalculateAction(true);
198
199
            case 'delete-doc':
200
                return $this->deleteDocAction();
201
202
            case 'delete-file':
203
                return $this->deleteFileAction();
204
205
            case 'edit-file':
206
                return $this->editFileAction();
207
208
            case 'find-customer':
209
                return $this->findCustomerAction();
210
211
            case 'find-product':
212
                return $this->findProductAction();
213
214
            case 'recalculate-line':
215
                return $this->recalculateAction(false);
216
217
            case 'save-doc':
218
                $this->saveDocAction();
219
                return false;
220
221
            case 'save-paid':
222
                return $this->savePaidAction();
223
224
            case 'save-status':
225
                return $this->saveStatusAction();
226
227
            case 'unlink-file':
228
                return $this->unlinkFileAction();
229
        }
230
231
        return parent::execPreviousAction($action);
232
    }
233
234
    protected function exportAction()
235
    {
236
        $this->setTemplate(false);
237
238
        $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

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

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