Test Failed
Push — master ( 5bd0ed...9eafef )
by Esteban De La Fuente
08:45
created

DocumentBag::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 12
nc 1
nop 11
dl 0
loc 25
ccs 13
cts 13
cp 1
crap 1
rs 9.8666
c 1
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * LibreDTE: Biblioteca PHP (Núcleo).
7
 * Copyright (C) LibreDTE <https://www.libredte.cl>
8
 *
9
 * Este programa es software libre: usted puede redistribuirlo y/o modificarlo
10
 * bajo los términos de la Licencia Pública General Affero de GNU publicada por
11
 * la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o
12
 * (a su elección) cualquier versión posterior de la misma.
13
 *
14
 * Este programa se distribuye con la esperanza de que sea útil, pero SIN
15
 * GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD
16
 * PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la Licencia Pública
17
 * General Affero de GNU para obtener una información más detallada.
18
 *
19
 * Debería haber recibido una copia de la Licencia Pública General Affero de
20
 * GNU junto a este programa.
21
 *
22
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
23
 */
24
25
namespace libredte\lib\Core\Package\Billing\Component\Document\Support;
26
27
use Derafu\Lib\Core\Package\Prime\Component\Certificate\Contract\CertificateInterface;
28
use Derafu\Lib\Core\Package\Prime\Component\Xml\Contract\XmlInterface;
29
use Derafu\Lib\Core\Support\Store\Contract\DataContainerInterface;
30
use Derafu\Lib\Core\Support\Store\DataContainer;
31
use libredte\lib\Core\Package\Billing\Component\Document\Contract\DocumentBagInterface;
32
use libredte\lib\Core\Package\Billing\Component\Document\Contract\DocumentInterface;
33
use libredte\lib\Core\Package\Billing\Component\Document\Contract\TipoDocumentoInterface;
34
use libredte\lib\Core\Package\Billing\Component\Document\Exception\DocumentException;
35
use libredte\lib\Core\Package\Billing\Component\Identifier\Contract\CafInterface;
36
use libredte\lib\Core\Package\Billing\Component\TradingParties\Contract\EmisorInterface;
37
use libredte\lib\Core\Package\Billing\Component\TradingParties\Contract\ReceptorInterface;
38
use LogicException;
39
use stdClass;
40
41
/**
42
 * Contenedor de datos del documento tributario electrónico.
43
 *
44
 * Permite "mover" un documento, junto a otros datos asociados, por métodos de
45
 * manera sencilla y, sobre todo, extensible.
46
 */
