Completed
Push — master ( abfe96...346057 )
by Carlos C
23s queued 13s
created

putComplementoImpuestoLocalSumas()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 11
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 17
rs 9.6111
1
<?php
2
3
namespace CfdiUtils\SumasConceptos;
4
5
use CfdiUtils\Elements\Cfdi33\Comprobante as Comprobante33;
6
use CfdiUtils\Elements\Cfdi40\Comprobante as Comprobante40;
7
use CfdiUtils\Nodes\NodeInterface;
8
use InvalidArgumentException;
9
10
class SumasConceptosWriter
11
{
12
    /** @var Comprobante33|Comprobante40 */
13
    private $comprobante;
14
15
    /** @var SumasConceptos */
16
    private $sumas;
17
18
    /** @var int */
19
    private $precision;
20
21
    /** @var bool */
22
    private $writeImpuestoBase;
23
24
    /**
25
     * Writer constructor.
26
     * @param Comprobante33|Comprobante40 $comprobante
27
     * @param SumasConceptos $sumas
28
     * @param int $precision
29
     */
30
    public function __construct(
31
        NodeInterface $comprobante,
32
        SumasConceptos $sumas,
33
        int $precision = 6
34
    ) {
35
        if ($comprobante instanceof Comprobante33) {
36
            $this->writeImpuestoBase = false;
37
        } elseif ($comprobante instanceof Comprobante40) {
38
            $this->writeImpuestoBase = true;
39
        } else {
40
            throw new InvalidArgumentException(
41
                'The argument $comprobante must be a Comprobante (CFDI 3.3 or CFDI 4.0) element'
42
            );
43
        }
44
        $this->comprobante = $comprobante;
0 ignored issues
show
Documentation Bug introduced by
$comprobante is of type CfdiUtils\Nodes\NodeInterface, but the property $comprobante was declared to be of type CfdiUtils\Elements\Cfdi3...ents\Cfdi40\Comprobante. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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 given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
45
        $this->sumas = $sumas;
46
        $this->precision = $precision;
47
    }
48
49
    public function put()
50
    {
51
        $this->putComprobanteSumas();
52
        $this->putImpuestosNode();
53
        $this->putComplementoImpuestoLocalSumas();
54
    }
55
56
    private function putComprobanteSumas(): void
57
    {
58
        $this->comprobante['SubTotal'] = $this->format($this->sumas->getSubTotal());
59
        $this->comprobante['Total'] = $this->format($this->sumas->getTotal());
60
        $this->comprobante['Descuento'] = $this->format($this->sumas->getDescuento());
61
        if (! $this->sumas->foundAnyConceptWithDiscount()
62
            && ! $this->valueGreaterThanZero($this->sumas->getDescuento())) {
63
            unset($this->comprobante['Descuento']);
64
        }
65
    }
66
67
    private function putImpuestosNode(): void
68
    {
69
        // obtain node reference
70
        $impuestos = $this->comprobante->getImpuestos();
71
        // if there is nothing to write then remove the children and exit
72
        if (! $this->sumas->hasTraslados() && ! $this->sumas->hasRetenciones()) {
73
            $this->comprobante->children()->remove($impuestos);
74
            return;
75
        }
76
        // clear previous values
77
        $impuestos->clear();
78
        // add traslados when needed
79
        if ($this->sumas->hasTraslados()) {
80
            $impuestos['TotalImpuestosTrasladados'] = $this->format($this->sumas->getImpuestosTrasladados());
81
            $impuestos->getTraslados()->multiTraslado(
82
                ...$this->getImpuestosContents($this->sumas->getTraslados(), $this->writeImpuestoBase)
83
            );
84
        }
85
        // add retenciones when needed
86
        if ($this->sumas->hasRetenciones()) {
87
            $impuestos['TotalImpuestosRetenidos'] = $this->format($this->sumas->getImpuestosRetenidos());
88
            $impuestos->getRetenciones()->multiRetencion(
89
                ...$this->getImpuestosContents($this->sumas->getRetenciones(), false)
90
            );
91
        }
92
    }
93
94
    private function putComplementoImpuestoLocalSumas(): void
95
    {
96
        // search for implocal node
97
        $impLocal = $this->comprobante->searchNode('cfdi:Complemento', 'implocal:ImpuestosLocales');
98
        if (! $impLocal) {
99
            return;
100
        }
101
        if (! $this->sumas->hasLocalesTraslados() && ! $this->sumas->hasLocalesRetenciones()) {
102
            $complemento = $this->comprobante->getComplemento();
103
            $complemento->children()->remove($impLocal);
104
            if (0 === $complemento->count()) {
105
                $this->comprobante->children()->remove($complemento);
106
            }
107
            return;
108
        }
109
        $impLocal->attributes()->set('TotaldeRetenciones', $this->format($this->sumas->getLocalesImpuestosRetenidos()));
110
        $impLocal->attributes()->set('TotaldeTraslados', $this->format($this->sumas->getLocalesImpuestosTrasladados()));
111
    }
112
113
    private function getImpuestosContents(array $impuestos, bool $hasBase): array
114
    {
115
        $return = [];
116
        foreach ($impuestos as $impuesto) {
117
            $impuesto['Base'] = $this->format($impuesto['Base'] ?? 0);
118
            $impuesto['Importe'] = $this->format($impuesto['Importe']);
119
            if (! $hasBase) {
120
                unset($impuesto['Base']);
121
            }
122
            $return[] = $impuesto;
123
        }
124
        return $return;
125
    }
126
127
    private function valueGreaterThanZero(float $value): bool
128
    {
129
        return (round($value, $this->precision) > 0);
130
    }
131
132
    public function format(float $number): string
133
    {
134
        return number_format($number, $this->precision, '.', '');
135
    }
136
137
    /** @return Comprobante33|Comprobante40 */
138
    public function getComprobante()
139
    {
140
        return $this->comprobante;
141
    }
142
143
    public function getSumasConceptos(): SumasConceptos
144
    {
145
        return $this->sumas;
146
    }
147
148
    public function getPrecision(): int
149
    {
150
        return $this->precision;
151
    }
152
153
    public function hasWriteImpuestoBase(): bool
154
    {
155
        return $this->writeImpuestoBase;
156
    }
157
}
158