This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file is part of the CFDI Wrapper library. |
||
5 | * |
||
6 | * @copyright 2015 César Antáres <[email protected]> |
||
7 | * @license http://opensource.org/licenses/MIT The MIT License. |
||
8 | */ |
||
9 | |||
10 | namespace ZzAntares\CfdiWrapper; |
||
11 | |||
12 | use ZzAntares\CfdiWrapper\Exceptions\UndefinedAttributeException; |
||
13 | use ZzAntares\CfdiWrapper\Exceptions\MalformedCfdiException; |
||
14 | |||
15 | class Cfdi |
||
16 | { |
||
17 | /** |
||
18 | * Holds the instance used by all the methods to acces to the properties of |
||
19 | * the CFDI XSD. |
||
20 | * |
||
21 | * @var \SimpleXMLElement |
||
22 | */ |
||
23 | private $cfdi; |
||
24 | |||
25 | /** |
||
26 | * Contains a map between CFDI XSD paths and an easier to read dotted |
||
27 | * representation of the paths. |
||
28 | * |
||
29 | * @var array |
||
30 | */ |
||
31 | private $paths = [ |
||
32 | 'cfdi' => '//cfdi:Comprobante', |
||
33 | 'cfdi.issuing' => '//cfdi:Comprobante//cfdi:Emisor', |
||
34 | 'cfdi.issuing.address' => '//cfdi:Comprobante//cfdi:Emisor//cfdi:DomicilioFiscal', |
||
35 | 'cfdi.issuing.issued_at' => '//cfdi:Comprobante//cfdi:Emisor//cfdi:ExpedidoEn', |
||
36 | 'cfdi.issuing.regimen' => '//cfdi:Comprobante//cfdi:Emisor//cfdi:RegimenFiscal', |
||
37 | 'cfdi.receiver' => '//cfdi:Comprobante//cfdi:Receptor', |
||
38 | 'cfdi.receiver.address' => '//cfdi:Comprobante//cfdi:Receptor//cfdi:Domicilio', |
||
39 | 'cfdi.items' => '//cfdi:Comprobante//cfdi:Conceptos//cfdi:Concepto', |
||
40 | 'cfdi.taxes' => '//cfdi:Comprobante//cfdi:Impuestos', |
||
41 | 'cfdi.taxes.holdbacks' => '//cfdi:Comprobante//cfdi:Impuestos//cfdi:Retenciones//cfdi:Retencion', |
||
42 | 'cfdi.taxes.transfers' => '//cfdi:Comprobante//cfdi:Impuestos//cfdi:Traslados//cfdi:Traslado', |
||
43 | 'cfdi.addon.taxes' => '//cfdi:Comprobante//cfdi:Complemento//implocal:ImpuestosLocales', |
||
44 | // @codingStandardsIgnoreLine |
||
45 | 'cfdi.addon.taxes.holdbacks' => '//cfdi:Comprobante//cfdi:Complemento//implocal:ImpuestosLocales//implocal:RetencionesLocales', |
||
46 | ]; |
||
47 | |||
48 | /** |
||
49 | * Contains the allowed attributes on the CFDI Wrapper instance. |
||
50 | * |
||
51 | * @var array |
||
52 | */ |
||
53 | private $allowedAttributes = [ |
||
54 | 'version', |
||
55 | 'serie', |
||
56 | 'folio', |
||
57 | 'fecha', |
||
58 | 'subTotal', |
||
59 | 'subtotal', // Alias of subTotal |
||
60 | 'total', |
||
61 | 'certificado' , |
||
62 | 'noCertificado', |
||
63 | 'condicionesDePago', |
||
64 | 'descuento', |
||
65 | 'motivoDescuento', |
||
66 | 'TipoCambio', |
||
67 | 'tipoCambio', // Alias of TipoCambio |
||
68 | 'Moneda', |
||
69 | 'moneda', // Alias of Moneda |
||
70 | 'metodoDePago', |
||
71 | 'sello' , |
||
72 | 'tipoDeComprobante', |
||
73 | 'formaDePago', |
||
74 | 'LugarExpedicion', |
||
75 | 'lugarExpedicion', // Alias of LugarExpedcion |
||
76 | 'NumCtaPago', |
||
77 | 'numCtaPago', // Alias of numCtaPago |
||
78 | 'cadenaOriginal', // Dinamically generated |
||
79 | 'leyenda', // Dinamically generated |
||
80 | 'iva', // Dinamically generated |
||
81 | ]; |
||
82 | |||
83 | /** |
||
84 | * List the nested objects vailable on the CFDI Wrapper instance. |
||
85 | * |
||
86 | * @var array |
||
87 | */ |
||
88 | private $nestedObjects = [ |
||
89 | 'emisor', |
||
90 | 'receptor', |
||
91 | 'conceptos', |
||
92 | 'impuestos', |
||
93 | 'impuestosLocales', |
||
94 | 'timbre', |
||
95 | 'timbreFiscalDigital', |
||
96 | ]; |
||
97 | |||
98 | |||
99 | /** |
||
100 | * Constructor. |
||
101 | * |
||
102 | * @return void |
||
103 | */ |
||
104 | 22 | public function __construct($pathOrContent) |
|
105 | { |
||
106 | 22 | if (file_exists($pathOrContent)) { |
|
107 | 3 | $this->loadFromFile($pathOrContent); |
|
108 | 2 | } else { |
|
109 | 22 | $this->load($pathOrContent); |
|
110 | } |
||
111 | 22 | } |
|
112 | |||
113 | /** |
||
114 | * Allows to change the loaded CFDI by specifying a new string whose content |
||
115 | * is the new CDFI to set. |
||
116 | * |
||
117 | * @param string $xmlContent |
||
118 | * |
||
119 | * @return bool 'true' if load was successful, 'false' otherwise. |
||
120 | */ |
||
121 | 22 | public function load($xmlContent) |
|
122 | { |
||
123 | 22 | $this->xmlContent = $this->sanitizeXML($xmlContent); |
|
124 | 22 | $cfdi = simplexml_load_string($this->xmlContent); |
|
125 | 22 | $this->cfdi = $cfdi; |
|
126 | |||
127 | 22 | return $this->isValid(true); |
|
128 | } |
||
129 | |||
130 | /** |
||
131 | * Allows to change the loaded CFDI by specifying the file path. |
||
132 | * |
||
133 | * @param string $path |
||
134 | * |
||
135 | * @return bool 'true' if load was successful, 'false' otherwise. |
||
136 | */ |
||
137 | 3 | public function loadFromFile($path) |
|
138 | { |
||
139 | 3 | return $this->load(file_get_contents($path)); |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * Saves the XML representation of the CFDI into a file. |
||
144 | * |
||
145 | * @throws RuntimeException If Overwritte can't happen and file exists. |
||
146 | * |
||
147 | * @param string $filepath Full path and filename where to save the CFDI. |
||
148 | * @param bool $overwrite If the given file path exists and this is set to |
||
149 | * 'true' then file will be overwritten, otherwise an exception |
||
150 | * will be thrown. |
||
151 | * |
||
152 | * @return return integer Bytes written to file or 'false' if writting |
||
153 | * wasn't possible. |
||
154 | */ |
||
155 | 3 | public function toFile($filepath, $overwrite = false) |
|
156 | { |
||
157 | 3 | if (file_exists($filepath) and !$overwrite) { |
|
158 | 1 | throw new \RuntimeException($filepath . ' already exists.'); |
|
159 | } |
||
160 | |||
161 | 2 | return file_put_contents($filepath, $this->xmlContent); |
|
162 | } |
||
163 | |||
164 | |||
165 | /** |
||
166 | * Checks if the given CFDI is valid by searching the needed namespaces. |
||
167 | * |
||
168 | * @throws ZzAntares\CfdiWrapper\Exceptions\MalformedCfdiException |
||
169 | * |
||
170 | * @param SimpleXMLElement $cfdi |
||
171 | * |
||
172 | * @return void |
||
173 | */ |
||
174 | 22 | public function isValid($throwException = false) |
|
175 | { |
||
176 | 22 | $namespaces = array_keys($this->cfdi->getNamespaces(true)); |
|
177 | |||
178 | 22 | if (in_array_all(['tfd', 'xsi', 'cfdi', 'implocal'], $namespaces)) { |
|
179 | 22 | return true; |
|
180 | } |
||
181 | |||
182 | 1 | if ($throwException) { |
|
183 | 1 | throw new MalformedCfdiException(); |
|
184 | } |
||
185 | |||
186 | return false; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Gets the specified attribute. |
||
191 | * |
||
192 | * @param string $attribute Property on the CFDI to retrieve. |
||
193 | * |
||
194 | * @return void |
||
195 | */ |
||
196 | 8 | private function getAttribute($attribute) |
|
197 | { |
||
198 | 8 | $comprobante = $this->cfdi->xpath($this->paths['cfdi']); |
|
199 | |||
200 | switch ($attribute) { |
||
201 | 8 | case 'subtotal': |
|
202 | 1 | $attribute = 'subTotal'; |
|
203 | 1 | break; |
|
204 | |||
205 | 8 | case 'tipoCambio': |
|
206 | 1 | $attribute = 'TipoCambio'; |
|
207 | 1 | break; |
|
208 | |||
209 | 8 | case 'moneda': |
|
210 | 1 | $attribute = 'Moneda'; |
|
211 | 1 | break; |
|
212 | |||
213 | 8 | case 'lugarExpedicion': |
|
214 | 1 | $attribute = 'LugarExpedicion'; |
|
215 | 1 | break; |
|
216 | |||
217 | 8 | case 'numCtaPago': |
|
218 | 1 | $attribute = 'NumCtaPago'; |
|
219 | 1 | break; |
|
220 | |||
221 | 8 | case 'cadenaOriginal': |
|
222 | 1 | return $this->getCadenaOriginal(); |
|
223 | break; |
||
224 | |||
225 | 7 | case 'leyenda': |
|
226 | 1 | return 'Este documento es una representación impresa de un CFDI'; |
|
227 | break; |
||
228 | |||
229 | 6 | case 'iva': |
|
230 | 1 | return $this->getImpuesto('iva'); |
|
231 | break; |
||
232 | } |
||
233 | |||
234 | 5 | return $comprobante[0][$attribute]->__toString(); |
|
235 | } |
||
236 | |||
237 | /** |
||
238 | * Retrieves the propper nested object under the CFDI. |
||
239 | * |
||
240 | * @return object |
||
241 | */ |
||
242 | 11 | private function getNestedObject($attribute) |
|
243 | { |
||
244 | switch ($attribute) { |
||
245 | 11 | case 'emisor': |
|
246 | 4 | return $this->getEmisor(); |
|
247 | break; |
||
248 | |||
249 | 10 | case 'receptor': |
|
250 | 4 | return $this->getReceptor(); |
|
251 | break; |
||
252 | |||
253 | 9 | case 'conceptos': |
|
254 | 1 | return $this->getConceptos(); |
|
255 | break; |
||
256 | |||
257 | 8 | case 'impuestos': |
|
258 | 2 | return $this->getImpuestos(); |
|
259 | break; |
||
260 | |||
261 | 6 | case 'impuestosLocales': |
|
262 | 1 | return $this->getImpuestosLocales(); |
|
263 | break; |
||
264 | |||
265 | 5 | case 'timbre': |
|
266 | 5 | case 'timbreFiscalDigital': |
|
267 | 5 | return $this->getTimbreFiscalDigital(); |
|
268 | break; |
||
269 | } |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Magic method to get attributes on the CFDI. |
||
274 | * |
||
275 | * @throws UndefinedAttributeException when attribute is not in the XML. |
||
276 | * |
||
277 | * @param string $attribute |
||
278 | * |
||
279 | * @return mixed A string or a nested object. |
||
280 | */ |
||
281 | 15 | public function __get($attribute) |
|
282 | { |
||
283 | 15 | if (in_array($attribute, $this->allowedAttributes)) { |
|
284 | 8 | return $this->getAttribute($attribute); |
|
285 | } |
||
286 | |||
287 | 12 | if (in_array($attribute, $this->nestedObjects)) { |
|
288 | 11 | return $this->getNestedObject($attribute); |
|
289 | } |
||
290 | |||
291 | 1 | throw new UndefinedAttributeException(); |
|
292 | } |
||
293 | |||
294 | /** |
||
295 | * Retrieves the string representation of the CFDI. |
||
296 | * |
||
297 | * @return string |
||
298 | */ |
||
299 | 3 | public function __toString() |
|
300 | { |
||
301 | 3 | return $this->sanitizeXML($this->xmlContent); |
|
302 | } |
||
303 | |||
304 | /** |
||
305 | * Removes new lines and spaces from the XML string representation. |
||
306 | * |
||
307 | * @param string $xmlString XML string. |
||
308 | * @return string |
||
309 | */ |
||
310 | 22 | public function sanitizeXML($xmlString) |
|
311 | { |
||
312 | 22 | $xmlHeader = '<?xml version="1.0" encoding="UTF-8"?>'; |
|
313 | |||
314 | 22 | $data = str_replace($xmlHeader, '', $xmlString); |
|
315 | 22 | $data = str_replace("\n", '', $data); |
|
316 | 22 | $data = str_replace("\r", '', $data); |
|
317 | 22 | $data = preg_replace('~\s*(<([^>]*)>[^<]*</\2>|<[^>]*>)\s*~', '$1', $data); |
|
318 | 22 | $data = preg_replace('/\r\n/', "\n", $data); |
|
319 | |||
320 | 22 | return $xmlHeader . PHP_EOL . $data; |
|
321 | } |
||
322 | |||
323 | /** |
||
324 | * Retrieves the 'cfdi.addon.digital_stamp' path in the CFDI. |
||
325 | * |
||
326 | * @return object |
||
327 | */ |
||
328 | 5 | private function getTimbreFiscalDigital() |
|
329 | { |
||
330 | // $stamp = $this->cfdi->xpath($this->paths['cfdi.addon.digital_stamp']); |
||
331 | |||
332 | 5 | $ns = $this->cfdi->getNamespaces(true); |
|
333 | 5 | $this->cfdi->registerXPathNamespace('tfd', $ns['tfd']); |
|
334 | 5 | $stamp = $this->cfdi->xpath('//tfd:TimbreFiscalDigital')[0]; |
|
335 | |||
336 | return (object) [ |
||
337 | 5 | 'version' => $stamp['version']->__toString(), |
|
338 | 5 | 'uuid' => $stamp['UUID']->__toString(), |
|
339 | 5 | 'UUID' => $stamp['UUID']->__toString(), |
|
340 | 5 | 'fecha' => $stamp['FechaTimbrado']->__toString(), |
|
341 | 5 | 'fechaTimbrado' => $stamp['FechaTimbrado']->__toString(), |
|
342 | 5 | 'selloCFD' => $stamp['selloCFD']->__toString(), |
|
343 | 5 | 'cfd' => $stamp['selloCFD']->__toString(), |
|
344 | 5 | 'noCertificadoSAT' => $stamp['noCertificadoSAT']->__toString(), |
|
345 | 5 | 'selloSAT' => $stamp['selloSAT']->__toString(), |
|
346 | 5 | 'sat' => $stamp['selloSAT']->__toString(), |
|
347 | 5 | ]; |
|
348 | } |
||
349 | |||
350 | /** |
||
351 | * Retrieves the 'cfdi.addon.taxes' path in the CFDI. |
||
352 | * |
||
353 | * @return object |
||
354 | */ |
||
355 | 1 | private function getImpuestosLocales() |
|
356 | { |
||
357 | 1 | $addon = $this->cfdi->xpath($this->paths['cfdi.addon.taxes'])[0]; |
|
358 | |||
359 | return (object) [ |
||
360 | 1 | 'version' => $addon['version'], |
|
361 | 1 | 'totalDeRetenciones' => $addon['TotaldeRetenciones'], |
|
362 | 1 | 'totaldeRetenciones' => $addon['TotaldeRetenciones'], |
|
363 | 1 | 'retenciones' => $addon['TotaldeRetenciones'], |
|
364 | 1 | 'totalDeTraslados' => $addon['TotaldeTraslados'], |
|
365 | 1 | 'totaldeTraslados' => $addon['TotaldeTraslados'], |
|
366 | 1 | 'traslados' => $addon['TotaldeTraslados'], |
|
367 | 1 | 'retencionesLocales' => $this->getRetencionesLocales(), |
|
368 | 1 | ]; |
|
369 | } |
||
370 | |||
371 | /** |
||
372 | * Retrieves the 'cfdi.addon.taxes.holdbacks' path in the CFDI. |
||
373 | * |
||
374 | * @return object |
||
375 | */ |
||
376 | 1 | private function getRetencionesLocales() |
|
377 | { |
||
378 | 1 | $holdbacks = $this->cfdi->xpath($this->paths['cfdi.addon.taxes.holdbacks'])[0]; |
|
379 | |||
380 | return (object) [ |
||
381 | 1 | 'impuesto' => $holdbacks['ImpLocRetenido']->__toString(), |
|
382 | 1 | 'importe' => $holdbacks['Importe']->__toString(), |
|
383 | 1 | 'tasa' => $holdbacks['TasadeRetencion']->__toString(), |
|
384 | 1 | ]; |
|
385 | } |
||
386 | |||
387 | /** |
||
388 | * Retrieves all the 'cfdi.taxes' with it's nested objects like |
||
389 | * 'retenciones' and 'traslados'. |
||
390 | * |
||
391 | * @return object |
||
392 | */ |
||
393 | 2 | private function getImpuestos() |
|
394 | { |
||
395 | 2 | $impuestos = $this->cfdi->xpath($this->paths['cfdi.taxes'])[0]; |
|
396 | |||
397 | return (object) [ |
||
398 | 2 | 'totalImpuestosTrasladados' => $impuestos['totalImpuestosTrasladados']->__toString(), |
|
399 | 2 | 'totalImpuestosRetenidos' => $impuestos['totalImpuestosRetenidos']->__toString(), |
|
400 | 2 | 'retenciones' => $this->getRetenciones(), |
|
401 | 2 | 'traslados' => $this->getTraslados(), |
|
402 | 2 | ]; |
|
403 | } |
||
404 | |||
405 | /** |
||
406 | * Retrieves all 'cfdi.taxes.transfers' in the CFDI. |
||
407 | * |
||
408 | * @return array |
||
409 | */ |
||
410 | 2 | View Code Duplication | private function getTraslados() |
0 ignored issues
–
show
|
|||
411 | { |
||
412 | 2 | $transfers = $this->cfdi->xpath($this->paths['cfdi.taxes.transfers']); |
|
413 | |||
414 | 2 | $traslados = []; |
|
415 | 2 | foreach ($transfers as $transfer) { |
|
416 | $traslado = [ |
||
417 | 2 | 'impuesto' => $transfer['impuesto']->__toString(), |
|
418 | 2 | 'importe' => $transfer['importe']->__toString(), |
|
419 | 2 | 'tasa' => $transfer['tasa']->__toString(), |
|
420 | 2 | ]; |
|
421 | |||
422 | 2 | $traslados[] = (object) $traslado; |
|
423 | 2 | } |
|
424 | |||
425 | 2 | return $traslados; |
|
426 | } |
||
427 | |||
428 | /** |
||
429 | * Retrieves the path 'cfdi.taxes.holdbacks'. |
||
430 | * |
||
431 | * @return array |
||
432 | */ |
||
433 | 2 | View Code Duplication | private function getRetenciones() |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
434 | { |
||
435 | 2 | $holdbacks = $this->cfdi->xpath($this->paths['cfdi.taxes.holdbacks']); |
|
436 | |||
437 | 2 | $retenciones = []; |
|
438 | 2 | foreach ($holdbacks as $holdback) { |
|
439 | $retencion = [ |
||
440 | 2 | 'impuesto' => $holdback['impuesto']->__toString(), |
|
441 | 2 | 'importe' => $holdback['importe']->__toString(), |
|
442 | 2 | ]; |
|
443 | |||
444 | 2 | $retenciones[] = (object) $retencion; |
|
445 | 2 | } |
|
446 | |||
447 | 2 | return $retenciones; |
|
448 | } |
||
449 | |||
450 | /** |
||
451 | * Retrieves all 'cfdi.items' in the CFDI. |
||
452 | * |
||
453 | * @return array |
||
454 | */ |
||
455 | 1 | private function getConceptos() |
|
456 | { |
||
457 | 1 | $items = $this->cfdi->xpath($this->paths['cfdi.items']); |
|
458 | 1 | $conceptos = []; |
|
459 | 1 | foreach ($items as $item) { |
|
460 | $concepto = [ |
||
461 | 1 | 'cantidad' => $item['cantidad']->__toString(), |
|
462 | 1 | 'unidad' => $item['unidad']->__toString(), |
|
463 | 1 | 'descripcion' => $item['descripcion']->__toString(), |
|
464 | 1 | 'valorUnitario' => $item['valorUnitario']->__toString(), |
|
465 | 1 | 'importe' => $item['importe']->__toString(), |
|
466 | 1 | ]; |
|
467 | |||
468 | 1 | $conceptos[] = (object) $concepto; |
|
469 | 1 | } |
|
470 | |||
471 | 1 | return $conceptos; |
|
472 | } |
||
473 | |||
474 | /** |
||
475 | * Retrieves the 'cfdi.receiver' path, including it's nested resources like |
||
476 | * 'domicilio'. |
||
477 | * |
||
478 | * @return object |
||
479 | */ |
||
480 | 4 | private function getReceptor() |
|
481 | { |
||
482 | 4 | $comprobante = $this->cfdi->xpath($this->paths['cfdi.receiver']); |
|
483 | 4 | $domicilio = $this->getDomicilioFiscal('cfdi.receiver.address'); |
|
484 | |||
485 | $receptor = [ |
||
486 | 4 | 'rfc' => $comprobante[0]['rfc']->__toString(), |
|
487 | 4 | 'nombre' => $comprobante[0]['nombre']->__toString(), |
|
488 | 4 | 'domicilioFiscal' => $domicilio, |
|
489 | 4 | 'domicilio' => $domicilio, |
|
490 | 4 | ]; |
|
491 | |||
492 | 4 | return (object) $receptor; |
|
493 | } |
||
494 | |||
495 | /** |
||
496 | * Retrieves the 'cfdi.issuing' path and all it's nested resources like |
||
497 | * 'comprobante', 'domicilio', 'regimen', etc. |
||
498 | * |
||
499 | * @return object |
||
500 | */ |
||
501 | 4 | private function getEmisor() |
|
502 | { |
||
503 | 4 | $comprobante = $this->cfdi->xpath($this->paths['cfdi.issuing']); |
|
504 | 4 | $domicilio = $this->getDomicilioFiscal('cfdi.issuing.address'); |
|
505 | 4 | $regimenFiscal = $this->getRegimenFiscal(); |
|
506 | |||
507 | $emisor = [ |
||
508 | 4 | 'rfc' => $comprobante[0]['rfc']->__toString(), |
|
509 | 4 | 'nombre' => $comprobante[0]['nombre']->__toString(), |
|
510 | 4 | 'domicilioFiscal' => $domicilio, |
|
511 | 4 | 'domicilio' => $domicilio, |
|
512 | 4 | 'expedidoEn' => $this->getExpedidoEn(), |
|
513 | 4 | 'regimenFiscal' => $regimenFiscal, |
|
514 | 4 | 'regimen' => $regimenFiscal, |
|
515 | 4 | ]; |
|
516 | |||
517 | 4 | return (object) $emisor; |
|
518 | } |
||
519 | |||
520 | /** |
||
521 | * Get the nested object domicilioFiscal for the specified path, this can be |
||
522 | * either 'cfdi.issuing.address' or 'cfdi.receiver.address'. |
||
523 | * |
||
524 | * @param string $path 'cfdi.receiver.address' or 'cfdi.issuing.address'. |
||
525 | * |
||
526 | * @return object |
||
527 | */ |
||
528 | 5 | private function getDomicilioFiscal($path) |
|
529 | { |
||
530 | 5 | $address = $this->cfdi->xpath($this->paths[$path])[0]; |
|
531 | |||
532 | $domicilio = [ |
||
533 | 5 | 'calle' => $address['calle']->__toString(), |
|
534 | 5 | 'colonia' => $address['colonia']->__toString(), |
|
535 | 5 | 'localidad' => '', |
|
536 | 5 | 'municipio' => $address['municipio']->__toString(), |
|
537 | 5 | 'noExterior' => $address['noExterior']->__toString(), |
|
538 | 5 | 'noInterior' => '', |
|
539 | 5 | 'estado' => $address['estado']->__toString(), |
|
540 | 5 | 'pais' => $address['pais']->__toString(), |
|
541 | 5 | 'codigoPostal' => $address['codigoPostal']->__toString(), |
|
542 | 5 | ]; |
|
543 | |||
544 | 5 | if (isset($address['localidad'])) { |
|
545 | 5 | $domicilio['localidad'] = $address['localidad']->__toString(); |
|
546 | 5 | } |
|
547 | |||
548 | 5 | if (isset($address['noInterior'])) { |
|
549 | 4 | $domicilio['noInterior'] = $address['noInterior']->__toString(); |
|
550 | 4 | } |
|
551 | |||
552 | 5 | return (object) $domicilio; |
|
553 | } |
||
554 | |||
555 | /** |
||
556 | * Retrieves the info located at 'cfdi.issuing.issued_at'. |
||
557 | * |
||
558 | * @return object |
||
559 | */ |
||
560 | 4 | private function getExpedidoEn() |
|
561 | { |
||
562 | 4 | $expedidoEn = $this->cfdi->xpath($this->paths['cfdi.issuing.issued_at']); |
|
563 | |||
564 | return (object) [ |
||
565 | 4 | 'pais' => $expedidoEn[0]['pais']->__toString(), |
|
566 | 4 | ]; |
|
567 | } |
||
568 | |||
569 | /** |
||
570 | * Retrieves the info located at 'cfdi.issuing.regimen'. |
||
571 | * |
||
572 | * @return object |
||
573 | */ |
||
574 | 4 | private function getRegimenFiscal() |
|
575 | { |
||
576 | 4 | $regimenFiscal = $this->cfdi->xpath($this->paths['cfdi.issuing.regimen']); |
|
577 | |||
578 | return (object) [ |
||
579 | 4 | 'regimen' => $regimenFiscal[0]['Regimen']->__toString(), |
|
580 | 4 | 'Regimen' => $regimenFiscal[0]['Regimen']->__toString(), |
|
581 | 4 | ]; |
|
582 | } |
||
583 | |||
584 | /** |
||
585 | * Retrieves the field known as 'Cadena original de complemento de |
||
586 | * certificación del SAT'. |
||
587 | * |
||
588 | * @return string |
||
589 | */ |
||
590 | 1 | private function getCadenaOriginal() |
|
591 | { |
||
592 | 1 | return sprintf( |
|
593 | 1 | '||%s|%s|%s|%s|%s||', |
|
594 | 1 | $this->timbre->version, |
|
595 | 1 | $this->timbre->uuid, |
|
596 | 1 | $this->timbre->fechaTimbrado, |
|
597 | 1 | $this->timbre->selloCFD, |
|
598 | 1 | $this->timbre->noCertificadoSAT |
|
599 | 1 | ); |
|
600 | } |
||
601 | |||
602 | /** |
||
603 | * Gives a stdClass object with information of the given tax like 'tasa', |
||
604 | * 'importe', etc. |
||
605 | * |
||
606 | * @throws UndefinedAttributeException When tax is not found in the CFDI. |
||
607 | * |
||
608 | * @param $taxName Which tax to retrieve, this could be 'iva', 'ieps', etc. |
||
609 | * |
||
610 | * @return object |
||
611 | */ |
||
612 | 1 | private function getImpuesto($taxName) |
|
613 | { |
||
614 | // Only 'iva' is supported for now |
||
615 | 1 | if ($taxName != 'iva') { |
|
616 | throw new UndefinedAttributeException(); |
||
617 | } |
||
618 | |||
619 | 1 | $taxName = strtoupper($taxName); |
|
620 | |||
621 | 1 | foreach ($this->impuestos->traslados as $tax) { |
|
622 | 1 | if ($tax->impuesto == $taxName) { |
|
623 | 1 | return $tax; |
|
624 | } |
||
625 | } |
||
626 | |||
627 | throw new UndefinedAttributeException(); |
||
628 | } |
||
629 | |||
630 | /** |
||
631 | * Gets the Qr string, this is the string that should be read when scaning |
||
632 | * the QR code image. |
||
633 | * |
||
634 | * @return string |
||
635 | */ |
||
636 | 3 | public function getQrString() |
|
637 | { |
||
638 | 3 | return '?re=' . $this->emisor->rfc |
|
639 | 3 | . '&rr=' . $this->receptor->rfc |
|
640 | 3 | . '&tt=' . $this->total |
|
641 | 3 | . '&id=' . $this->timbre->uuid; |
|
642 | } |
||
643 | |||
644 | /** |
||
645 | * Gets the qr code in string format. |
||
646 | * |
||
647 | * @param integer $width Width of the QR code image. |
||
648 | * @param integer $height Height of the QR code image. |
||
649 | * @param bool $base64 If you want raw bytes then set this to 'false'. |
||
650 | * |
||
651 | * @return string |
||
652 | */ |
||
653 | 2 | public function qr($width = 256, $height = 256, $base64 = true) |
|
654 | { |
||
655 | 2 | $renderer = new \BaconQrCode\Renderer\Image\Png(); |
|
656 | 2 | $renderer->setWidth($width); |
|
657 | 2 | $renderer->setHeight($height); |
|
658 | |||
659 | 2 | $writer = new \BaconQrCode\Writer($renderer); |
|
660 | 2 | $qrString = $writer->writeString($this->getQrString()); |
|
661 | |||
662 | 2 | if (!$base64) { |
|
663 | 1 | return $qrString; |
|
664 | } |
||
665 | |||
666 | 1 | return base64_encode($qrString); |
|
667 | } |
||
668 | |||
669 | /** |
||
670 | * Writes the QR code string (bytes) to a file in order to generate the |
||
671 | * QR code image. |
||
672 | * |
||
673 | * @param string $filepath Full path to file location with filename included. |
||
674 | * @param integer $width Width of the QR code image. |
||
675 | * @param integer $height Height of the QR code image. |
||
676 | * |
||
677 | * @return integer Bytes written or 'false' on failure. |
||
678 | */ |
||
679 | 1 | public function qrCode($filepath, $width = 256, $height = 256) |
|
680 | { |
||
681 | 1 | return file_put_contents($filepath, $this->qr($width, $height, false)); |
|
682 | } |
||
683 | } |
||
684 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.