47
class DocumentBag implements DocumentBagInterface
48
{
49
    /**
50
     * Datos originales de entrada que se utilizarán para construir el
51
     * documento tributario.
52
     *
53
     * El formato de estos datos puede ser cualquiera soportado por los parsers.
54
     *
55
     * @var string|null
56
     */
57
    private ?string $inputData;
58
59
    /**
60
     * Datos de entrada procesados (parseados).
61
     *
62
     * Están en el formato estándar de LibreDTE. Que es básicamente el oficial
63
     * del SII. Con algunas extensiones, como los datos "extras".
64
     *
65
     * Estos son los datos que se usarán para construir el documento. Estos
66
     * datos no están normaliados, solo parseados.
67
     *
68
     * @var array|null
69
     */
70
    private ?array $parsedData;
71
72
    /**
73
     * Datos normalizados del documento tributario.
74
     *
75
     * Son los datos con todos sus campos necesarios ya determinados, calculados
76
     * y validados.
77
     *
78
     * La estructura de estos datos depende de los normalizadores.
79
     *
80
     * Importante: si se desactiva la normalización este arreglo contendrá lo
81
     * mismo que $parsedData pues no se tocarán los datos de entrada procesados.
82
     *
83
     * @var array|null
84
     */
85
    private ?array $normalizedData;
86
87
    /**
88
     * Opciones para los workers asociados al documento.
89
     *
90
     * Se definen los siguientes índices para las opciones:
91
     *
92
     *   - `builder`: Opciones para los constructores.
93
     *   - `normalizer`: Opciones para los normalizadores.
94
     *   - `parser`: Opciones para los analizadores sintácticos.
95
     *   - `renderer`: Opciones para los renderizadores.
96
     *   - `sanitizer`: Opciones para los sanitizadores.
97
     *   - `validator`: Opciones para los validadores.
98
     *
99
     * Se usarán las opciones por defecto en cada worker si no se indican los
100
     * índices en el arreglo $options.
101
     *
102
     * @var DataContainerInterface|null
103
     */
104
    private ?DataContainerInterface $options;
105
106
    /**
107
     * Reglas de esquema de las opciones del documento.
108
     *
109
     * El formato del esquema es el utilizado por
110
     * Symfony\Component\OptionsResolver\OptionsResolver.
111
     *
112
     * Acá solo se indicarán los índices que deben pueden existir en las
113
     * opciones. No se define el esquema de cada opción pues cada clase que
114
     * utilice estas opciones deberá resolver y validar sus propias opciones.
115
     *
116
     * @var array
117
     */
118
    protected array $optionsSchema = [
119
        'builder' => [
120
            'types' => 'array',
121
            'default' => [],
122
        ],
123
        'normalizer' => [
124
            'types' => 'array',
125
            'default' => [],
126
        ],
127
        'parser' => [
128
            'types' => 'array',
129
            'default' => [],
130
        ],
131
        'renderer' => [
132
            'types' => 'array',
133
            'default' => [],
134
        ],
135
        'sanitizer' => [
136
            'types' => 'array',
137
            'default' => [],
138
        ],
139
        'validator' => [
140
            'types' => 'array',
141
            'default' => [],
142
        ],
143
    ];
144
145
    /**
146
     * Instancia del documento XML asociada al DTE.
147
     *
148
     * @var XmlInterface|null
149
     */
150
    private ?XmlInterface $xmlDocument;
151
152
    /**
153
     * Código de Asignación de Folios (CAF) para timbrar el Documento Tributario
154
     * Electrónico (DTE) que se generará.
155
     *
156
     * @var CafInterface|null
157
     */
158
    private ?CafInterface $caf;
159
160
    /**
161
     * Certificado digital (firma electrónica) para la firma del documento.
162
     *
163
     * @var CertificateInterface|null
164
     */
165
    private ?CertificateInterface $certificate;
166
167
    /**
168
     * Entidad con el documento tributario electrónico generado.
169
     *
170
     * @var DocumentInterface|null
171
     */
172
    private ?DocumentInterface $document;
173
174
    /**
175
     * Entidad que representa al tipo de documento tributario que está contenido
176
     * en esta bolsa.
177
     *
178
     * @var TipoDocumentoInterface|null
179
     */
180
    private ?TipoDocumentoInterface $documentType = null;
181
182
    /**
183
     * Emisor del documento tributario.
184
     *
185
     * @var EmisorInterface|null
186
     */
187
    private ?EmisorInterface $emisor = null;
188
189
    /**
190
     * Receptor del documento tributario.
191
     *
192
     * @var ReceptorInterface|null
193
     */
194
    private ?ReceptorInterface $receptor = null;
195
196
    /**
197
     * Arreglo con la estructura del nodo TED del documento.
198
     *
199
     * @var array|null
200
     */
201
    private ?array $timbre = null;
202
203
    /**
204
     * Arreglo con los datos normalizados consolidados con el timbre y la firma
205
     * si existen en la bolsa.
206
     *
207
     * @var array|null
208
     */
209
    private ?array $data = null;
210
211
    /**
212
     * Constructor del contenedor.
213
     *
214
     * Recibe los datos en diferentes formatos para pasarlos a los setters que
215
     * los normalizan y asignan al contenedor.
216
     *
217
     * @param string|array|stdClass|null $inputData
218
     * @param array|null $parsedData
219
     * @param array|null $normalizedData
220
     * @param array|DataContainerInterface|null $options
221
     * @param XmlInterface|null $xmlDocument
222
     * @param CafInterface|null $caf
223
     * @param CertificateInterface|null $certificate
224
     * @param DocumentInterface|null $document
225
     * @param TipoDocumentoInterface|null $documentType
226
     * @param EmisorInterface|null $emisor
227
     * @param ReceptorInterface|null $receptor
228
     */
229 72
    public function __construct(
230
        string|array|stdClass $inputData = null,
231
        array $parsedData = null,
232
        array $normalizedData = null,
233
        array|DataContainerInterface $options = null,
234
        XmlInterface $xmlDocument = null,
235
        CafInterface $caf = null,
236
        CertificateInterface $certificate = null,
237
        DocumentInterface $document = null,
238
        TipoDocumentoInterface $documentType = null,
239
        EmisorInterface $emisor = null,
240
        ReceptorInterface $receptor = null
241
    ) {
242 72
        $this
243 72
            ->setInputData($inputData)
244 72
            ->setParsedData($parsedData)
245 72
            ->setNormalizedData($normalizedData)
246 72
            ->setOptions($options)
247 72
            ->setXmlDocument($xmlDocument)
248 72
            ->setCaf($caf)
249 72
            ->setCertificate($certificate)
250 72
            ->setDocument($document)
251 72
            ->setDocumentType($documentType)
252 72
            ->setEmisor($emisor)
253 72
            ->setReceptor($receptor)
254 72
        ;
255
    }
256
257
    /**
258
     * {@inheritDoc}
259
     */
260 72
    public function setInputData(string|array|stdClass|null $inputData): static
261
    {
262 72
        if ($inputData === null) {
0 ignored issues
show
introduced by
The condition $inputData === null is always false.
Loading history...
263 63
            $this->inputData = null;
264
265 63
            return $this;
266
        }
267
268 14
        if (!is_string($inputData)) {
0 ignored issues
show
introduced by
The condition is_string($inputData) is always false.
Loading history...
269 6
            $inputData = json_encode($inputData);
270
        }
271
272 14
        $this->inputData = $inputData;
273
274 14
        return $this;
275
    }
276
277
    /**
278
     * {@inheritDoc}
279
     */
280 72
    public function getInputData(): ?string
281
    {
282 72
        return $this->inputData;
283
    }
284
285
    /**
286
     * {@inheritDoc}
287
     */
288 72
    public function setParsedData(?array $parsedData): static
289
    {
290 72
        $this->parsedData = $parsedData;
291
292 72
        return $this;
293
    }
294
295
    /**
296
     * {@inheritDoc}
297
     */
298 71
    public function getParsedData(): ?array
299
    {
300 71
        return $this->parsedData;
301
    }
302
303
    /**
304
     * {@inheritDoc}
305
     */
306 72
    public function setNormalizedData(?array $normalizedData): static
307
    {
308 72
        $this->normalizedData = $normalizedData;
309
310 72
        return $this;
311
    }
312
313
    /**
314
     * {@inheritDoc}
315
     */
316 67
    public function getNormalizedData(): ?array
317
    {
318 67
        return $this->normalizedData;
319
    }
320
321
    /**
322
     * {@inheritDoc}
323
     */
324 72
    public function setOptions(array|DataContainerInterface|null $options): static
325
    {
326 72
        if ($options === null) {
0 ignored issues
show
introduced by
The condition $options === null is always false.
Loading history...
327 70
            $options = [];
328
        }
329
330 72
        if (is_array($options)) {
0 ignored issues
show
introduced by
The condition is_array($options) is always true.
Loading history...
331 70
            $options = new DataContainer($options, $this->optionsSchema);
332
        }
333
334 72
        $this->options = $options;
335
336 72
        return $this;
337
    }
338
339
    /**
340
     * {@inheritDoc}
341
     */
342 4
    public function getOptions(): ?DataContainerInterface
343
    {
344 4
        return $this->options;
345
    }
346
347
    /**
348
     * {@inheritDoc}
349
     */
350 14
    public function getParserOptions(): array
351
    {
352 14
        return (array) $this->options?->get('parser');
353
    }
354
355
    /**
356
     * {@inheritDoc}
357
     */
358 54
    public function getBuilderOptions(): array
359
    {
360 54
        return (array) $this->options?->get('builder');
361
    }
362
363
    /**
364
     * {@inheritDoc}
365
     */
366 58
    public function getNormalizerOptions(): array
367
    {
368 58
        return (array) $this->options?->get('normalizer');
369
    }
370
371
    /**
372
     * {@inheritDoc}
373
     */
374
    public function getSanitizerOptions(): array
375
    {
376
        return (array) $this->options?->get('sanitizer');
377
    }
378
379
    /**
380
     * {@inheritDoc}
381
     */
382
    public function getValidatorOptions(): array
383
    {
384
        return (array) $this->options?->get('validator');
385
    }
386
387
    /**
388
     * {@inheritDoc}
389
     */
390 54
    public function getRendererOptions(): array
391
    {
392 54
        return (array) $this->options?->get('renderer');
393
    }
394
395
    /**
396
     * {@inheritDoc}
397
     */
398 72
    public function setXmlDocument(?XmlInterface $xmlDocument): static
399
    {
400 72
        $this->xmlDocument = $xmlDocument;
401
402 72
        return $this;
403
    }
404
405
    /**
406
     * {@inheritDoc}
407
     */
408 67
    public function getXmlDocument(): ?XmlInterface
409
    {
410 67
        return $this->xmlDocument;
411
    }
412
413
    /**
414
     * {@inheritDoc}
415
     */
416 72
    public function setCaf(?CafInterface $caf): static
417
    {
418 72
        $this->caf = $caf;
419
420 72
        return $this;
421
    }
422
423
    /**
424
     * {@inheritDoc}
425
     */
426 58
    public function getCaf(): ?CafInterface
427
    {
428 58
        return $this->caf;
429
    }
430
431
    /**
432
     * {@inheritDoc}
433
     */
434 72
    public function setCertificate(?CertificateInterface $certificate): static
435
    {
436 72
        $this->certificate = $certificate;
437
438 72
        return $this;
439
    }
440
441
    /**
442
     * {@inheritDoc}
443
     */
444 58
    public function getCertificate(): ?CertificateInterface
445
    {
446 58
        return $this->certificate;
447
    }
448
449
    /**
450
     * {@inheritDoc}
451
     */
452 72
    public function setDocument(?DocumentInterface $document): static
453
    {
454 72
        $this->document = $document;
455
456 72
        return $this;
457
    }
458
459
    /**
460
     * {@inheritDoc}
461
     */
462 67
    public function getDocument(): ?DocumentInterface
463
    {
464 67
        return $this->document;
465
    }
466
467
    /**
468
     * {@inheritDoc}
469
     */
470 72
    public function setDocumentType(?TipoDocumentoInterface $documentType): static
471
    {
472 72
        $this->documentType = $documentType;
473
474 72
        return $this;
475
    }
476
477
    /**
478
     * {@inheritDoc}
479
     */
480 68
    public function setTipoDocumento(?TipoDocumentoInterface $tipoDocumento): static
481
    {
482 68
        return $this->setDocumentType($tipoDocumento);
483
    }
484
485
    /**
486
     * {@inheritDoc}
487
     */
488 71
    public function getDocumentType(): ?TipoDocumentoInterface
489
    {
490 71
        return $this->documentType;
491
    }
492
493
    /**
494
     * {@inheritDoc}
495
     */
496 71
    public function getTipoDocumento(): ?TipoDocumentoInterface
497
    {
498 71
        return $this->getDocumentType();
499
    }
500
501
    /**
502
     * {@inheritDoc}
503
     */
504 71
    public function getDocumentTypeId(): ?int
505
    {
506 71
        $TipoDTE = $this->parsedData['Encabezado']['IdDoc']['TipoDTE']
507 71
            ?? $this->normalizedData['Encabezado']['IdDoc']['TipoDTE']
508 71
            ?? $this->xmlDocument?->query('//Encabezado/IdDoc/TipoDTE')
509 71
            ?? $this->document?->getCodigo()
510 64
            ?? null
511 71
        ;
512
513 71
        if (!$TipoDTE) {
514 2
            throw new DocumentException(
515 2
                'Falta indicar el tipo de documento (TipoDTE) en los datos del DTE.'
516 2
            );
517
        }
518
519 69
        return (int) $TipoDTE;
520
    }
521
522
    /**
523
     * {@inheritDoc}
524
     */
525 71
    public function getCodigoTipoDocumento(): ?int
526
    {
527 71
        return $this->getDocumentTypeId();
528
    }
529
530
    /**
531
     * {@inheritDoc}
532
     */
533 72
    public function setEmisor(?EmisorInterface $emisor): static
534
    {
535 72
        $this->emisor = $emisor;
536
537 72
        return $this;
538
    }
539
540
    /**
541
     * {@inheritDoc}
542
     */
543 67
    public function getEmisor(): ?EmisorInterface
544
    {
545 67
        return $this->emisor;
546
    }
547
548
    /**
549
     * {@inheritDoc}
550
     */
551 72
    public function setReceptor(?ReceptorInterface $receptor): static
552
    {
553 72
        $this->receptor = $receptor;
554
555 72
        return $this;
556
    }
557
558
    /**
559
     * {@inheritDoc}
560
     */
561 67
    public function getReceptor(): ?ReceptorInterface
562
    {
563 67
        return $this->receptor;
564
    }
565
566
    /**
567
     * {@inheritDoc}
568
     */
569 54
    public function setTimbre(?array $timbre): static
570
    {
571 54
        $this->timbre = $timbre;
572
573 54
        return $this;
574
    }
575
576
    /**
577
     * {@inheritDoc}
578
     */
579 58
    public function getTimbre(): ?array
580
    {
581 58
        return $this->timbre;
582
    }
583
584
    /**
585
     * {@inheritDoc}
586
     */
587 58
    public function getData(): ?array
588
    {
589
        // Si los datos ya estaban generados se entregan.
590 58
        if ($this->data !== null) {
591
            return $this->data;
592
        }
593
594
        // Si no hay datos normalizados se entrega `null`.
595 58
        if (!$this->getNormalizedData()) {
596
            return null;
597
        }
598
599
        // Se arma la estructura del nodo Documento.
600 58
        $tagXml = $this->getTipoDocumento()->getTagXml()->getNombre();
601 58
        $this->data = [
602 58
            'DTE' => [
603 58
                '@attributes' => [
604 58
                    'version' => '1.0',
605 58
                    'xmlns' => 'http://www.sii.cl/SiiDte',
606 58
                ],
607 58
                $tagXml => array_merge(
608 58
                    [
609 58
                        '@attributes' => [
610 58
                            'ID' => $this->getId(),
611 58
                        ],
612 58
                    ],
613 58
                    $this->getNormalizedData(),
614 58
                    (array) $this->getTimbre(),
615 58
                ),
616 58
                //'Signature' => '', // Se agrega al firmar (NO INCLUIR ACÁ).
617 58
            ],
618 58
        ];
619
620
        // Se entrega la estructura con los datos.
621 58
        return $this->data;
622
    }
623
624
    /**
625
     * {@inheritDoc}
626
     */
627 58
    public function getId(): string
628
    {
629 58
        $folio = $this->getFolio();
630
631 58
        if (is_int($folio)) {
632 53
            return sprintf(
633 53
                'LibreDTE_%s_T%03dF%09d',
634 53
                $this->getNormalizedData()['Encabezado']['Emisor']['RUTEmisor'],
635 53
                $this->getNormalizedData()['Encabezado']['IdDoc']['TipoDTE'],
636 53
                $folio
637 53
            );
638
        } else {
639 5
            return sprintf(
640 5
                'LibreDTE_%s_%03d-%s',
641 5
                $this->getNormalizedData()['Encabezado']['Emisor']['RUTEmisor'],
642 5
                $this->getNormalizedData()['Encabezado']['IdDoc']['TipoDTE'],
643 5
                $folio
644 5
            );
645
        }
646
    }
647
648
    /**
649
     * {@inheritDoc}
650
     */
651
    public function setFolio(int $folio): static
652
    {
653
        if ($this->getXmlDocument()) {
654
            throw new LogicException(
655
                'No es posible asignar el folio si ya se generó el documento XML.'
656
            );
657
        }
658
659
        $parsedData = $this->getParsedData();
660
        $normalizedData = $this->getNormalizedData();
661
662
        if ($parsedData === null && $normalizedData === null) {
663
            throw new LogicException(
664
                'No es posible asignar el folio si no existen datos parseados o normalizados.'
665
            );
666
        }
667
668
        if ($parsedData !== null) {
669
            $parsedData['Encabezado']['IdDoc']['Folio'] = $folio;
670
            $this->setParsedData($parsedData);
671
        }
672
673
        if ($normalizedData !== null) {
674
            $normalizedData['Encabezado']['IdDoc']['Folio'] = $folio;
675
            $this->setNormalizedData($normalizedData);
676
        }
677
678
        return $this;
679 58
    }
680
681 58
    /**
682
     * {@inheritDoc}
683 58
     */
684
    public function getFolio(): int|string|null
685 58
    {
686 5
        $data = $this->getNormalizedData() ?? $this->getParsedData();
687
688
        $folio = $data['Encabezado']['IdDoc']['Folio'];
689 53
690
        if (!$folio) {
691
            return null;
692
        }
693
694
        return is_numeric($folio) ? (int) $folio : (string) $folio;
695 4
    }
696
697 4
    /**
698
     * {@inheritDoc}
699 4
     */
700 4
    public function withCaf(CafInterface $caf): DocumentBagInterface
701 4
    {
702 4
        $class = static::class;
703 4
704 4
        return new $class(
705 4
            inputData: $this->getInputData(),
706 4
            parsedData: $this->getParsedData(),
707 4
            normalizedData: $this->getNormalizedData(),
708 4
            options: $this->getOptions(),
709 4
            caf: $caf,
710
            certificate: $this->getCertificate(),
711
            documentType: $this->getDocumentType(),
712
            emisor: $this->getEmisor(),
713
            receptor: $this->getReceptor()
714
        );
715 4
    }
716
717
    /**
718 4
     * {@inheritDoc}
719
     */
720 4
    public function withCertificate(
721 4
        CertificateInterface $certificate
722 4
    ): DocumentBagInterface {
723 4
        $class = static::class;
724 4
725 4
        return new $class(
726 4
            inputData: $this->getInputData(),
727 4
            parsedData: $this->getParsedData(),
728 4
            normalizedData: $this->getNormalizedData(),
729 4
            options: $this->getOptions(),
730 4
            caf: $this->getCaf(),
731
            certificate: $certificate,
732
            documentType: $this->getDocumentType(),
733
            emisor: $this->getEmisor(),
734
            receptor: $this->getReceptor()
735
        );
736 60
    }
737
738 60
    /**
739 60
     * {@inheritDoc}
740 60
     */
741 1
    public function getAlias(): string
742 2
    {
743 60
        return $this->getTipoDocumento()?->getAlias()
744 60
            ?? (
745 60
                $this->getTipoDocumento()?->getCodigo()
746 60
                    ? 'documento_' .  $this->getTipoDocumento()->getCodigo()
747
                    : null
748
            )
749
            ?? $this->getParsedData()['Encabezado']['IdDoc']['TipoDTE']
750
            ?? 'documento_desconocido'
751
        ;
752
    }
753
}
754