Passed
Push — master ( c02ff8...8cf6c5 )
by Esteban De La Fuente
06:05
created

DocumentoFactory::getDocumentoBuilderClass()   C

Complexity

Conditions 14
Paths 14

Size

Total Lines 42
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 14.0042

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 35
c 1
b 0
f 0
nc 14
nop 1
dl 0
loc 42
ccs 35
cts 36
cp 0.9722
crap 14.0042
rs 6.2666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Builder;
26
27
use libredte\lib\Core\Service\ArrayDataProvider;
28
use libredte\lib\Core\Service\DataProviderInterface;
29
use libredte\lib\Core\Sii\Dte\Documento\AbstractDocumento;
30
use libredte\lib\Core\Sii\Dte\Documento\DocumentoException;
31
use libredte\lib\Core\Xml\XmlConverter;
32
use libredte\lib\Core\Xml\XmlDocument;
33
use Symfony\Component\Yaml\Yaml;
34
35
/**
36
 * Fábrica de documentos tributarios electrónicos.
37
 *
38
 * Permite crear un documento tributario electrónico a partir de los datos de
39
 * un arreglo o de un string XML.
40
 *
41
 * La principal ventaja de usar esta fábrica es que abstrae todo lo que se debe
42
 * hacer para buscar el "builder" del documento tributario y crear un documento
43
 * fácilmente a partir de sus datos. Además se preocupa de instanciar solo una
44
 * vez cada "builder".
45
 */
