Passed
Push — master ( 66ce43...256ff6 )
by Esteban De La Fuente
08:40
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\Sii\Dte\Documento\Parser\DocumentoParser;
32
use libredte\lib\Core\Xml\XmlConverter;
33
use libredte\lib\Core\Xml\XmlDocument;
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
     * Analizador sintáxtico (parser) general para permitir la carga de datos en
50
     * diferentes formatos.
51
     *
52
     * @var DocumentoParser
53
     */
54
    private DocumentoParser $parser;
55
56
    /**
57
     * Constructores ("builders") de documentos que están inicializados.
58
     *
59
     * Esta es la "caché" para evitar instanciar más de una vez un "builder".
60
     *
61
     * @var array
62
     */
63
    private array $builders = [];
64
65
    /**
66
     * Proveedor de datos.
67
     *
68
     * @var DataProviderInterface
69
     */
70
    protected DataProviderInterface $dataProvider;
71
72
    /**
73
     * Constructor de la clase.
74
     *
75
     * @param ?DataProviderInterface $dataProvider Proveedor de datos.
76
     */
77 309
    public function __construct(?DataProviderInterface $dataProvider = null)
78
    {
79 309
        $this->dataProvider = $dataProvider ?? new ArrayDataProvider();
80
    }
81
82
    /**
83
     * Entrega la instancia del parser para ser utilizado al crear el DTE a
84
     * partir de datos que no son los de un arreglo con el formato oficial.
85
     *
86
     * @return DocumentoParser
87
     */
88 3
    private function getParser(): DocumentoParser
89
    {
90 3
        if (!isset($this->parser)) {
91 3
            $this->parser = new DocumentoParser();
92
        }
93
94 3
        return $this->parser;
95
    }
96
97
    /**
98
     * Construye un documento tributario electrónico a partir de los datos en
99
     * un arreglo.
100
     *
101
     * @param array $data Arreglo con los datos del documento.
102
     * @return AbstractDocumento Documento tributario construído.
103
     */
104 307
    public function createFromArray(array $data): AbstractDocumento
105
    {
106
        // Crear builder para el documento que se creará.
107 307
        $builderClass = $this->getDocumentoBuilderClass($data);
108 303
        $builder = $this->getDocumentoBuilder($builderClass);
109
110
        // Construir y retornar el documento tributario solicitado.
111 303
        return $builder->build($data);
112
    }
113
114
    /**
115
     * Construye un documento tributario electrónico a partir de los datos en
116
     * un string XML.
117
     *
118
     * @param string $data String XML con los datos del documento.
119
     * @return AbstractDocumento Documento tributario construído.
120
     */
121 1
    public function createFromXml(string $data): AbstractDocumento
122
    {
123 1
        $array = $this->getParser()->parse($data, 'sii.xml');
124
125 1
        return $this->createFromArray($array);
126
    }
127
128
    /**
129
     * Construye un documento tributario electrónico a partir de los datos en
130
     * un string YAML.
131
     *
132
     * @param string $data String YAML con los datos del documento.
133
     * @return AbstractDocumento Documento tributario construído.
134
     */
135 1
    public function createFromYaml(string $data): AbstractDocumento
136
    {
137 1
        $array = $this->getParser()->parse($data, 'sii.yaml');
138
139 1
        return $this->createFromArray($array);
140
    }
141
142
    /**
143
     * Construye un documento tributario electrónico a partir de los datos en
144
     * un string JSON.
145
     *
146
     * @param string $data String JSON con los datos del documento.
147
     * @return AbstractDocumento Documento tributario construído.
148
     */
149 1
    public function createFromJson(string $data): AbstractDocumento
150
    {
151 1
        $array = $this->getParser()->parse($data, 'sii.json');
152
153 1
        return $this->createFromArray($array);
154
    }
155
156
    /**
157
     * Carga un documento tributario electrónico a partir de los datos en
158
     * un string XML.
159
     *
160
     * NOTE: Este método de creación de un documento espera que el XML contenga
161
     * todos los nodos y datos necesarios del documento (ej: incluyendo firma).
162
     * Se debe utilizar solamente para construir los documentos que vienen de
163
     * un XML que ya está listo el DTE. Si se desea crear un documento a partir
164
     * de datos que están en un string XML, pero que no están normalizados,
165
     * timbrados o firmados, se debe utilizar createFromXml().
166
     *
167
     * @param string $xml String XML con los datos del documento.
168
     * @return AbstractDocumento Documento tributario construído.
169
     */
