DocumentBagManagerWorker::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 9
dl 0
loc 11
ccs 1
cts 1
cp 1
crap 1
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 por
11
 * la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o
12
 * (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\Package\Billing\Component\Document\Worker;
26
27
use Derafu\Backbone\Abstract\AbstractWorker;
28
use Derafu\Backbone\Attribute\Worker;
29
use Derafu\Repository\Contract\RepositoryManagerInterface;
30
use Derafu\Xml\Contract\XmlDocumentInterface;
31
use Derafu\Xml\Contract\XmlServiceInterface;
32
use libredte\lib\Core\Package\Billing\Component\Document\Contract\BuilderWorkerInterface;
33
use libredte\lib\Core\Package\Billing\Component\Document\Contract\DocumentBagInterface;
34
use libredte\lib\Core\Package\Billing\Component\Document\Contract\DocumentBagManagerWorkerInterface;
35
use libredte\lib\Core\Package\Billing\Component\Document\Contract\DocumentInterface;
36
use libredte\lib\Core\Package\Billing\Component\Document\Contract\NormalizerWorkerInterface;
37
use libredte\lib\Core\Package\Billing\Component\Document\Contract\ParserWorkerInterface;
38
use libredte\lib\Core\Package\Billing\Component\Document\Contract\SanitizerWorkerInterface;
39
use libredte\lib\Core\Package\Billing\Component\Document\Contract\TipoDocumentoInterface;
40
use libredte\lib\Core\Package\Billing\Component\Document\Contract\ValidatorWorkerInterface;
41
use libredte\lib\Core\Package\Billing\Component\Document\Exception\DocumentBagManagerException;
42
use libredte\lib\Core\Package\Billing\Component\Document\Support\DocumentBag;
43
use libredte\lib\Core\Package\Billing\Component\TradingParties\Contract\EmisorFactoryInterface;
44
use libredte\lib\Core\Package\Billing\Component\TradingParties\Contract\ReceptorFactoryInterface;
45
46
/**
47
 * Clase para el administrador de la bolsa con los datos de un DTE.
48
 */
49
#[Worker(name: 'document_bag_manager', component: 'document', package: 'billing')]
50
class DocumentBagManagerWorker extends AbstractWorker implements DocumentBagManagerWorkerInterface
51
{
52
    protected $documentBagClass = DocumentBag::class;
53
54 56
    public function __construct(
55
        private XmlServiceInterface $xmlService,
56
        private BuilderWorkerInterface $builderWorker,
57
        private ParserWorkerInterface $parserWorker,
58
        private NormalizerWorkerInterface $normalizerWorker,
59
        private SanitizerWorkerInterface $sanitizerWorker,
60
        private ValidatorWorkerInterface $validatorWorker,
61
        private RepositoryManagerInterface $repositoryManager,
62
        private EmisorFactoryInterface $emisorFactory,
63
        private ReceptorFactoryInterface $receptorFactory
64
    ) {
65 56
    }
66
67
    /**
68
     * {@inheritDoc}
69
     */
70 53
    public function create(
71
        string|array|XmlDocumentInterface|DocumentInterface $source,
72
        bool $normalizeAll = true
73
    ): DocumentBagInterface {
74
        // Asignar (si se pasó) o crear la bolsa.
75 53
        $class = $this->documentBagClass;
76 53
        $bag = new $class();
77
78
        // Si los datos vienen como string se deben parsear para asignar.
79
        // Además se normalizarán.
80 53
        if (is_string($source)) {
0 ignored issues
show
introduced by
The condition is_string($source) is always false.
Loading history...
81
            $aux = explode(':', $source, 1);
82
            $parserStrategy = str_replace('parser.strategy.', '', $aux[0]);
83
            $inputData = $aux[1] ?? '';
84
            $bag->setInputData($inputData);
85
            $bag->getOptions()->set('parser.strategy', $parserStrategy);
86
            $$this->parserWorker->parse($bag);
87
        }
88
89
        // Si los datos vienen como arreglo son los datos normalizados.
90 53
        if (is_array($source)) {
0 ignored issues
show
introduced by
The condition is_array($source) is always true.
Loading history...
91 1
            $bag->setNormalizedData($source);
92
        }
93
94
        // Si los datos vienen como documento XML es un XML cargado desde un
95
        // string XML (carga realizada por LoaderWorker). Ya viene normalizado.
96 53
        if ($source instanceof XmlDocumentInterface) {
0 ignored issues
show
introduced by
$source is never a sub-type of Derafu\Xml\Contract\XmlDocumentInterface.
Loading history...
97 53
            $bag->setXmlDocument($source);
98
        }
99
100
        // Si los datos vienen como documento tributario entonces es un
101
        // documento que ya está creado. Puede estar o no timbrado y firmado,
102
        // eso no se determina ni valida acá. Si debe estará normalizado.
103 53
        if ($source instanceof DocumentInterface) {
0 ignored issues
show
introduced by
$source is never a sub-type of libredte\lib\Core\Packag...tract\DocumentInterface.
Loading history...
104
            $bag->setDocument($source);
105
        }
106
107
        // Normalizar los datos de la bolsa según otros datos que contenga.
108 53
        $bag = $this->normalize($bag, all: $normalizeAll);
109
110
        // Entregar la bolsa creada.
111 53
        return $bag;
112
    }
113
114
    /**
115
     * {@inheritDoc}
116
     */
117 56
    public function normalize(
118
        DocumentBagInterface $bag,
119
        bool $all = false
120
    ): DocumentBagInterface {
121
        // Datos esenciales que se normalizan.
122 56
        $this->ensureParsedData($bag);
123 56
        $this->ensureTipoDocumento($bag);
124
125
        // Datos extras que se pueden normalizar si se solicitó normalizar todo.
126 54
        if ($all === true) {
127 53
            $this->ensureNormalizedData($bag);
128 53
            $this->ensureXmlDocument($bag);
129 53
            $this->ensureDocument($bag);
130 53
            $this->ensureEmisor($bag);
131 53
            $this->ensureReceptor($bag);
132
        }
133
134
        // Entregar la bolsa normalizada.
135 54
        return $bag;
136
    }
137
138
    /**
139
     * Asegura que existan datos parseados en la bolsa si existen datos de
140
     * entrada para poder determinarlo y no está asignado previamente.
141
     *
142
     * Requiere: $bag->getInputData().
143
     *
144
     * Además, si los datos no usan la estrategia por defecto de parseo se debe
145
     * indicar en las opciones de la bolsa.
146
     *
147
     * @param DocumentBagInterface $bag
148
     * @return void
149
     */
150 56
    protected function ensureParsedData(DocumentBagInterface $bag): void
151
    {
152
        // Verificar si es necesario, y se puede, asignar.
153 56
        if ($bag->getParsedData() || !$bag->getInputData()) {
154 53
            return;
155
        }
156
157
        // Parsear los datos.
158 55
        $this->parserWorker->parse($bag);
159
    }
160
161
    /**
162
     * Asegura que exista un tipo de documento en la bolsa si existen datos para
163
     * poder determinarlo y no está asignado previamente.
164
     *
165
     * Requiere: $bag->getParsedData().
166
     *
167
     * @param DocumentBagInterface $bag
168
     * @return void
169
     */
170 56
    protected function ensureTipoDocumento(DocumentBagInterface $bag): void
171
    {
172
        // Verificar si es necesario, y se puede, asignar.
173 56
        if ($bag->getTipoDocumento() || !$bag->getCodigoTipoDocumento()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $bag->getCodigoTipoDocumento() of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
174 53
            return;
175
        }
176
177
        // Buscar el tipo de documento tributario que se desea construir.
178 54
        $codigoTipoDocumento = $bag->getCodigoTipoDocumento();
179 54
        $tipoDocumento = $this->repositoryManager
180 54
            ->getRepository(TipoDocumentoInterface::class)
181 54
            ->find($codigoTipoDocumento)
182 54
        ;
183
184
        // Si el documento no existe error.
185 54
        if (!$tipoDocumento) {
186
            throw new DocumentBagManagerException(sprintf(
187
                'No se encontró un código de documento tributario válido en los datos del DTE.'
188
            ));
189
        }
190
191
        // Asignar el tipo documento a la bolsa.
192 54
        $bag->setTipoDocumento($tipoDocumento);
193
    }
194
195
    /**
196
     * Asegura que existan los datos normalizados en la bolsa si existen datos
197
     * parseados para poder determinarlo y no está asignado previamente.
198
     *
199
     * Los datos normalizados también se pueden crear si existe un XmlDocument
200
     * o un DTE.
201
     *
202
     * Requiere: $bag->getParsedData() o $bag->getXmlDocument() o $bag->getDocument().
203
     *
204
     * @param DocumentBagInterface $bag
205
     * @return void
206
     */
207 53
    protected function ensureNormalizedData(DocumentBagInterface $bag): void
208
    {
209
        // Verificar si es necesario, y se puede, asignar.
210 53
        if ($bag->getNormalizedData()) {
211 53
            return;
212
        }
213
214
        // Construir los datos normalizados a partir de los datos parseados.
215
        if ($bag->getParsedData()) {
216
            // Normalizar los datos del documento de la bolsa si corresponde.
217
            $normalize = $bag->getNormalizerOptions()['normalize'] ?? true;
218
            if ($normalize) {
219
                // Normalizar los datos parseados de la bolsa.
220
                $this->normalizerWorker->normalize($bag);
221
222
                // Sanitizar los datos normalizados de la bolsa.
223
                $this->sanitizerWorker->sanitize($bag);
224
225
                // Validar los datos normalizados y sanitizados de la bolsa.
226
                $this->validatorWorker->validate($bag);
227
            }
228
        }
229
230
        // Construir los datos normalizados a partir del documento XML.
231
        // Importante: se asume que el documento XML se cargó desde un XML que
232
        // es válido y por ende normalizado.
233
        elseif ($bag->getXmlDocument()) {
234
            $normalizedData = $this->xmlService->decode(
235
                $bag->getXmlDocument()
236
            );
237
            $bag->setNormalizedData($normalizedData);
238
        }
239
240
        // Construir los datos normalizados a partir del DTE.
241
        // Importante: se asume que el DTE se cargó desde un XML que
242
        // es válido y por ende normalizado.
243
        elseif ($bag->getDocument()) {
244
            $normalizedData = $this->xmlService->decode(
245
                $bag->getDocument()->getXmlDocument()
246
            );
247
            $bag->setNormalizedData($normalizedData);
248
        }
249
    }
250
251
    /**
252
     * Asegura que existan un documento XML en la bolsa si existen datos
253
     * normalizados para poder determinarlo y no está asignado previamente.
254
     *
255
     * El XmlDocument también se puede crear si existe DTE.
256
     *
257
     * Requiere: $bag->getNormalizedData() o $bag->getDocument().
258
     *
259
     * @param DocumentBagInterface $bag
260
     * @return void
261
     */
262 53
    protected function ensureXmlDocument(DocumentBagInterface $bag): void
263
    {
264
        // Verificar si es necesario, y se puede, asignar.
265 53
        if ($bag->getXmlDocument()) {
266 53
            return;
267
        }
268
269
        // Construir el XmlDocument a partir de los datos normalizados.
270 1
        if ($bag->getNormalizedData()) {
271 1
            $tagXml = $bag->getTipoDocumento()->getTagXml()->getNombre();
272 1
            $xmlDocumentData = [
273 1
                'DTE' => [
274 1
                    '@attributes' => [
275 1
                        'version' => '1.0',
276 1
                        'xmlns' => 'http://www.sii.cl/SiiDte',
277 1
                    ],
278 1
                    $tagXml => array_merge([
279 1
                        '@attributes' => [
280 1
                            'ID' => $bag->getId(),
281 1
                        ],
282 1
                    ], $bag->getNormalizedData()),
283 1
                ],
284 1
            ];
285 1
            $xmlDocument = $this->xmlService->encode(
286 1
                $xmlDocumentData
287 1
            );
288 1
            $bag->setXmlDocument($xmlDocument);
289
        }
290
291
        // Asignar el XmlDocument a partir del DTE.
292
        elseif ($bag->getDocument()) {
293
            $bag->setXmlDocument($bag->getDocument()->getXmlDocument());
294
        }
295
    }
296
297
    /**
298
     * Asegura que existan un DTE en la bolsa si existe un XmlDocument
299
     * para poder crearlo y no está asignado previamente.
300
     *
301
     * Requiere: $bag->getXmlDocument() y $bag->getTipoDocumento().
302
     *
303
     * @param DocumentBagInterface $bag
304
     * @return void
305
     */
306 53
    protected function ensureDocument(DocumentBagInterface $bag): void
307
    {
308
        // Verificar si es necesario, y se puede, asignar.
309 53
        if ($bag->getDocument() || !$bag->getXmlDocument() || !$bag->getTipoDocumento()) {
310 53
            return;
311
        }
312
313
        // Crear el DTE.
314 1
        $document = $this->builderWorker->create($bag);
315 1
        $bag->setDocument($document);
316
    }
317
318
    /**
319
     * Asegura que existan el emisor en la bolsa si existe el dTE
320
     * para poder determinarlo y no está asignado previamente.
321
     *
322
     * El emisor también se pueden crear si existen un XmlDocument, datos
323
     * normalizados o datos parseados.
324
     *
325
     * Requiere: $bag->getDocument(), $bag->getXmlDocument(),
326
     * $bag->getNormalizedData() o $bag->getParsedData().
327
     *
328
     * @param DocumentBagInterface $bag
329
     * @return void
330
     */
331 53
    protected function ensureEmisor(DocumentBagInterface $bag): void
332
    {
333
        // Verificar si es necesario, y se puede, asignar.
334 53
        if ($bag->getEmisor()) {
335 52
            return;
336
        }
337
338
        // Crear a partir de los datos del DTE (sería lo más normal).
339 53
        if ($bag->getDocument()) {
340 53
            $emisor = $this->emisorFactory->create(
341 53
                $bag->getDocument()->getEmisor()
342 53
            );
343 53
            $bag->setEmisor($emisor);
344
        }
345
346
        // Crear a partir de los datos del XmlDocument.
347
        elseif ($bag->getXmlDocument()) {
348
            $emisor = $this->emisorFactory->create(
349
                $bag->getXmlDocument()->query(
0 ignored issues
show
Bug introduced by
It seems like $bag->getXmlDocument()->...y('/Encabezado/Emisor') can also be of type null and string; however, parameter $data of libredte\lib\Core\Packag...toryInterface::create() 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

349
                /** @scrutinizer ignore-type */ $bag->getXmlDocument()->query(
Loading history...
350
                    '/Encabezado/Emisor'
351
                )
352
            );
353
            $bag->setEmisor($emisor);
354
        }
355
356
        // Crear a partir de los datos normalizados.
357
        elseif ($bag->getNormalizedData()) {
358
            $emisor = $this->emisorFactory->create(
359
                $bag->getNormalizedData()['Encabezado']['Emisor']
360
            );
361
            $bag->setEmisor($emisor);
362
        }
363
364
        // Crear a partir de los datos parseados.
365
        elseif ($bag->getParsedData()) {
366
            $emisor = $this->emisorFactory->create(
367
                $bag->getParsedData()['Encabezado']['Emisor']
368
            );
369
            $bag->setEmisor($emisor);
370
        }
371
    }
372
373
    /**
374
     * Asegura que existan el emisor en la bolsa si existen datos
375
     * normalizados para poder determinarlo y no está asignado previamente.
376
     *
377
     * El emisor también se pueden crear si existe un XmlDocument o un DTE.
378
     *
379
     * Requiere: $bag->getNormalizedData() o $bag->getXmlDocument() o $bag->getDocument().
380
     *
381
     * @param DocumentBagInterface $bag
382
     * @return void
383
     */
384 53
    protected function ensureReceptor(DocumentBagInterface $bag): void
385
    {
386
        // Si ya está asignado el receptor no se hace nada.
387 53
        if ($bag->getReceptor()) {
388 52
            return;
389
        }
390
391
        // Crear a partir de los datos del DTE (sería lo más normal).
392 53
        if ($bag->getDocument()) {
393 53
            $emisor = $this->receptorFactory->create(
394 53
                $bag->getDocument()->getReceptor()
395 53
            );
396 53
            $bag->setReceptor($emisor);
397
        }
398
399
        // Crear a partir de los datos del XmlDocument.
400
        elseif ($bag->getXmlDocument()) {
401
            $emisor = $this->receptorFactory->create(
402
                $bag->getXmlDocument()->query(
0 ignored issues
show
Bug introduced by
It seems like $bag->getXmlDocument()->...'/Encabezado/Receptor') can also be of type null and string; however, parameter $data of libredte\lib\Core\Packag...toryInterface::create() 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

402
                /** @scrutinizer ignore-type */ $bag->getXmlDocument()->query(
Loading history...
403
                    '/Encabezado/Receptor'
404
                )
405
            );
406
            $bag->setReceptor($emisor);
407
        }
408
409
        // Crear a partir de los datos normalizados.
410
        elseif ($bag->getNormalizedData()) {
411
            $emisor = $this->receptorFactory->create(
412
                $bag->getNormalizedData()['Encabezado']['Receptor']
413
            );
414
            $bag->setReceptor($emisor);
415
        }
416
417
        // Crear a partir de los datos parseados.
418
        elseif ($bag->getParsedData()) {
419
            $emisor = $this->receptorFactory->create(
420
                $bag->getParsedData()['Encabezado']['Receptor']
421
            );
422
            $bag->setReceptor($emisor);
423
        }
424
    }
425
}
426