Completed
Pull Request — master (#39)
by Cesar
02:26
created

CfdiCreator33::__toString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
namespace CfdiUtils;
3
4
use CfdiUtils\CadenaOrigen\DOMBuilder;
5
use CfdiUtils\CadenaOrigen\XsltBuilderInterface;
6
use CfdiUtils\CadenaOrigen\XsltBuilderPropertyInterface;
7
use CfdiUtils\CadenaOrigen\XsltBuilderPropertyTrait;
8
use CfdiUtils\Certificado\Certificado;
9
use CfdiUtils\Certificado\CertificadoPropertyInterface;
10
use CfdiUtils\Certificado\CertificadoPropertyTrait;
11
use CfdiUtils\Elements\Cfdi33\Comprobante;
12
use CfdiUtils\Elements\Cfdi33\Helpers\SumasConceptosWriter;
13
use CfdiUtils\Nodes\NodeInterface;
14
use CfdiUtils\Nodes\XmlNodeUtils;
15
use CfdiUtils\PemPrivateKey\PemPrivateKey;
16
use CfdiUtils\SumasConceptos\SumasConceptos;
17
use CfdiUtils\Validate\Asserts;
18
use CfdiUtils\Validate\Hydrater;
19
use CfdiUtils\Validate\MultiValidatorFactory;
20
use CfdiUtils\XmlResolver\XmlResolver;
21
use CfdiUtils\XmlResolver\XmlResolverPropertyInterface;
22
use CfdiUtils\XmlResolver\XmlResolverPropertyTrait;
23
24
class CfdiCreator33 implements
25
    CertificadoPropertyInterface,
26
    XmlResolverPropertyInterface,
27
    XsltBuilderPropertyInterface
28
{
29
    use CertificadoPropertyTrait;
30
    use XmlResolverPropertyTrait;
31
    use XsltBuilderPropertyTrait;
32
33
    /** @var Comprobante */
34
    private $comprobante;
35
36
    /**
37
     * CfdiCreator33 constructor.
38
     * @param string[] $complementoAttributes
39
     * @param Certificado|null $certificado
40
     * @param XmlResolver|null $xmlResolver
41
     * @param XsltBuilderInterface|null $xsltBuilder
42
     */
43 15
    public function __construct(
44
        array $complementoAttributes = [],
45
        Certificado $certificado = null,
46
        XmlResolver $xmlResolver = null,
47
        XsltBuilderInterface $xsltBuilder = null
48
    ) {
49 15
        $this->comprobante = new Comprobante($complementoAttributes);
50 15
        $this->setXmlResolver($xmlResolver ? : new XmlResolver());
51 15
        if (null !== $certificado) {
52 13
            $this->putCertificado($certificado);
53
        }
54 15
        $this->setXsltBuilder($xsltBuilder ? : new DOMBuilder());
55 15
    }
56
57
    public static function newUsingNode(
58
        NodeInterface $node,
59
        Certificado $certificado = null,
60
        XmlResolver $xmlResolver = null
61
    ): self {
62
        $new = new self($node->attributes()->exportArray(), $certificado, $xmlResolver);
63
        $comprobante = $new->comprobante();
64
        foreach ($node as $child) {
65
            $comprobante->addChild($child);
66
        }
67
        return $new;
68
    }
69
70 13
    public function comprobante(): Comprobante
71
    {
72 13
        return $this->comprobante;
73
    }
74
75 13
    public function putCertificado(Certificado $certificado, bool $putEmisorRfcNombre = true)
76
    {
77 13
        $this->setCertificado($certificado);
78 13
        $cerfile = $certificado->getFilename();
79 13
        $this->comprobante['NoCertificado'] = $certificado->getSerial();
80 13
        if (file_exists($cerfile)) {
81 13
            $this->comprobante['Certificado'] = base64_encode(file_get_contents($cerfile));
82
        }
83 13
        if ($putEmisorRfcNombre) {
84 13
            $this->comprobante->addEmisor([
85 13
                'Nombre' => $certificado->getName(),
86 13
                'Rfc' => $certificado->getRfc(),
87
            ]);
88
        }
89 13
    }
90
91 2
    public function asXml(): string
92
    {
93 2
        return XmlNodeUtils::nodeToXmlString($this->comprobante, true);
94
    }
95
96
    public function saveXml(string $filename): bool
97
    {
98
        return (false !== file_put_contents($filename, $this->asXml()));
99
    }
100
101 1
    public function buildCadenaDeOrigen(): string
102
    {
103 1
        if (! $this->hasXmlResolver()) {
104
            throw new \LogicException(
105
                'Cannot build the cadena de origen since there is no xml resolver'
106
            );
107
        }
108 1
        $xsltLocation = $this->getXmlResolver()->resolveCadenaOrigenLocation('3.3');
109 1
        return $this->getXsltBuilder()->build($this->asXml(), $xsltLocation);
110
    }
111
112 1
    public function buildSumasConceptos(int $precision = 2): SumasConceptos
113
    {
114 1
        return new SumasConceptos($this->comprobante, $precision);
115
    }
116
117 1
    public function addSumasConceptos(SumasConceptos $sumasConceptos = null, int $precision = 2)
118
    {
119 1
        $sumasConceptos = $sumasConceptos ? : $this->buildSumasConceptos($precision);
120 1
        $writer = new SumasConceptosWriter($this->comprobante, $sumasConceptos, $precision);
121 1
        $writer->put();
122 1
    }
123
124 1
    public function addSello(string $key, string $passPhrase = '')
125
    {
126
        // create private key
127 1
        $privateKey = new PemPrivateKey($key);
128 1
        if (! $privateKey->open($passPhrase)) {
129
            throw new \RuntimeException('Cannot open the private key');
130
        }
131
132
        // check privatekey belongs to certificado
133 1
        if ($this->hasCertificado()) {
134 1
            if (! $privateKey->belongsTo($this->getCertificado()->getPemContents())) {
135
                throw new \RuntimeException('The private key does not belong to the current certificate');
136
            }
137
        }
138
139
        // create sign and set into Sello attribute
140 1
        $this->comprobante['Sello'] = base64_encode(
141 1
            $privateKey->sign($this->buildCadenaDeOrigen(), OPENSSL_ALGO_SHA256)
142
        );
143 1
    }
144
145
    /**
146
     * @return Asserts|\CfdiUtils\Validate\Assert[]
147
     */
148 1
    public function validate(): Asserts
149
    {
150 1
        $factory = new MultiValidatorFactory();
151 1
        $validator = $factory->newCreated33();
152
153 1
        $hydrater = new Hydrater();
154 1
        $hydrater->setXmlString($this->asXml());
155 1
        $hydrater->setXmlResolver(($this->hasXmlResolver()) ? $this->getXmlResolver() : null);
156 1
        $hydrater->setXsltBuilder($this->getXsltBuilder());
157 1
        $validator->hydrate($hydrater);
158
159 1
        $asserts = new Asserts();
160 1
        $validator->validate($this->comprobante(), $asserts);
161
162 1
        return $asserts;
163
    }
164
165
    /**
166
     * @return string
167
     */
168 2
    public function __toString()
169
    {
170
        try {
171 2
            return $this->asXml();
172 1
        } catch (\Throwable $ex) {
173 1
            return '';
174
        }
175
    }
176
}
177