Passed
Push — master ( 66ce43...256ff6 )
by Esteban De La Fuente
08:40
created

DocumentoFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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