Test Failed
Push — master ( 59e870...659f9a )
by Esteban De La Fuente
04:33
created

DocumentBag::setDocument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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