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 |
11
|
|
|
* por la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, |
12
|
|
|
* o (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\Sii\Dte\Documento; |
26
|
|
|
|
27
|
|
|
use DateTime; |
28
|
|
|
use libredte\lib\Core\Service\ArrayDataProvider; |
29
|
|
|
use libredte\lib\Core\Service\DataProviderInterface; |
30
|
|
|
use libredte\lib\Core\Service\PathManager; |
31
|
|
|
use libredte\lib\Core\Signature\Certificate; |
32
|
|
|
use libredte\lib\Core\Signature\SignatureException; |
33
|
|
|
use libredte\lib\Core\Signature\SignatureGenerator; |
34
|
|
|
use libredte\lib\Core\Signature\XmlSignatureNode; |
35
|
|
|
use libredte\lib\Core\Sii\Contribuyente\Contribuyente; |
36
|
|
|
use libredte\lib\Core\Sii\Dte\AutorizacionFolio\Caf; |
37
|
|
|
use libredte\lib\Core\Sii\Dte\AutorizacionFolio\CafException; |
38
|
|
|
use libredte\lib\Core\Xml\XmlConverter; |
39
|
|
|
use libredte\lib\Core\Xml\XmlDocument; |
40
|
|
|
use libredte\lib\Core\Xml\XmlException; |
41
|
|
|
use libredte\lib\Core\Xml\XmlUtils; |
42
|
|
|
use libredte\lib\Core\Xml\XmlValidator; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Clase abstracta (base) de la representación de un documento. |
46
|
|
|
*/ |
47
|
|
|
abstract class AbstractDocumento |
48
|
|
|
{ |
49
|
|
|
/** |
50
|
|
|
* Código del tipo de documento tributario al que está asociada esta |
51
|
|
|
* instancia de un documento. |
52
|
|
|
*/ |
53
|
|
|
protected int $codigo; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Instancia del tipo de documento tributario, según el código, asociado a |
57
|
|
|
* esta instancia de un documento. |
58
|
|
|
* |
59
|
|
|
* @var DocumentoTipo |
60
|
|
|
*/ |
61
|
|
|
private DocumentoTipo $tipo; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Arreglo con los datos del documento tributario. |
65
|
|
|
* |
66
|
|
|
* Estos datos podrían o no haber sido normalizados. Sin embargo, si no |
67
|
|
|
* fueron normalizados, se espera que se hayan asignados según lo que el |
68
|
|
|
* SII requiere (o sea, como si se hubiesen "normalizado"). |
69
|
|
|
* |
70
|
|
|
* @var array |
71
|
|
|
*/ |
72
|
|
|
protected array $data; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Instancia del documento XML asociado a los datos. |
76
|
|
|
* |
77
|
|
|
* @var XmlDocument |
78
|
|
|
*/ |
79
|
|
|
protected XmlDocument $xmlDocument; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Contribuyente emisor del documento. |
83
|
|
|
* |
84
|
|
|
* Este objeto representa al contribuyente que emitió el documento. |
85
|
|
|
* |
86
|
|
|
* @var Contribuyente |
87
|
|
|
*/ |
88
|
|
|
private Contribuyente $emisor; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Contribuyente receptor del documento. |
92
|
|
|
* |
93
|
|
|
* Este objeto representa al contribuyente que recibió el documento. |
94
|
|
|
* |
95
|
|
|
* @var Contribuyente |
96
|
|
|
*/ |
97
|
|
|
private Contribuyente $receptor; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Proveedor de datos. |
101
|
|
|
* |
102
|
|
|
* @var DataProviderInterface |
103
|
|
|
*/ |
104
|
|
|
protected DataProviderInterface $dataProvider; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Constructor de la clase. |
108
|
|
|
* |
109
|
|
|
* @param DataProviderInterface|null $dataProvider Proveedor de datos. |
110
|
|
|
*/ |
111
|
116 |
|
public function __construct(?DataProviderInterface $dataProvider = null) |
112
|
|
|
{ |
113
|
116 |
|
$this->dataProvider = $dataProvider ?? new ArrayDataProvider(); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Entrega la instancia del tipo de documento asociado a este documento. |
118
|
|
|
* |
119
|
|
|
* @return DocumentoTipo |
120
|
|
|
*/ |
121
|
116 |
|
public function getTipo(): DocumentoTipo |
122
|
|
|
{ |
123
|
116 |
|
if (!isset($this->tipo)) { |
124
|
116 |
|
$this->tipo = new DocumentoTipo( |
125
|
116 |
|
$this->codigo, |
126
|
116 |
|
$this->dataProvider |
127
|
116 |
|
); |
128
|
|
|
} |
129
|
|
|
|
130
|
116 |
|
return $this->tipo; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Asigna los datos del documento. |
135
|
|
|
* |
136
|
|
|
* @param array $data |
137
|
|
|
* @return self |
138
|
|
|
*/ |
139
|
114 |
|
public function setData(array $data): self |
140
|
|
|
{ |
141
|
114 |
|
$this->data = $data; |
142
|
114 |
|
unset($this->xmlDocument); |
143
|
|
|
|
144
|
114 |
|
return $this; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Obtiene los datos del documento. |
149
|
|
|
* |
150
|
|
|
* @return array |
151
|
|
|
*/ |
152
|
114 |
|
public function getData(): array |
153
|
|
|
{ |
154
|
114 |
|
return $this->data; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Carga el XML completo de un documento para crear la instancia del |
159
|
|
|
* documento XML asociada a este documento. |
160
|
|
|
* |
161
|
|
|
* @param string $xml |
162
|
|
|
* @return self |
163
|
|
|
*/ |
164
|
2 |
|
public function loadXML(string $xml): self |
165
|
|
|
{ |
166
|
2 |
|
$this->xmlDocument = new XmlDocument(); |
167
|
2 |
|
$this->xmlDocument->loadXML($xml); |
168
|
|
|
|
169
|
2 |
|
return $this; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Entrega el string XML del documento XML. |
174
|
|
|
* |
175
|
|
|
* Es un wrapper de XmlDocument::getXML(); |
176
|
|
|
* |
177
|
|
|
* @return string |
178
|
|
|
*/ |
179
|
2 |
|
public function getXml(): string |
180
|
|
|
{ |
181
|
2 |
|
return $this->getXmlDocument()->getXML(); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Entrega el string XML del documento XML. |
186
|
|
|
* |
187
|
|
|
* Es un wrapper de XmlDocument::saveXML(); |
188
|
|
|
* |
189
|
|
|
* @return string |
190
|
|
|
*/ |
191
|
27 |
|
public function saveXml(): string |
192
|
|
|
{ |
193
|
27 |
|
return $this->getXmlDocument()->saveXML(); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Entrega el ID que LibreDTE asigna al documento. |
198
|
|
|
* |
199
|
|
|
* @return string |
200
|
|
|
*/ |
201
|
56 |
|
public function getId(): string |
202
|
|
|
{ |
203
|
56 |
|
return sprintf( |
204
|
56 |
|
'LibreDTE_T%dF%d', |
205
|
56 |
|
$this->getCodigo(), |
206
|
56 |
|
$this->getFolio() |
207
|
56 |
|
); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Entrega el código del tipo de documento tributario. |
212
|
|
|
* |
213
|
|
|
* @return int |
214
|
|
|
*/ |
215
|
56 |
|
public function getCodigo(): int |
216
|
|
|
{ |
217
|
56 |
|
return $this->codigo; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Entrega el folio del documento tributario. |
222
|
|
|
* |
223
|
|
|
* @return int |
224
|
|
|
*/ |
225
|
83 |
|
public function getFolio(): int |
226
|
|
|
{ |
227
|
83 |
|
$data = $this->getData(); |
228
|
|
|
|
229
|
83 |
|
return (int) $data['Encabezado']['IdDoc']['Folio']; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Entrega la fecha de emisión asignada al documento tributario. |
234
|
|
|
* |
235
|
|
|
* Esta es la fecha de emisión informada al SII del documento, no es la |
236
|
|
|
* fecha de creación real del documento en LibreDTE. |
237
|
|
|
* |
238
|
|
|
* @return string |
239
|
|
|
*/ |
240
|
56 |
|
public function getFechaEmision(): string |
241
|
|
|
{ |
242
|
56 |
|
$data = $this->getData(); |
243
|
|
|
|
244
|
56 |
|
return $data['Encabezado']['IdDoc']['FchEmis']; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Obtiene el contribuyente emisor del documento. |
249
|
|
|
* |
250
|
|
|
* @return Contribuyente Instancia de Contribuyente que representa al |
251
|
|
|
* emisor. |
252
|
|
|
*/ |
253
|
56 |
|
public function getEmisor(): Contribuyente |
254
|
|
|
{ |
255
|
56 |
|
if (!isset($this->emisor)) { |
256
|
56 |
|
$data = $this->getData(); |
257
|
|
|
|
258
|
56 |
|
$this->emisor = new Contribuyente( |
259
|
56 |
|
data: $data['Encabezado']['Emisor'], |
260
|
56 |
|
dataProvider: $this->dataProvider |
261
|
56 |
|
); |
262
|
|
|
} |
263
|
|
|
|
264
|
56 |
|
return $this->emisor; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Obtiene el contribuyente receptor del documento. |
269
|
|
|
* |
270
|
|
|
* @return Contribuyente Instancia de Contribuyente que representa al |
271
|
|
|
* receptor. |
272
|
|
|
*/ |
273
|
56 |
|
public function getReceptor(): Contribuyente |
274
|
|
|
{ |
275
|
56 |
|
if (!isset($this->receptor)) { |
276
|
56 |
|
$data = $this->getData(); |
277
|
|
|
|
278
|
56 |
|
$this->receptor = new Contribuyente( |
279
|
56 |
|
data: $data['Encabezado']['Receptor'], |
280
|
56 |
|
dataProvider: $this->dataProvider |
281
|
56 |
|
); |
282
|
|
|
} |
283
|
|
|
|
284
|
56 |
|
return $this->receptor; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Entrega todos los valores del tag "Totales". |
289
|
|
|
* |
290
|
|
|
* @return array |
291
|
|
|
*/ |
292
|
|
|
public function getTotales(): array |
293
|
|
|
{ |
294
|
|
|
$data = $this->getData(); |
295
|
|
|
|
296
|
|
|
return $data['Encabezado']['Totales']; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Entrega el monto total del documento. |
301
|
|
|
* |
302
|
|
|
* El monto estará en la moneda del documento. |
303
|
|
|
* |
304
|
|
|
* @return int|float Monto total del documento. |
305
|
|
|
*/ |
306
|
60 |
|
public function getMontoTotal(): int|float |
307
|
|
|
{ |
308
|
60 |
|
$data = $this->getData(); |
309
|
60 |
|
$monto = $data['Encabezado']['Totales']['MntTotal']; |
310
|
|
|
|
311
|
|
|
// Verificar si el monto es equivalente a un entero. |
312
|
60 |
|
if (is_float($monto) && floor($monto) == $monto) { |
313
|
9 |
|
return (int) $monto; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
// Entregar como flotante. |
317
|
52 |
|
return $monto; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Entrega el detalle del documento. |
322
|
|
|
* |
323
|
|
|
* Se puede solicitar todo el detalle o el detalle de una línea en |
324
|
|
|
* específico. |
325
|
|
|
* |
326
|
|
|
* @param integer|null $index Índice de la línea de detalle solicitada o |
327
|
|
|
* `null` (por defecto) para obtener todas las líneas. |
328
|
|
|
* @return array |
329
|
|
|
*/ |
330
|
56 |
|
public function getDetalle(?int $index = null): array |
331
|
|
|
{ |
332
|
56 |
|
$data = $this->getData(); |
333
|
|
|
|
334
|
56 |
|
return $index !== null |
335
|
56 |
|
? $data['Detalle'][$index] ?? [] |
336
|
56 |
|
: $data['Detalle'] ?? [] |
337
|
56 |
|
; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Obtiene la instancia del documento XML asociada al documento tributario. |
342
|
|
|
* |
343
|
|
|
* @return XmlDocument |
344
|
|
|
*/ |
345
|
57 |
|
public function getXmlDocument(): XmlDocument |
346
|
|
|
{ |
347
|
57 |
|
if (!isset($this->xmlDocument)) { |
348
|
56 |
|
$xmlDocumentData = [ |
349
|
56 |
|
'DTE' => [ |
350
|
56 |
|
'@attributes' => [ |
351
|
56 |
|
'version' => '1.0', |
352
|
56 |
|
'xmlns' => 'http://www.sii.cl/SiiDte', |
353
|
56 |
|
], |
354
|
56 |
|
$this->getTipo()->getTagXML() => array_merge([ |
355
|
56 |
|
'@attributes' => [ |
356
|
56 |
|
'ID' => $this->getId(), |
357
|
56 |
|
], |
358
|
56 |
|
], $this->getData()), |
359
|
56 |
|
], |
360
|
56 |
|
]; |
361
|
56 |
|
$this->xmlDocument = XmlConverter::arrayToXml($xmlDocumentData); |
|
|
|
|
362
|
|
|
} |
363
|
|
|
|
364
|
57 |
|
return $this->xmlDocument; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* Realiza el timbrado del documento. |
369
|
|
|
* |
370
|
|
|
* @param Caf $caf Instancia del CAF con el que se desea timbrar. |
371
|
|
|
* @param string $timestamp Marca de tiempo a utilizar en el timbre. |
372
|
|
|
* @throws CafException Si existe algún problema al timbrar el documento. |
373
|
|
|
*/ |
374
|
56 |
|
public function timbrar(Caf $caf, ?string $timestamp = null): void |
375
|
|
|
{ |
376
|
|
|
// Verificar que el folio del documento esté dentro del rango del CAF. |
377
|
56 |
|
if (!$caf->enRango($this->getFolio())) { |
378
|
|
|
throw new CafException(sprintf( |
379
|
|
|
'El folio %d del documento %s no está disponible en el rango del CAF %s.', |
380
|
|
|
$this->getFolio(), |
381
|
|
|
$this->getID(), |
382
|
|
|
$caf->getID() |
383
|
|
|
)); |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
// Asignar marca de tiempo si no se pasó una. |
387
|
56 |
|
if ($timestamp === null) { |
388
|
56 |
|
$timestamp = date('Y-m-d\TH:i:s'); |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
// Corroborar que el CAF esté vigente según el timestamp usado. |
392
|
56 |
|
if (!$caf->vigente($timestamp)) { |
393
|
|
|
throw new CafException(sprintf( |
394
|
|
|
'El CAF %s que contiene el folio %d del documento %s no está vigente, venció el día %s.', |
395
|
|
|
$caf->getID(), |
396
|
|
|
$this->getFolio(), |
397
|
|
|
$this->getID(), |
398
|
|
|
(new DateTime($caf->getFechaVencimiento()))->format('d/m/Y'), |
399
|
|
|
)); |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
// Preparar datos del timbre. |
403
|
56 |
|
$tedData = [ |
404
|
56 |
|
'TED' => [ |
405
|
56 |
|
'@attributes' => [ |
406
|
56 |
|
'version' => '1.0', |
407
|
56 |
|
], |
408
|
56 |
|
'DD' => [ |
409
|
56 |
|
'RE' => $this->getEmisor()->getRut(), |
410
|
56 |
|
'TD' => $this->getTipo()->getCodigo(), |
411
|
56 |
|
'F' => $this->getFolio(), |
412
|
56 |
|
'FE' => $this->getFechaEmision(), |
413
|
56 |
|
'RR' => $this->getReceptor()->getRut(), |
414
|
56 |
|
'RSR' => $this->getReceptor()->getRazonSocial(), |
415
|
56 |
|
'MNT' => $this->getMontoTotal(), |
416
|
56 |
|
'IT1' => $this->getDetalle(0)['NmbItem'] ?? '', |
417
|
56 |
|
'CAF' => $caf->getAutorizacion(), |
418
|
56 |
|
'TSTED' => $timestamp, |
419
|
56 |
|
], |
420
|
56 |
|
'FRMT' => [ |
421
|
56 |
|
'@attributes' => [ |
422
|
56 |
|
'algoritmo' => 'SHA1withRSA', |
423
|
56 |
|
], |
424
|
56 |
|
'@value' => '', // Se agregará luego. |
425
|
56 |
|
], |
426
|
56 |
|
], |
427
|
56 |
|
]; |
428
|
|
|
|
429
|
|
|
// Armar XML del timbre y obtener los datos a timbrar (tag DD: datos |
430
|
|
|
// del documento). |
431
|
56 |
|
$tedXmlDocument = XmlConverter::arrayToXml($tedData); |
432
|
56 |
|
$ddToStamp = $tedXmlDocument->C14NWithIsoEncodingFlattened('/TED/DD'); |
|
|
|
|
433
|
|
|
|
434
|
|
|
// Timbrar los "datos a timbrar" $ddToStamp. |
435
|
56 |
|
$timbre = $caf->timbrar($ddToStamp); |
436
|
56 |
|
$tedData['TED']['FRMT']['@value'] = $timbre; |
437
|
|
|
|
438
|
|
|
// Actualizar los datos del documento incorporando el timbre calculado. |
439
|
56 |
|
$newData = array_merge($this->getData(), $tedData); |
440
|
56 |
|
$this->setData($newData); |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* Realiza la firma del documento. |
445
|
|
|
* |
446
|
|
|
* @param Certificate $certificate Instancia que representa la firma |
447
|
|
|
* electrónica. |
448
|
|
|
* @param string $timestamp Marca de tiempo a utilizar en la firma. |
449
|
|
|
* @return string String con el XML firmado. |
450
|
|
|
* @throws SignatureException Si existe algún problema al firmar el documento. |
451
|
|
|
*/ |
452
|
29 |
|
public function firmar(Certificate $certificate, ?string $timestamp = null): string |
453
|
|
|
{ |
454
|
|
|
// Asignar marca de tiempo si no se pasó una. |
455
|
29 |
|
if ($timestamp === null) { |
456
|
29 |
|
$timestamp = date('Y-m-d\TH:i:s'); |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
// Corroborar que el certificado esté vigente según el timestamp usado. |
460
|
29 |
|
if (!$certificate->isActive($timestamp)) { |
461
|
|
|
throw new SignatureException(sprintf( |
462
|
|
|
'El certificado digital de %s no está vigente en el tiempo %s, su rango de vigencia es del %s al %s.', |
463
|
|
|
$certificate->getID(), |
464
|
|
|
(new DateTime($timestamp))->format('d/m/Y H:i'), |
465
|
|
|
(new DateTime($certificate->getFrom()))->format('d/m/Y H:i'), |
466
|
|
|
(new DateTime($certificate->getTo()))->format('d/m/Y H:i'), |
467
|
|
|
)); |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
// Agregar timestamp. |
471
|
29 |
|
$newData = array_merge($this->getData(), ['TmstFirma' => $timestamp]); |
472
|
29 |
|
$this->setData($newData); |
473
|
|
|
|
474
|
|
|
// Firmar el tag que contiene el documento y retornar el XML firmado. |
475
|
29 |
|
$xmlSigned = SignatureGenerator::signXml( |
476
|
29 |
|
$this->getXmlDocument(), |
477
|
29 |
|
$certificate, |
478
|
29 |
|
$this->getId() |
479
|
29 |
|
); |
480
|
|
|
|
481
|
|
|
// Cargar XML en el documento. |
482
|
29 |
|
$this->xmlDocument->loadXML($xmlSigned); |
483
|
|
|
|
484
|
|
|
// Entregar XML firmado. |
485
|
29 |
|
return $xmlSigned; |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* Obtiene una instancia del nodo de la firma. |
490
|
|
|
* |
491
|
|
|
* @return XmlSignatureNode |
492
|
|
|
* @throws SignatureException Si el documento XML no está firmado. |
493
|
|
|
*/ |
494
|
29 |
|
public function getXmlSignatureNode(): XmlSignatureNode |
495
|
|
|
{ |
496
|
29 |
|
$tag = $this->getXmlDocument()->documentElement->tagName; |
497
|
29 |
|
$xpath = '/*[local-name()="' . $tag . '"]/*[local-name()="Signature"]'; |
498
|
29 |
|
$signatureElement = XmlUtils::xpath($this->getXmlDocument(), $xpath)->item(0); |
499
|
29 |
|
if ($signatureElement === null) { |
500
|
|
|
throw new SignatureException('El documento XML del DTE no se encuentra firmado (no se encontró el nodo "Signature").'); |
501
|
|
|
} |
502
|
|
|
|
503
|
29 |
|
$xmlSignatureNode = new XmlSignatureNode(); |
504
|
29 |
|
$xmlSignatureNode->loadXML($signatureElement->C14N()); |
505
|
|
|
|
506
|
29 |
|
return $xmlSignatureNode; |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
/** |
510
|
|
|
* Valida la firma electrónica del documento XML del DTE. |
511
|
|
|
* |
512
|
|
|
* @return void |
513
|
|
|
* @throws SignatureException Si la validación del esquema falla. |
514
|
|
|
*/ |
515
|
29 |
|
public function validateSignature() |
516
|
|
|
{ |
517
|
29 |
|
$xmlSignatureNode = $this->getXmlSignatureNode(); |
518
|
29 |
|
$xmlSignatureNode->validate($this->getXmlDocument()); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
/** |
522
|
|
|
* Valida el esquema del XML del DTE. |
523
|
|
|
* |
524
|
|
|
* @return void |
525
|
|
|
* @throws XmlException Si la validación del esquema falla. |
526
|
|
|
*/ |
527
|
30 |
|
public function validateSchema(): void |
528
|
|
|
{ |
529
|
|
|
// Las boletas no se validan de manera individual (el DTE). Se validan |
530
|
|
|
// a través del EnvioBOLETA. |
531
|
30 |
|
if ($this->getTipo()->esBoleta()) { |
532
|
5 |
|
return; |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
// Validar esquema de otros DTE (no boletas). |
536
|
25 |
|
$schema = 'DTE_v10.xsd'; |
537
|
25 |
|
$schemaPath = PathManager::getSchemasPath($schema); |
538
|
25 |
|
XmlValidator::validateSchema($this->getXmlDocument(), $schemaPath); |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.