Passed
Push — master ( c54296...9f2d90 )
by Esteban De La Fuente
06:57
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 74
    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 74
        $this
243 74
            ->setInputData($inputData)
244 74
            ->setParsedData($parsedData)
245 74
            ->setNormalizedData($normalizedData)
246 74
            ->setOptions($options)
247 74
            ->setXmlDocument($xmlDocument)
248 74
            ->setCaf($caf)
249 74
            ->setCertificate($certificate)
250 74
            ->setDocument($document)
251 74
            ->setDocumentType($documentType)
252 74
            ->setEmisor($emisor)
253 74
            ->setReceptor($receptor)
254 74
        ;
255
    }
256
257
    /**
258
     * {@inheritDoc}
259
     */
260 74
    public function setInputData(string|array|stdClass|null $inputData): static
261
    {
262 74
        if ($inputData === null) {
0 ignored issues
show
introduced by
The condition $inputData === null is always false.
Loading history...
263 65
            $this->inputData = null;
264
265 65
            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 74
    public function getInputData(): ?string
281
    {
282 74
        return $this->inputData;
283
    }
284
285
    /**
286
     * {@inheritDoc}
287
     */
288 74
    public function setParsedData(?array $parsedData): static
289
    {
290 74
        $this->parsedData = $parsedData;
291
292 74
        return $this;
293
    }
294
295
    /**
296
     * {@inheritDoc}
297
     */
298 73
    public function getParsedData(): ?array
299
    {
300 73
        return $this->parsedData;
301
    }
302
303
    /**
304
     * {@inheritDoc}
305
     */
306 74
    public function setNormalizedData(?array $normalizedData): static
307
    {
308 74
        $this->normalizedData = $normalizedData;
309
310 74
        return $this;
311
    }
312
313
    /**
314
     * {@inheritDoc}
315
     */
316 69
    public function getNormalizedData(): ?array
317
    {
318 69
        return $this->normalizedData;
319
    }
320
321
    /**
322
     * {@inheritDoc}
323
     */
324 74
    public function setOptions(array|DataContainerInterface|null $options): static
325
    {
326 74
        if ($options === null) {
0 ignored issues
show
introduced by
The condition $options === null is always false.
Loading history...
327 72
            $options = [];
328
        }
329
330 74
        if (is_array($options)) {
0 ignored issues
show
introduced by
The condition is_array($options) is always true.
Loading history...
331 72
            $options = new DataContainer($options, $this->optionsSchema);
332
        }
333
334 74
        $this->options = $options;
335
336 74
        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 56
    public function getBuilderOptions(): array
359
    {
360 56
        return (array) $this->options?->get('builder');
361
    }
362
363
    /**
364
     * {@inheritDoc}
365
     */
366 60
    public function getNormalizerOptions(): array
367
    {
368 60
        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 56
    public function getRendererOptions(): array
391
    {
392 56
        return (array) $this->options?->get('renderer');
393
    }
394
395
    /**
396
     * {@inheritDoc}
397
     */
398 74
    public function setXmlDocument(?XmlInterface $xmlDocument): static
399
    {
400 74
        $this->xmlDocument = $xmlDocument;
401
402 74
        return $this;
403
    }
404
405
    /**
406
     * {@inheritDoc}
407
     */
408 69
    public function getXmlDocument(): ?XmlInterface
409
    {
410 69
        return $this->xmlDocument;
411
    }
412
413
    /**
414
     * {@inheritDoc}
415
     */
416 74
    public function setCaf(?CafInterface $caf): static
417
    {
418 74
        $this->caf = $caf;
419
420 74
        return $this;
421
    }
422
423
    /**
424
     * {@inheritDoc}
425
     */
426 60
    public function getCaf(): ?CafInterface
427
    {
428 60
        return $this->caf;
429
    }
430
431
    /**
432
     * {@inheritDoc}
433
     */
434 74
    public function setCertificate(?CertificateInterface $certificate): static
435
    {
436 74
        $this->certificate = $certificate;
437
438 74
        return $this;
439
    }
440
441
    /**
442
     * {@inheritDoc}
443
     */
444 60
    public function getCertificate(): ?CertificateInterface
445
    {
446 60
        return $this->certificate;
447
    }
448
449
    /**
450
     * {@inheritDoc}
451
     */
452 74
    public function setDocument(?DocumentInterface $document): static
453
    {
454 74
        $this->document = $document;
455
456 74
        return $this;
457
    }
458
459
    /**
460
     * {@inheritDoc}
461
     */
462 69
    public function getDocument(): ?DocumentInterface
463
    {
464 69
        return $this->document;
465
    }
466
467
    /**
468
     * {@inheritDoc}
469
     */
470 74
    public function setDocumentType(?TipoDocumentoInterface $documentType): static
471
    {
472 74
        $this->documentType = $documentType;
473
474 74
        return $this;
475
    }
476
477
    /**
478
     * {@inheritDoc}
479
     */
480 70
    public function setTipoDocumento(?TipoDocumentoInterface $tipoDocumento): static
481
    {
482 70
        return $this->setDocumentType($tipoDocumento);
483
    }
484
485
    /**
486
     * {@inheritDoc}
487
     */
488 73
    public function getDocumentType(): ?TipoDocumentoInterface
489
    {
490 73
        return $this->documentType;
491
    }
492
493
    /**
494
     * {@inheritDoc}
495
     */
496 73
    public function getTipoDocumento(): ?TipoDocumentoInterface
497
    {
498 73
        return $this->getDocumentType();
499
    }
500
501
    /**
502
     * {@inheritDoc}
503
     */
504 73
    public function getDocumentTypeId(): ?int
505
    {
506 73
        $TipoDTE = $this->parsedData['Encabezado']['IdDoc']['TipoDTE']
507 73
            ?? $this->normalizedData['Encabezado']['IdDoc']['TipoDTE']
508 73
            ?? $this->xmlDocument?->query('//Encabezado/IdDoc/TipoDTE')
509 73
            ?? $this->document?->getCodigo()
510 66
            ?? null
511 73
        ;
512
513 73
        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 71
        return (int) $TipoDTE;
520
    }
521
522
    /**
523
     * {@inheritDoc}
524
     */
525 73
    public function getCodigoTipoDocumento(): ?int
526
    {
527 73
        return $this->getDocumentTypeId();
528
    }
529
530
    /**
531
     * {@inheritDoc}
532
     */
533 74
    public function setEmisor(?EmisorInterface $emisor): static
534
    {
535 74
        $this->emisor = $emisor;
536
537 74
        return $this;
538
    }
539
540
    /**
541
     * {@inheritDoc}
542
     */
543 69
    public function getEmisor(): ?EmisorInterface
544
    {
545 69
        return $this->emisor;
546
    }
547
548
    /**
549
     * {@inheritDoc}
550
     */
551 74
    public function setReceptor(?ReceptorInterface $receptor): static
552
    {
553 74
        $this->receptor = $receptor;
554
555 74
        return $this;
556
    }
557
558
    /**
559
     * {@inheritDoc}
560
     */
561 69
    public function getReceptor(): ?ReceptorInterface
562
    {
563 69
        return $this->receptor;
564
    }
565
566
    /**
567
     * {@inheritDoc}
568
     */
569 56
    public function setTimbre(?array $timbre): static
570
    {
571 56
        $this->timbre = $timbre;
572
573 56
        return $this;
574
    }
575
576
    /**
577
     * {@inheritDoc}
578
     */
579 60
    public function getTimbre(): ?array
580
    {
581 60
        return $this->timbre;
582
    }
583
584
    /**
585
     * {@inheritDoc}
586
     */
587 60
    public function getData(): ?array
588
    {
589
        // Si los datos ya estaban generados se entregan.
590 60
        if ($this->data !== null) {
591
            return $this->data;
592
        }
593
594
        // Si no hay datos normalizados se entrega `null`.
595 60
        if (!$this->getNormalizedData()) {
596
            return null;
597
        }
598
599
        // Se arma la estructura del nodo Documento.
600 60
        $tagXml = $this->getTipoDocumento()->getTagXml()->getNombre();
601 60
        $this->data = [
602 60
            'DTE' => [
603 60
                '@attributes' => [
604 60
                    'version' => '1.0',
605 60
                    'xmlns' => 'http://www.sii.cl/SiiDte',
606 60
                ],
607 60
                $tagXml => array_merge(
608 60
                    [
609 60
                        '@attributes' => [
610 60
                            'ID' => $this->getId(),
611 60
                        ],
612 60
                    ],
613 60
                    $this->getNormalizedData(),
614 60
                    (array) $this->getTimbre(),
615 60
                ),
616 60
                //'Signature' => '', // Se agrega al firmar (NO INCLUIR ACÁ).
617 60
            ],
618 60
        ];
619
620
        // Se entrega la estructura con los datos.
621 60
        return $this->data;
622
    }
623
624
    /**
625
     * {@inheritDoc}
626
     */
627 60
    public function getId(): string
628
    {
629 60
        $folio = $this->getFolio();
630
631 60
        if (is_int($folio)) {
632 55
            return sprintf(
633 55
                'LibreDTE_%s_T%03dF%09d',
634 55
                $this->getNormalizedData()['Encabezado']['Emisor']['RUTEmisor'],
635 55
                $this->getNormalizedData()['Encabezado']['IdDoc']['TipoDTE'],
636 55
                $folio
637 55
            );
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
        $data = $this->getNormalizedData() ?? $this->getParsedData();
660
661
        if ($data === null) {
662
            throw new LogicException(
663
                'No es posible asignar el folio si no existen datos normalizados o parseados.'
664
            );
665
        }
666
667
        $data['Encabezado']['IdDoc']['Folio'] = $folio;
668
669
        if ($this->getNormalizedData()) {
670
            return $this->setNormalizedData($data);
671
        }
672
673
        return $this->setParsedData($data);
674
    }
675
676
    /**
677
     * {@inheritDoc}
678
     */
679 60
    public function getFolio(): int|string|null
680
    {
681 60
        $data = $this->getNormalizedData() ?? $this->getParsedData();
682
683 60
        $folio = $data['Encabezado']['IdDoc']['Folio'];
684
685 60
        if (!$folio) {
686 5
            return null;
687
        }
688
689 55
        return is_numeric($folio) ? (int) $folio : (string) $folio;
690
    }
691
692
    /**
693
     * {@inheritDoc}
694
     */
695 4
    public function withCaf(CafInterface $caf): DocumentBagInterface
696
    {
697 4
        $class = static::class;
698
699 4
        return new $class(
700 4
            inputData: $this->getInputData(),
701 4
            parsedData: $this->getParsedData(),
702 4
            normalizedData: $this->getNormalizedData(),
703 4
            options: $this->getOptions(),
704 4
            caf: $caf,
705 4
            certificate: $this->getCertificate(),
706 4
            documentType: $this->getDocumentType(),
707 4
            emisor: $this->getEmisor(),
708 4
            receptor: $this->getReceptor()
709 4
        );
710
    }
711
712
    /**
713
     * {@inheritDoc}
714
     */
715 4
    public function withCertificate(
716
        CertificateInterface $certificate
717
    ): DocumentBagInterface {
718 4
        $class = static::class;
719
720 4
        return new $class(
721 4
            inputData: $this->getInputData(),
722 4
            parsedData: $this->getParsedData(),
723 4
            normalizedData: $this->getNormalizedData(),
724 4
            options: $this->getOptions(),
725 4
            caf: $this->getCaf(),
726 4
            certificate: $certificate,
727 4
            documentType: $this->getDocumentType(),
728 4
            emisor: $this->getEmisor(),
729 4
            receptor: $this->getReceptor()
730 4
        );
731
    }
732
733
    /**
734
     * {@inheritDoc}
735
     */
736 62
    public function getAlias(): string
737
    {
738 62
        return $this->getTipoDocumento()?->getAlias()
739 62
            ?? (
740 62
                $this->getTipoDocumento()?->getCodigo()
741 1
                    ? 'documento_' .  $this->getTipoDocumento()->getCodigo()
742 2
                    : null
743 62
            )
744 62
            ?? $this->getParsedData()['Encabezado']['IdDoc']['TipoDTE']
745 62
            ?? 'documento_desconocido'
746 62
        ;
747
    }
748
}
749