AbstractDocument::query()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
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\Abstract;
26
27
use Derafu\Lib\Core\Helper\Arr;
28
use Derafu\Lib\Core\Helper\Selector;
29
use Derafu\Lib\Core\Package\Prime\Component\Entity\Entity\Entity;
30
use Derafu\Lib\Core\Package\Prime\Component\Xml\Contract\XmlInterface;
31
use Derafu\Lib\Core\Package\Prime\Component\Xml\Exception\XmlException;
32
use libredte\lib\Core\Package\Billing\Component\Document\Contract\DocumentInterface;
33
use libredte\lib\Core\Package\Billing\Component\Document\Enum\CodigoDocumento;
34
use LogicException;
35
36
/**
37
 * Clase abstracta (base) de la representación de un documento tributario
38
 * electrónico.
39
 */
40
abstract class AbstractDocument extends Entity implements DocumentInterface
41
{
42
    /**
43
     * Código del tipo de documento tributario al que está asociada esta
44
     * instancia de un documento.
45
     *
46
     * Este valor está definido en cada clase que hereda de esta.
47
     */
48
    protected CodigoDocumento $tipoDocumento;
49
50
    /**
51
     * Instancia del documento XML asociado a los datos.
52
     *
53
     * @var XmlInterface
54
     */
55
    protected readonly XmlInterface $xmlDocument;
56
57
    /**
58
     * Datos del documento tributario estandarizados.
59
     *
60
     * @var array
61
     */
62
    private array $datos;
63
64
    /**
65
     * Constructor del documento tributario.
66
     *
67
     * @param XmlInterface $xmlDocument
68
     * @return void
69
     */
70 116
    public function __construct(XmlInterface $xmlDocument)
71
    {
72 116
        $this->xmlDocument = $xmlDocument;
0 ignored issues
show
Bug introduced by
The property xmlDocument is declared read-only in libredte\lib\Core\Packag...stract\AbstractDocument.
Loading history...
73
74
        // Validar que el código que está en el XmlDocument sea el que la clase
75
        // de la estrategia espera.
76 116
        $xmlDocumentTipoDTE = (int) $xmlDocument->query('//Encabezado/IdDoc/TipoDTE');
77 116
        if ($xmlDocumentTipoDTE !== $this->getCodigo()) {
78
            throw new LogicException(sprintf(
79
                'El código %s del XmlDocument cargado en %s no corresponde con el esperado, debería ser código %d.',
80
                $xmlDocumentTipoDTE,
81
                static::class,
82
                $this->getCodigo()
83
            ));
84
        }
85
    }
86
87
    /**
88
     * {@inheritDoc}
89
     */
90
    public function __toString(): string
91
    {
92
        return $this->getId();
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98 103
    public function getXmlDocument(): XmlInterface
99
    {
100 103
        return $this->xmlDocument;
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106 102
    public function saveXml(): string
107
    {
108 102
        return $this->xmlDocument->saveXml();
109
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114
    public function getXml(): string
115
    {
116
        return $this->xmlDocument->getXml();
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     */
122
    public function getId(): string
123
    {
124
        return sprintf(
125
            '%s_T%03dF%09d',
126
            $this->getRutEmisor(),
127
            $this->getCodigo(),
128
            $this->getFolio()
129
        );
130
    }
131
132
    /**
133
     * {@inheritDoc}
134
     */
135 116
    public function getCodigo(): int
136
    {
137 116
        return $this->tipoDocumento->getCodigo();
138
    }
139
140
    /**
141
     * {@inheritDoc}
142
     */
143 103
    public function getFolio(): int
144
    {
145 103
        return (int) $this->xmlDocument->query('//Encabezado/IdDoc/Folio');
146
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151
    public function getTipoDocumento(): CodigoDocumento
152
    {
153
        return $this->tipoDocumento;
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159 115
    public function getEmisor(): array
160
    {
161 115
        return $this->xmlDocument->query('//Encabezado/Emisor');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xmlDocumen...('//Encabezado/Emisor') could return the type null|string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     */
167 103
    public function getRutEmisor(): string
168
    {
169 103
        return $this->xmlDocument->query('//Encabezado/Emisor/RUTEmisor');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xmlDocumen...zado/Emisor/RUTEmisor') could return the type array|null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
170
    }
171
172
    /**
173
     * {@inheritDoc}
174
     */
175 116
    public function getReceptor(): array
176
    {
177 116
        return $this->xmlDocument->query('//Encabezado/Receptor');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xmlDocumen...//Encabezado/Receptor') could return the type null|string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
178
    }
179
180
    /**
181
     * {@inheritDoc}
182
     */
183 103
    public function getRutReceptor(): string
184
    {
185 103
        return $this->xmlDocument->query('//Encabezado/Receptor/RUTRecep');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xmlDocumen...ado/Receptor/RUTRecep') could return the type array|null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
186
    }
187
188
    /**
189
     * {@inheritDoc}
190
     */
191 103
    public function getRazonSocialReceptor(): string
192
    {
193 103
        return $this->xmlDocument->query('//Encabezado/Receptor/RznSocRecep');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xmlDocumen.../Receptor/RznSocRecep') could return the type array|null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
194
    }
195
196
    /**
197
     * {@inheritDoc}
198
     */
199 103
    public function getFechaEmision(): string
200
    {
201 103
        return $this->xmlDocument->query('//Encabezado/IdDoc/FchEmis');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xmlDocumen...abezado/IdDoc/FchEmis') could return the type array|null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
202
    }
203
204
    /**
205
     * {@inheritDoc}
206
     */
207
    public function getTotales(): array
208
    {
209
        return $this->xmlDocument->query('//Encabezado/Totales');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xmlDocumen...'//Encabezado/Totales') could return the type null|string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215 107
    public function getMontoTotal(): int|float
216
    {
217 107
        $monto = (float) $this->xmlDocument->query('//Encabezado/Totales/MntTotal');
218
219
        // Verificar si el monto es equivalente a un entero.
220 107
        if (floor($monto) == $monto) {
221 107
            return (int) $monto;
222
        }
223
224
        // Entregar como flotante.
225
        return $monto;
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231
    public function getMoneda(): string
232
    {
233
        $moneda = $this->query('//Encabezado/Totales/TpoMoneda') ?? 'PESO CL';
234
235
        return (string) $moneda;
236
    }
237
238
    /**
239
     * {@inheritDoc}
240
     */
241 103
    public function getDetalle(?int $index = null): array
242
    {
243 103
        $detalle = $this->xmlDocument->query('//Detalle');
244 103
        if ($detalle === null) {
245
            return [];
246
        }
247
248 103
        if (!isset($detalle[0])) {
249 99
            $detalle = [$detalle];
250
        }
251
252 103
        return $index !== null ? ($detalle[$index] ?? []) : $detalle;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $index !== null ?...] ?? array() : $detalle could return the type string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
253
    }
254
255
    /**
256
     * {@inheritDoc}
257
     */
258 103
    public function getDatos(): array
259
    {
260
        // Si los datos del DTE no están determinados se crean de una manera
261
        // estandarizada compatible con los datos de entrada normalizados.
262 103
        if (!isset($this->datos)) {
263 103
            $array = $this->xmlDocument->toArray();
264
265 103
            $array = $array['DTE']['Documento']
266 103
                ?? $array['DTE']['Exportaciones']
267 103
                ?? $array['DTE']['Liquidacion']
268 16
                ?? $array
269 103
            ;
270
271 103
            unset($array['TED'], $array['TmstFirma']);
272
273 103
            $arrayRequired = [
274 103
                'Encabezado.Totales.ImptoReten',
275 103
                'Detalle',
276 103
                'DscRcgGlobal',
277 103
                'Referencia',
278 103
            ];
279 103
            foreach ($arrayRequired as $path) {
280 103
                Arr::ensureArrayAtPath($array, $path);
281
            }
282
283 103
            $this->datos = Arr::autoCastRecursive($array);
284
        }
285
286
        // Entregar los datos del DTE.
287 103
        return $this->datos;
288
    }
289
290
    /**
291
     * {@inheritDoc}
292
     */
293 103
    public function getTED(): ?string
294
    {
295
        try {
296 103
            return $this->getXmlDocument()->C14NWithIsoEncodingFlattened('//TED');
297
        } catch (XmlException $e) {
298
            return null;
299
        }
300
    }
301
302
    /**
303
     * {@inheritDoc}
304
     */
305 103
    public function getPlantillaTED(): array
306
    {
307 103
        return [
308 103
            'TED' => [
309 103
                '@attributes' => [
310 103
                    'version' => '1.0',
311 103
                ],
312 103
                'DD' => [
313 103
                    'RE' => $this->getRutEmisor(),
314 103
                    'TD' => $this->getCodigo(),
315 103
                    'F' => $this->getFolio(),
316 103
                    'FE' => $this->getFechaEmision(),
317 103
                    'RR' => $this->getRutReceptor(),
318 103
                    'RSR' => mb_substr($this->getRazonSocialReceptor(), 0, 40),
319 103
                    'MNT' => $this->getMontoTotal(),
320 103
                    'IT1' => mb_substr(
321 103
                        $this->getDetalle(0)['NmbItem'] ?? '',
322 103
                        0,
323 103
                        40
324 103
                    ),
325 103
                    'CAF' => '', // Se deberá agregar al timbrar.
326 103
                    'TSTED' => '', // Se deberá agregar al timbrar.
327 103
                ],
328 103
                'FRMT' => [
329 103
                    '@attributes' => [
330 103
                        'algoritmo' => 'SHA1withRSA',
331 103
                    ],
332 103
                    '@value' => '', // Se deberá agregar al timbrar.
333 103
                ],
334 103
            ],
335 103
        ];
336
    }
337
338
    /**
339
     * {@inheritDoc}
340
     */
341
    public function get(string $selector): mixed
342
    {
343
        return Selector::get($this->getDatos(), $selector);
344
    }
345
346
    /**
347
     * {@inheritDoc}
348
     */
349
    public function query(string $query, array $params = []): string|array|null
350
    {
351
        return $this->xmlDocument->query($query, $params);
352
    }
353
}
354