46
class DocumentoFactory
47
{
48
    /**
49
     * Constructores ("builders") de documentos que están inicializados.
50
     *
51
     * Esta es la "caché" para evitar instanciar más de una vez un "builder".
52
     *
53
     * @var array
54
     */
55
    private array $builders = [];
56
57
    /**
58
     * Proveedor de datos.
59
     *
60
     * @var DataProviderInterface
61
     */
62
    protected DataProviderInterface $dataProvider;
63
64
    /**
65
     * Constructor de la clase.
66
     *
67
     * @param ?DataProviderInterface $dataProvider Proveedor de datos.
68
     */
69 120
    public function __construct(?DataProviderInterface $dataProvider = null)
70
    {
71 120
        $this->dataProvider = $dataProvider ?? new ArrayDataProvider();
72
    }
73
74
    /**
75
     * Construye un documento tributario electrónico a partir de los datos en
76
     * un arreglo.
77
     *
78
     * @param array $data Arreglo con los datos del documento.
79
     * @return AbstractDocumento Documento tributario construído.
80
     */
81 118
    public function createFromArray(array $data): AbstractDocumento
82
    {
83
        // Crear builder para el documento que se creará.
84 118
        $builderClass = $this->getDocumentoBuilderClass($data);
85 114
        $builder = $this->getDocumentoBuilder($builderClass);
86
87
        // Construir y retornar el documento tributario solicitado.
88 114
        return $builder->build($data);
89
    }
90
91
    /**
92
     * Construye un documento tributario electrónico a partir de los datos en
93
     * un string XML.
94
     *
95
     * @param string $data String XML con los datos del documento.
96
     * @return AbstractDocumento Documento tributario construído.
97
     */
98 1
    public function createFromXml(string $data): AbstractDocumento
99
    {
100 1
        $xmlDocument = new XmlDocument();
101 1
        $xmlDocument->loadXML($data);
102 1
        $array = XmlConverter::xmlToArray($xmlDocument);
103
104
        // Obtener los datos del documento a generar.
105 1
        $documentoData = $array['DTE']['Documento']
106 1
            ?? $array['DTE']['Exportaciones']
107 1
            ?? $array['DTE']['Liquidacion']
108
            ?? null
109 1
        ;
110
111
        // Si el XML no tiene los tags válidos se lanza una excepción.
112 1
        if ($documentoData === null) {
113
            throw new DocumentoException(
114
                'El nodo raíz del XML del documento debe ser el tag "DTE". Dentro de este nodo raíz debe existir un tag "Documento", "Exportaciones" o "Liquidacion". Este segundo nodo es el que debe contener los datos del documento.'
115
            );
116
        }
117
118
        // Quitar los atributos que tenga el tag encontrado.
119 1
        unset($documentoData['@attributes']);
120
121
        // Crear el documento a partir de los datos encontrados.
122 1
        return $this->createFromArray($documentoData);
123
    }
124
125
    /**
126
     * Construye un documento tributario electrónico a partir de los datos en
127
     * un string YAML.
128
     *
129
     * @param string $data String YAML con los datos del documento.
130
     * @return AbstractDocumento Documento tributario construído.
131
     */
132 1
    public function createFromYaml(string $data): AbstractDocumento
133
    {
134 1
        $array = Yaml::parse($data);
135
136 1
        return $this->createFromArray($array);
137
    }
138
139
    /**
140
     * Construye un documento tributario electrónico a partir de los datos en
141
     * un string JSON.
142
     *
143
     * @param string $data String JSON con los datos del documento.
144
     * @return AbstractDocumento Documento tributario construído.
145
     */
146 1
    public function createFromJson(string $data): AbstractDocumento
147
    {
148 1
        $array = json_decode($data, true);
149
150 1
        return $this->createFromArray($array);
151
    }
152
153
    /**
154
     * Carga un documento tributario electrónico a partir de los datos en
155
     * un string XML.
156
     *
157
     * NOTE: Este método de creación de un documento espera que el XML contenga
158
     * todos los nodos y datos necesarios del documento (ej: incluyendo firma).
159
     * Se debe utilizar solamente para construir los documentos que vienen de
160
     * un XML que ya está listo el DTE. Si se desea crear un documento a partir
161
     * de datos que están en un string XML, pero que no están normalizados,
162
     * timbrados o firmados, se debe utilizar createFromXml().
163
     *
164
     * @param string $xml String XML con los datos del documento.
165
     * @return AbstractDocumento Documento tributario construído.
166
     */
167 2
    public function loadFromXml(string $xml): AbstractDocumento
168
    {
169 2
        $xmlDocument = new XmlDocument();
170 2
        $xmlDocument->loadXML($xml);
171 2
        $array = XmlConverter::xmlToArray($xmlDocument);
172
173
        // Obtener los datos del documento a generar.
174 2
        $data = $array['DTE']['Documento']
175 2
            ?? $array['DTE']['Exportaciones']
176 2
            ?? $array['DTE']['Liquidacion']
177
            ?? null
178 2
        ;
179
180
        // Crear builder para el documento que se creará.
181 2
        $builderClass = $this->getDocumentoBuilderClass($data);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type null; however, parameter $data of libredte\lib\Core\Sii\Dt...DocumentoBuilderClass() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
        $builderClass = $this->getDocumentoBuilderClass(/** @scrutinizer ignore-type */ $data);
Loading history...
182 2
        $builder = $this->getDocumentoBuilder($builderClass);
183
184
        // Construir una instancia del documento con los datos del XML y
185
        // retornarla.
186 2
        return $builder->loadFromXml($xml);
187
    }
188
189
    /**
190
     * Entrega la instancia del "builder" según la clase que se ha indicado.
191
     *
192
     * Este método utiliza una "caché" para entregar siempre el mismo "builder"
193
     * para el mismo tipo de documento.
194
     *
195
     * @param string $class Clase del "builder" que se desea su instancia.
196
     * @return AbstractDocumentoBuilder Instancia del "builder" solicitado.
197
     */
198 116
    private function getDocumentoBuilder(string $class): AbstractDocumentoBuilder
199
    {
200 116
        if (!isset($this->builders[$class])) {
201 116
            $this->builders[$class] = new $class(
202 116
                $this->dataProvider
203 116
            );
204
        }
205
206 116
        return $this->builders[$class];
207
    }
208
209
    /**
210
     * Determina qué "builder" se debe utilizar según el código del documento
211
     * que viene en los datos del documento que se debe crear.
212
     *
213
     * @param array $data Arreglo con los datos del documento.
214
     * @return string Clase del "builder" a usar para crear el documento.
215
     */
216 120
    private function getDocumentoBuilderClass(array $data): string
217
    {
218
        // Obtener el código del tipo de documento que se debe generar.
219 120
        $TipoDTE = $data['Encabezado']['IdDoc']['TipoDTE'] ?? null;
220 120
        if ($TipoDTE === null) {
221 1
            throw new DocumentoException(sprintf(
222 1
                'No se encontró el campo %s en el documento, el cual es obligatorio.',
223 1
                'TipoDTE'
224 1
            ));
225
        }
226
227
        // Determinar clase del "builder" en base al tipo de documento.
228 119
        switch ((int) $TipoDTE) {
229 119
            case 33:
230 39
                return FacturaAfectaBuilder::class;
231 81
            case 34:
232 5
                return FacturaExentaBuilder::class;
233 77
            case 39:
234 9
                return BoletaAfectaBuilder::class;
235 69
            case 41:
236 9
                return BoletaExentaBuilder::class;
237 60
            case 43:
238
                return LiquidacionFacturaBuilder::class;
239 60
            case 46:
240 17
                return FacturaCompraBuilder::class;
241 44
            case 52:
242 9
                return GuiaDespachoBuilder::class;
243 36
            case 56:
244 5
                return NotaDebitoBuilder::class;
245 32
            case 61:
246 13
                return NotaCreditoBuilder::class;
247 20
            case 110:
248 9
                return FacturaExportacionBuilder::class;
249 12
            case 111:
250 5
                return NotaDebitoExportacionBuilder::class;
251 8
            case 112:
252 5
                return NotaCreditoExportacionBuilder::class;
253
            default:
254 3
                throw new DocumentoException(sprintf(
255 3
                    'El valor "%s" del campo %s del documento es inválido.',
256 3
                    $TipoDTE,
257 3
                    'TipoDTE'
258 3
                ));
259
        }
260
    }
261
}
262