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); |
|||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||
124 | 22 | $cfdi = simplexml_load_string($this->xmlContent); |
|||
125 | 22 | $this->cfdi = $cfdi; |
|||
126 | |||||
127 | 22 | return $this->isValid(true); |
|||
0 ignored issues
–
show
Are you sure the usage of
$this->isValid(true) targeting ZzAntares\CfdiWrapper\Cfdi::isValid() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
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 |
||||
0 ignored issues
–
show
The type
ZzAntares\CfdiWrapper\return was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
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); |
|||
0 ignored issues
–
show
|
|||||
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 |
||||
0 ignored issues
–
show
|
|||||
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; |
|||
0 ignored issues
–
show
|
|||||
180 | } |
||||
181 | |||||
182 | 1 | if ($throwException) { |
|||
183 | 1 | throw new MalformedCfdiException(); |
|||
184 | } |
||||
185 | |||||
186 | return false; |
||||
0 ignored issues
–
show
|
|||||
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(); |
|||
0 ignored issues
–
show
|
|||||
223 | break; |
||||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other ![]() |
|||||
224 | |||||
225 | 7 | case 'leyenda': |
|||
226 | 1 | return 'Este documento es una representación impresa de un CFDI'; |
|||
0 ignored issues
–
show
|
|||||
227 | break; |
||||
228 | |||||
229 | 6 | case 'iva': |
|||
230 | 1 | return $this->getImpuesto('iva'); |
|||
0 ignored issues
–
show
|
|||||
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; |
||||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other ![]() |
|||||
248 | |||||
249 | 10 | case 'receptor': |
|||
250 | 4 | return $this->getReceptor(); |
|||
251 | break; |
||||
252 | |||||
253 | 9 | case 'conceptos': |
|||
254 | 1 | return $this->getConceptos(); |
|||
0 ignored issues
–
show
|
|||||
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); |
|||
0 ignored issues
–
show
Are you sure the usage of
$this->getAttribute($attribute) targeting ZzAntares\CfdiWrapper\Cfdi::getAttribute() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
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
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. ![]() |
|||||
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, |
|||
0 ignored issues
–
show
The property
timbre does not exist on ZzAntares\CfdiWrapper\Cfdi . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
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. |
||||
0 ignored issues
–
show
The type
ZzAntares\CfdiWrapper\Which was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
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) { |
|||
0 ignored issues
–
show
The property
impuestos does not exist on ZzAntares\CfdiWrapper\Cfdi . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
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 |
|||
0 ignored issues
–
show
The property
emisor does not exist on ZzAntares\CfdiWrapper\Cfdi . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
639 | 3 | . '&rr=' . $this->receptor->rfc |
|||
0 ignored issues
–
show
The property
receptor does not exist on ZzAntares\CfdiWrapper\Cfdi . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
640 | 3 | . '&tt=' . $this->total |
|||
0 ignored issues
–
show
Are you sure
$this->total of type object can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() The property
total does not exist on ZzAntares\CfdiWrapper\Cfdi . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
641 | 3 | . '&id=' . $this->timbre->uuid; |
|||
0 ignored issues
–
show
The property
timbre does not exist on ZzAntares\CfdiWrapper\Cfdi . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
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 |