Passed
Push — master ( 4286a4...290de6 )
by Carlos
05:27
created

BusinessDocument::saveUpdate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 16
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * This file is part of FacturaScripts
4
 * Copyright (C) 2013-2021 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\Model\Base;
21
22
use FacturaScripts\Dinamic\Lib\BusinessDocumentCode;
23
use FacturaScripts\Dinamic\Model\Almacen;
24
use FacturaScripts\Dinamic\Model\Divisa;
25
use FacturaScripts\Dinamic\Model\Ejercicio;
26
use FacturaScripts\Dinamic\Model\Serie;
27
28
/**
29
 * Description of BusinessDocument
30
 *
31
 * @author Carlos García Gómez <[email protected]>
32
 */
33
abstract class BusinessDocument extends ModelOnChangeClass
34
{
35
36
    use CompanyRelationTrait;
37
    use CurrencyRelationTrait;
0 ignored issues
show
Bug introduced by
The trait FacturaScripts\Core\Mode...e\CurrencyRelationTrait requires the property $tasaconvcompra which is not provided by FacturaScripts\Core\Model\Base\BusinessDocument.
Loading history...
38
    use ExerciseRelationTrait;
39
    use PaymentRelationTrait;
40
    use SerieRelationTrait;
41
42
    /**
43
     * VAT number of the customer or supplier.
44
     *
45
     * @var string
46
     */
47
    public $cifnif;
48
49
    /**
50
     * Warehouse in which the merchandise enters.
51
     *
52
     * @var string
53
     */
54
    public $codalmacen;
55
56
    /**
57
     * Unique identifier for humans.
58
     *
59
     * @var string
60
     */
61
    public $codigo;
62
63
    /**
64
     * Percentage of discount.
65
     *
66
     * @var float
67
     */
68
    public $dtopor1;
69
70
    /**
71
     * Percentage of discount.
72
     *
73
     * @var float
74
     */
75
    public $dtopor2;
76
77
    /**
78
     * Date of the document.
79
     *
80
     * @var string
81
     */
82
    public $fecha;
83
84
    /**
85
     * Date on which the document was sent by email.
86
     *
87
     * @var string
88
     */
89
    public $femail;
90
91
    /**
92
     * Document time.
93
     *
94
     * @var string
95
     */
96
    public $hora;
97
98
    /**
99
     * Default retention for this document. Each line can have a different retention.
100
     *
101
     * @var float|int
102
     */
103
    public $irpf;
104
105
    /**
106
     * Sum of the pvptotal of lines. Total of the document before taxes.
107
     *
108
     * @var float|int
109
     */
110
    public $neto;
111
112
    /**
113
     * Sum of the pvptotal of lines. Total of the document before taxes and global discounts.
114
     *
115
     * @var float|int
116
     */
117
    public $netosindto;
118
119
    /**
120
     * User who created this document. User model.
121
     *
122
     * @var string
123
     */
124
    public $nick;
125
126
    /**
127
     * Number of the document. Unique within the series.
128
     *
129
     * @var string
130
     */
131
    public $numero;
132
133
    /**
134
     * Notes of the document.
135
     *
136
     * @var string
137
     */
138
    public $observaciones;
139
140
    /**
141
     * Total sum of the document, with taxes.
142
     *
143
     * @var float|int
144
     */
145
    public $total;
146
147
    /**
148
     * Sum of the VAT of the lines.
149
     *
150
     * @var float|int
151
     */
152
    public $totaliva;
153
154
    /**
155
     * Total expressed in euros, if it were not the currency of the document.
156
     * totaleuros = total / tasaconv
157
     * It is not necessary to fill it, when doing save() the value is calculated.
158
     *
159
     * @var float|int
160
     */
161
    public $totaleuros;
162
163
    /**
164
     * Total sum of the IRPF withholdings of the lines.
165
     *
166
     * @var float|int
167
     */
168
    public $totalirpf;
169
170
    /**
171
     * Total sum of the equivalence surcharge of the lines.
172
     *
173
     * @var float|int
174
     */
175
    public $totalrecargo;
176
177
    /**
178
     * Total sum of supplied lines.
179
     *
180
     * @var float|int
181
     */
182
    public $totalsuplidos;
183
184
    /**
185
     * Returns the lines associated with the document.
186
     */
187
    abstract public function getLines();
188
189
    /**
190
     * Returns a new line for this business document.
191
     */
192
    abstract public function getNewLine(array $data = [], array $exclude = []);
193
194
    /**
195
     * Returns a new line for this business document completed with the product data.
196
     */
197
    abstract public function getNewProductLine($reference);
198
199
    /**
200
     * Returns the subject of this document.
201
     */
202
    abstract public function getSubject();
203
204
    /**
205
     * Sets the author for this document.
206
     */
207
    abstract public function setAuthor($author);
208
209
    /**
210
     * Sets subject for this document.
211
     */
212
    abstract public function setSubject($subject);
213
214
    /**
215
     * Returns the name of the column for subject.
216
     */
217
    abstract public function subjectColumn();
218
219
    /**
220
     * Updates subjects data in this document.
221
     */
222
    abstract public function updateSubject();
223
224
    /**
225
     * Reset the values of all model properties.
226
     */
227
    public function clear()
228
    {
229
        parent::clear();
230
231
        $appSettings = $this->toolBox()->appSettings();
232
        $this->codalmacen = $appSettings->get('default', 'codalmacen');
233
        $this->codpago = $appSettings->get('default', 'codpago');
234
        $this->codserie = $appSettings->get('default', 'codserie');
235
        $this->dtopor1 = 0.0;
236
        $this->dtopor2 = 0.0;
237
        $this->fecha = date(self::DATE_STYLE);
238
        $this->hora = date(self::HOUR_STYLE);
239
        $this->idempresa = $appSettings->get('default', 'idempresa');
240
        $this->irpf = 0.0;
241
        $this->neto = 0.0;
242
        $this->netosindto = 0.0;
243
        $this->numero = 1;
244
        $this->total = 0.0;
245
        $this->totaleuros = 0.0;
246
        $this->totalirpf = 0.0;
247
        $this->totaliva = 0.0;
248
        $this->totalrecargo = 0.0;
249
        $this->totalsuplidos = 0.0;
250
    }
251
252
    /**
253
     * Returns the Equivalent Unified Discount.
254
     *
255
     * @return float
256
     */
257
    public function getEUDiscount()
258
    {
259
        $eud = 1.0;
260
        foreach ([$this->dtopor1, $this->dtopor2] as $dto) {
261
            $eud *= 1 - $dto / 100;
262
        }
263
264
        return $eud;
265
    }
266
267
    /**
268
     * This function is called when creating the model table. Returns the SQL
269
     * that will be executed after the creation of the table. Useful to insert values
270
     * default.
271
     *
272
     * @return string
273
     */
274
    public function install()
275
    {
276
        // needed dependencies
277
        new Serie();
278
        new Ejercicio();
279
        new Almacen();
280
        new Divisa();
281
282
        return parent::install();
283
    }
284
285
    /**
286
     * @return bool
287
     */
288
    public function paid()
289
    {
290
        return false;
291
    }
292
293
    /**
294
     * Returns the description of the column that is the model's primary key.
295
     *
296
     * @return string
297
     */
298
    public function primaryDescriptionColumn()
299
    {
300
        return 'codigo';
301
    }
302
303
    /**
304
     * Stores the model data in the database.
305
     *
306
     * @return bool
307
     */
308
    public function save()
309
    {
310
        // check accounting exercise
311
        if (empty($this->codejercicio)) {
312
            $this->setDate($this->fecha, $this->hora);
313
        }
314
315
        // empty code?
316
        if (empty($this->codigo)) {
317
            BusinessDocumentCode::getNewCode($this);
318
        }
319
320
        return parent::save();
321
    }
322
323
    /**
324
     * Assign the date and find an accounting exercise.
325
     *
326
     * @param string $date
327
     * @param string $hour
328
     *
329
     * @return bool
330
     */
331
    public function setDate(string $date, string $hour): bool
332
    {
333
        // force check of warehouse-company relation
334
        if (false === $this->setWarehouse($this->codalmacen)) {
335
            return false;
336
        }
337
338
        $ejercicio = new Ejercicio();
339
        $ejercicio->idempresa = $this->idempresa;
340
        if ($ejercicio->loadFromDate($date)) {
341
            $this->codejercicio = $ejercicio->codejercicio;
342
            $this->fecha = $date;
343
            $this->hora = $hour;
344
            return true;
345
        }
346
347
        $this->toolBox()->i18nLog()->warning('accounting-exercise-not-found');
348
        return false;
349
    }
350
351
    /**
352
     * @return string
353
     */
354
    public function subjectColumnValue()
355
    {
356
        return $this->{$this->subjectColumn()};
357
    }
358
359
    /**
360
     * Returns True if there is no errors on properties values.
361
     *
362
     * @return bool
363
     */
364
    public function test()
365
    {
366
        $utils = $this->toolBox()->utils();
367
        $this->observaciones = $utils->noHtml($this->observaciones);
368
369
        // check number
370
        if ((int)$this->numero < 1) {
371
            $this->toolBox()->i18nLog()->error('invalid-number', ['%number%' => $this->numero]);
372
            return false;
373
        }
374
375
        // check total
376
        $total = $this->neto + $this->totalsuplidos + $this->totaliva - $this->totalirpf + $this->totalrecargo;
377
        if (false === $utils->floatcmp($this->total, $total, FS_NF0, true)) {
378
            $this->toolBox()->i18nLog()->error('bad-total-error');
379
            return false;
380
        }
381
382
        /**
383
         * We use the euro as a bridge currency when adding, compare
384
         * or convert amounts in several currencies. For this reason we need
385
         * many decimals.
386
         */
387
        $this->totaleuros = empty($this->tasaconv) ? 0 : round($this->total / $this->tasaconv, 5);
388
389
        return parent::test();
390
    }
391
392
    /**
393
     * Check changed fields before updata the database.
394
     *
395
     * @param string $field
396
     *
397
     * @return bool
398
     */
399
    protected function onChange($field)
400
    {
401
        switch ($field) {
402
            case 'codalmacen':
403
                foreach ($this->getLines() as $line) {
404
                    $line->transfer($this->previousData['codalmacen'], $this->codalmacen);
405
                }
406
                break;
407
408
            case 'codserie':
409
                BusinessDocumentCode::getNewCode($this);
410
                break;
411
412
            case 'fecha':
413
                $oldCodejercicio = $this->codejercicio;
414
                if (false === $this->setDate($this->fecha, $this->hora)) {
415
                    return false;
416
                } elseif ($this->codejercicio != $oldCodejercicio) {
417
                    BusinessDocumentCode::getNewCode($this);
418
                }
419
                break;
420
421
            case 'idempresa':
422
                $this->toolBox()->i18nLog()->warning('non-editable-columns', ['%columns%' => 'idempresa']);
423
                return false;
424
425
            case 'numero':
426
                BusinessDocumentCode::getNewCode($this, false);
427
                break;
428
        }
429
430
        return parent::onChange($field);
431
    }
432
433
    /**
434
     * @param array $values
435
     *
436
     * @return bool
437
     */
438
    protected function saveUpdate(array $values = [])
439
    {
440
        if (false === parent::saveUpdate($values)) {
441
            return false;
442
        }
443
444
        // add audit log
445
        self::toolBox()::i18nLog(self::AUDIT_CHANNEL)->info('updated-model', [
446
            '%model%' => $this->modelClassName(),
447
            '%key%' => $this->primaryColumnValue(),
448
            '%desc%' => $this->primaryDescription(),
449
            'model-class' => $this->modelClassName(),
450
            'model-code' => $this->primaryColumnValue(),
451
            'model-data' => $this->toArray()
452
        ]);
453
        return true;
454
    }
455
456
    /**
457
     * Sets fields to be watched.
458
     *
459
     * @param array $fields
460
     */
461
    protected function setPreviousData(array $fields = [])
462
    {
463
        $more = [
464
            'codalmacen', 'coddivisa', 'codpago', 'codserie',
465
            'fecha', 'hora', 'idempresa', 'numero', 'total'
466
        ];
467
        parent::setPreviousData(array_merge($more, $fields));
468
    }
469
470
    /**
471
     * Sets warehouse and company for this document.
472
     *
473
     * @param string $codalmacen
474
     *
475
     * @return bool
476
     */
477
    protected function setWarehouse($codalmacen)
478
    {
479
        $almacen = new Almacen();
480
        if ($almacen->loadFromCode($codalmacen)) {
481
            $this->codalmacen = $almacen->codalmacen;
482
            $this->idempresa = $almacen->idempresa ?? $this->idempresa;
483
            return true;
484
        }
485
486
        $this->toolBox()->i18nLog()->warning('warehouse-not-found');
487
        return false;
488
    }
489
}
490