Test Failed
Push — master ( 8dc6a9...a5e9ad )
by Esteban De La Fuente
06:42
created

AbstractDocument::getMoneda()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 5
ccs 0
cts 3
cp 0
crap 2
rs 10
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\Entity\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 63
    public function __construct(XmlInterface $xmlDocument)
71
    {
72 63
        $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 63
        $xmlDocumentTipoDTE = (int) $xmlDocument->query('//Encabezado/IdDoc/TipoDTE');
77 63
        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 54
    public function getXmlDocument(): XmlInterface
99
    {
100 54
        return $this->xmlDocument;
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 54
    public function saveXml(): string
107
    {
108 54
        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
            '%sT%dF%d',
126
            $this->getRutEmisor(),
127
            $this->getCodigo(),
128
            $this->getFolio()
129
        );
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 63
    public function getCodigo(): int
136
    {
137 63
        return $this->tipoDocumento->getCodigo();
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143 54
    public function getFolio(): int
144
    {
145 54
        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 63
    public function getEmisor(): array
160
    {
161 63
        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 54
    public function getRutEmisor(): string
168
    {
169 54
        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 63
    public function getReceptor(): array
176
    {
177 63
        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 54
    public function getRutReceptor(): string
184
    {
185 54
        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 54
    public function getRazonSocialReceptor(): string
192
    {
193 54
        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 54
    public function getFechaEmision(): string
200
    {
201 54
        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 58
    public function getMontoTotal(): int|float
216
    {
217 58
        $monto = (float) $this->xmlDocument->query('//Encabezado/Totales/MntTotal');
218
219
        // Verificar si el monto es equivalente a un entero.
220 58
        if (floor($monto) == $monto) {
221 58
            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 54
    public function getDetalle(?int $index = null): array
242
    {
243 54
        $detalle = $this->xmlDocument->query('//Detalle');
244 54
        if ($detalle === null) {
245
            return [];
246
        }
247
248 54
        if (!isset($detalle[0])) {
249 52
            $detalle = [$detalle];
250
        }
251
252 54
        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 54
    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 54
        if (!isset($this->datos)) {
263 54
            $array = $this->xmlDocument->query('/');
264
265 54
            $array = $array['DTE']['Documento']
266 54
                ?? $array['DTE']['Exportaciones']
267 54
                ?? $array['DTE']['Liquidacion']
268 9
                ?? $array
269 54
            ;
270
271 54
            unset($array['TED'], $array['TmstFirma']);
272
273 54
            $arrayRequired = [
274 54
                'Detalle',
275 54
                'Encabezado.Totales.ImptoReten',
276 54
            ];
277 54
            foreach ($arrayRequired as $path) {
278 54
                Arr::ensureArrayAtPath($array, $path);
279
            }
280
281 54
            $this->datos = Arr::autoCastRecursive($array);
282
        }
283
284
        // Entregar los datos del DTE.
285 54
        return $this->datos;
286
    }
287
288
    /**
289
     * {@inheritdoc}
290
     */
291 54
    public function getTED(): ?string
292
    {
293
        try {
294 54
            return $this->getXmlDocument()->C14NWithIsoEncodingFlattened('//TED');
295
        } catch (XmlException $e) {
296
            return null;
297
        }
298
    }
299
300
    /**
301
     * {@inheritdoc}
302
     */
303 54
    public function getPlantillaTED(): array
304
    {
305 54
        return [
306 54
            'TED' => [
307 54
                '@attributes' => [
308 54
                    'version' => '1.0',
309 54
                ],
310 54
                'DD' => [
311 54
                    'RE' => $this->getRutEmisor(),
312 54
                    'TD' => $this->getCodigo(),
313 54
                    'F' => $this->getFolio(),
314 54
                    'FE' => $this->getFechaEmision(),
315 54
                    'RR' => $this->getRutReceptor(),
316 54
                    'RSR' => mb_substr($this->getRazonSocialReceptor(), 0, 40),
317 54
                    'MNT' => $this->getMontoTotal(),
318 54
                    'IT1' => mb_substr(
319 54
                        $this->getDetalle(0)['NmbItem'] ?? '',
320 54
                        0,
321 54
                        40
322 54
                    ),
323 54
                    'CAF' => '', // Se deberá agregar al timbrar.
324 54
                    'TSTED' => '', // Se deberá agregar al timbrar.
325 54
                ],
326 54
                'FRMT' => [
327 54
                    '@attributes' => [
328 54
                        'algoritmo' => 'SHA1withRSA',
329 54
                    ],
330 54
                    '@value' => '', // Se deberá agregar al timbrar.
331 54
                ],
332 54
            ],
333 54
        ];
334
    }
335
336
    /**
337
     * {@inheritdoc}
338
     */
339
    public function get(string $selector): mixed
340
    {
341
        return Selector::get($this->getDatos(), $selector);
342
    }
343
344
    /**
345
     * {@inheritdoc}
346
     */
347
    public function query(string $query, array $params = []): string|array|null
348
    {
349
        return $this->xmlDocument->query($query, $params);
350
    }
351
}
352