170 2
    public function loadFromXml(string $xml): AbstractDocumento
171
    {
172 2
        $xmlDocument = new XmlDocument();
173 2
        $xmlDocument->loadXML($xml);
174 2
        $array = XmlConverter::xmlToArray($xmlDocument);
175
176
        // Obtener los datos del documento a generar.
177 2
        $data = $array['DTE']['Documento']
178 2
            ?? $array['DTE']['Exportaciones']
179 2
            ?? $array['DTE']['Liquidacion']
180
            ?? []
181 2
        ;
182
183
        // Crear builder para el documento que se creará.
184 2
        $builderClass = $this->getDocumentoBuilderClass($data);
185 2
        $builder = $this->getDocumentoBuilder($builderClass);
186
187
        // Construir una instancia del documento con los datos del XML y
188
        // retornarla.
189 2
        return $builder->loadFromXml($xml);
190
    }
191
192
    /**
193
     * Entrega la instancia del "builder" según la clase que se ha indicado.
194
     *
195
     * Este método utiliza una "caché" para entregar siempre el mismo "builder"
196
     * para el mismo tipo de documento.
197
     *
198
     * @param string $class Clase del "builder" que se desea su instancia.
199
     * @return AbstractDocumentoBuilder Instancia del "builder" solicitado.
200
     */
201 305
    private function getDocumentoBuilder(string $class): AbstractDocumentoBuilder
202
    {
203 305
        if (!isset($this->builders[$class])) {
204 305
            $this->builders[$class] = new $class(
205 305
                $this->dataProvider
206 305
            );
207
        }
208
209 305
        return $this->builders[$class];
210
    }
211
212
    /**
213
     * Determina qué "builder" se debe utilizar según el código del documento
214
     * que viene en los datos del documento que se debe crear.
215
     *
216
     * @param array $data Arreglo con los datos del documento.
217
     * @return string Clase del "builder" a usar para crear el documento.
218
     */
219 309
    private function getDocumentoBuilderClass(array $data): string
220
    {
221
        // Obtener el código del tipo de documento que se debe generar.
222 309
        $TipoDTE = $data['Encabezado']['IdDoc']['TipoDTE'] ?? null;
223 309
        if ($TipoDTE === null) {
224 1
            throw new DocumentoException(sprintf(
225 1
                'No se encontró el campo %s en el documento, el cual es obligatorio.',
226 1
                'TipoDTE'
227 1
            ));
228
        }
229
230
        // Determinar clase del "builder" en base al tipo de documento.
231 308
        switch ((int) $TipoDTE) {
232 308
            case 33:
233 106
                return FacturaAfectaBuilder::class;
234 203
            case 34:
235 31
                return FacturaExentaBuilder::class;
236 173
            case 39:
237 13
                return BoletaAfectaBuilder::class;
238 161
            case 41:
239 13
                return BoletaExentaBuilder::class;
240 148
            case 43:
241
                return LiquidacionFacturaBuilder::class;
242 148
            case 46:
243 31
                return FacturaCompraBuilder::class;
244 118
            case 52:
245 31
                return GuiaDespachoBuilder::class;
246 88
            case 56:
247 13
                return NotaDebitoBuilder::class;
248 76
            case 61:
249 19
                return NotaCreditoBuilder::class;
250 58
            case 110:
251 19
                return FacturaExportacionBuilder::class;
252 40
            case 111:
253 19
                return NotaDebitoExportacionBuilder::class;
254 22
            case 112:
255 19
                return NotaCreditoExportacionBuilder::class;
256
            default:
257 3
                throw new DocumentoException(sprintf(
258 3
                    'El valor "%s" del campo %s del documento es inválido.',
259 3
                    $TipoDTE,
260 3
                    'TipoDTE'
261 3
                ));
262
        }
263
    }
264
}
265