CsvBatchProcessorStrategy::setInitialDTE()   F
last analyzed

Complexity

Conditions 15
Paths 16384

Size

Total Lines 67
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
eloc 61
dl 0
loc 67
ccs 0
cts 57
cp 0
rs 1.7499
c 0
b 0
f 0
cc 15
nc 16384
nop 1
crap 240

How to fix   Long Method    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 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\BatchProcessor\Strategy\Spreadsheet;
26
27
use Derafu\Backbone\Abstract\AbstractStrategy;
28
use Derafu\Backbone\Attribute\Strategy;
29
use Derafu\Repository\Contract\RepositoryManagerInterface;
30
use Derafu\Support\Csv;
31
use Derafu\Support\Date;
32
use libredte\lib\Core\Package\Billing\Component\Document\Contract\BatchProcessorStrategyInterface;
33
use libredte\lib\Core\Package\Billing\Component\Document\Contract\DocumentBatchInterface;
34
use libredte\lib\Core\Package\Billing\Component\Document\Entity\AduanaMoneda;
35
use libredte\lib\Core\Package\Billing\Component\Document\Exception\BatchProcessorException;
36
37
/**
38
 * Estrategia "billing.document.batch_processor.strategy:spreadsheet.csv".
39
 *
40
 * Procesa en lote los documentos tributarios de un archivo CSV con el formato
41
 * estándar de LibreDTE.
42
 */
43
#[Strategy(name: 'spreadsheet.csv', worker: 'batch_processor', component: 'document', package: 'billing')]
44
class CsvBatchProcessorStrategy extends AbstractStrategy implements BatchProcessorStrategyInterface
45
{
46
    /**
47
     * Constructor de la estrategia con sus dependencias.
48
     *
49
     * @param RepositoryManagerInterface $repositoryManager
50
     */
51
    public function __construct(
52
        private RepositoryManagerInterface $repositoryManager
53
    ) {
54
    }
55
56
    /**
57
     * {@inheritDoc}
58
     */
59
    public function process(DocumentBatchInterface $batch): array
60
    {
61
        // Cargar archivo CSV y obtener los datos.
62
        $datos = Csv::read($batch->getFile());
63
        $n_datos = count($datos);
64
        $documentos = [];
65
        $documento = [];
66
67
        // Procesar cada fila del archivo.
68
        for ($i = 1; $i < $n_datos; $i++) {
69
            // Si la fila corresponde a un documento nuevo.
70
            if (!empty($datos[$i][0])) {
71
                // Agregar el documento actual al listado si existe.
72
                if ($documento) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $documento of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
73
                    $documentos[] = $documento;
74
                }
75
                // Crear un nuevo documento.
76
                $documento = $this->createDocument($datos[$i]);
77
            } else {
78
                // Si la fila no corresponde a un documento nuevo, agregar
79
                // detalles al documento actual.
80
                if (!empty($datos[$i][13])) {
81
                    $datosItem = array_merge(
82
                        // Datos originales del item (vienen juntos en el
83
                        // archivo).
84
                        array_slice($datos[$i], 11, 8),
85
86
                        // Datos adicionales del item (vienen después del item,
87
                        // "al final", porque se añadieron después de los
88
                        // previos al archivo).
89
                        [
90
                            // CodImpAdic.
91
                            !empty($datos[$i][38]) ? $datos[$i][38] : null,
92
                        ]
93
                    );
94
95
                    $this->addItem($documento, $datosItem);
96
                }
97
98
                // Agregar referencias al documento.
99
                $this->addReference($documento, array_slice($datos[$i], 28, 5));
100
            }
101
        }
102
103
        // Agregar el último documento procesado al listado.
104
        $documentos[] = $documento;
105
106
        return $documentos;
0 ignored issues
show
introduced by
The expression return $documentos returns an array which contains values of type array which are incompatible with the return type libredte\lib\Core\Packag...tract\DocumentInterface mandated by libredte\lib\Core\Packag...egyInterface::process().
Loading history...
107
    }
108
109
    /**
110
     * Crea un documento a partir de los datos proporcionados.
111
     *
112
     * Verifica los datos mínimos requeridos y genera la estructura base.
113
     *
114
     * También agrega ítems, transporte y referencias al documento.
115
     *
116
     * @param array $datos Datos para crear el documento. Los índices corresponden a:
117
     *   - 0: Tipo de documento (obligatorio).
118
     *   - 1: Folio del documento (obligatorio).
119
     *   - 2: Fecha de emisión (opcional).
120
     *   - 3: Fecha de vencimiento (opcional).
121
     *   - 4: RUT del receptor (obligatorio).
122
     *   - 5: Razón social del receptor (obligatoria si no es boleta).
123
     *   - 6: Giro del receptor (obligatorio si no es boleta).
124
     *   - 7: Contacto del receptor (opcional).
125
     *   - 8: Correo del receptor (opcional, validado si se proporciona).
126
     *   - 9: Dirección del receptor (obligatoria si no es boleta).
127
     *   - 10: Comuna del receptor (obligatoria si no es boleta).
128
     *   - 33: Tipo de moneda (opcional, por defecto USD si aplica).
129
     *   - 34: Número de identificación del receptor extranjero (opcional).
130
     *   - 35: Descuento global (opcional, porcentaje o monto).
131
     *   - 36: Nombre del PDF (opcional).
132
     *   - 37: Forma de pago (opcional, 1, 2 o 3).
133
     *   - 38: Código de impuesto adicional (opcional).
134
     * @return array Estructura del documento generado.
135
     * @throws BatchProcessorException Si faltan datos mínimos o son inválidos.
136
     */
137
    private function createDocument(array $datos): array
138
    {
139
        // Verificar datos mínimos obligatorios.
140
        if (empty($datos[0])) {
141
            throw new BatchProcessorException('Falta tipo de documento.');
142
        }
143
        if (empty($datos[1])) {
144
            throw new BatchProcessorException('Falta folio del documento.');
145
        }
146
        if (empty($datos[4])) {
147
            throw new BatchProcessorException('Falta RUT del receptor.');
148
        }
149
150
        // Verificar datos si no es boleta.
151
        if (!in_array($datos[0], [39, 41])) {
152
            if (empty($datos[5])) {
153
                throw new BatchProcessorException(
154
                    'Falta razón social del receptor.'
155
                );
156
            }
157
            if (empty($datos[6])) {
158
                throw new BatchProcessorException(
159
                    'Falta giro del receptor.'
160
                );
161
            }
162
            if (empty($datos[9])) {
163
                throw new BatchProcessorException(
164
                    'Falta dirección del receptor.'
165
                );
166
            }
167
            if (empty($datos[10])) {
168
                throw new BatchProcessorException(
169
                    'Falta comuna del receptor.'
170
                );
171
            }
172
        }
173
174
        // Crear la estructura base del documento.
175
        $documento = $this->setInitialDTE($datos);
176
177
        // Validar correo electrónico.
178
        if (!empty($datos[8])) {
179
            if (!filter_var($datos[8], FILTER_VALIDATE_EMAIL)) {
180
                throw new BatchProcessorException(sprintf(
181
                    'Correo electrónico %s no es válido.',
182
                    $datos[8]
183
                ));
184
            }
185
            $documento['Encabezado']['Receptor']['CorreoRecep'] = mb_substr(
186
                trim($datos[8]),
187
                0,
188
                80
189
            );
190
        }
191
192
        // Manejar tipos de moneda para documentos de exportación.
193
        if (in_array($documento['Encabezado']['IdDoc']['TipoDTE'], [110,111,112])) {
194
            // Agregar moneda.
195
            if (empty($datos[33])) {
196
                $datos[33] = 'USD';
197
            }
198
            $moneda = $this->getCurrency($datos[33]);
199
            if (empty($moneda)) {
200
                throw new BatchProcessorException(
201
                    sprintf(
202
                        'El tipo de moneda %s no está permitido, solo: USD, EUR y CLP.',
203
                        $datos[33]
204
                    )
205
                );
206
            }
207
            $documento['Encabezado']['Totales']['TpoMoneda'] = $moneda;
208
209
            // Agregar ID del receptor.
210
            if (!empty($datos[34])) {
211
                $documento['Encabezado']['Receptor']['Extranjero']['NumId'] = mb_substr(
212
                    trim($datos[34]),
213
                    0,
214
                    20
215
                );
216
            }
217
        }
218
219
        // Procesar descuentos globales.
220
        if (!empty($datos[35])) {
221
            if (strpos($datos[35], '%')) {
222
                $TpoValor_global = '%';
223
                $ValorDR_global = (float)substr($datos[35], 0, -1);
224
            } else {
225
                $TpoValor_global = '$';
226
                $ValorDR_global = (float)$datos[35];
227
            }
228
            $documento['DscRcgGlobal'][] = [
229
                'TpoMov' => 'D',
230
                'TpoValor' => $TpoValor_global,
231
                'ValorDR' => $ValorDR_global,
232
                'IndExeDR' => 1,
233
            ];
234
        }
235
236
        // Asignar el nombre del PDF si se proporciona.
237
        // Esto permite asociar un archivo PDF específico al documento.
238
        if (!empty($datos[36])) {
239
            $documento['LibreDTE']['pdf']['nombre'] = $datos[36];
240
        }
241
242
        // Procesar forma de pago.
243
        if (!empty($datos[37])) {
244
            if (!in_array($datos[37], [1, 2, 3])) {
245
                throw new BatchProcessorException(sprintf(
246
                    'Forma de pago de código %s es incorrecta, debe ser: 1 (contado), 2 (crédito) o 3 (sin costo).',
247
                    $datos[37]
248
                ));
249
            }
250
            $documento['Encabezado']['IdDoc']['FmaPago'] = (int) $datos[37];
251
        }
252
253
        // Agregar ítems, transporte y referencias.
254
        $datosItem = array_merge(
255
            // Datos originales del item (vienen juntos en el archivo).
256
            array_slice($datos, 11, 8),
257
258
            // Datos adicionales del item (vienen después del item, "al final",
259
            // porque se añadieron después de los previos al archivo)
260
            [
261
                // CodImpAdic.
262
                !empty($datos[38]) ? $datos[38] : null,
263
            ]
264
        );
265
266
        $this->addItem($documento, $datosItem);
267
        $this->addTransport($documento, array_slice($datos, 22, 6));
268
        $this->addReference($documento, array_slice($datos, 28, 5));
269
270
        return $documento;
271
    }
272
273
    /**
274
     * Genera la estructura inicial del DTE.
275
     *
276
     * Este método crea un arreglo con la estructura base del DTE, incluyendo
277
     * encabezado, emisor, receptor y detalles. Configura valores
278
     * predeterminados para los campos opcionales y procesa algunos datos de
279
     * entrada.
280
     *
281
     * @param array $datos Datos de entrada para generar la estructura del DTE.
282
     * @return array Arreglo con la estructura inicial del DTE.
283
     */
284
    private function setInitialDTE(array $datos): array
285
    {
286
        return [
287
            'Encabezado' => [
288
                'IdDoc' => [
289
                    'TipoDTE' => (int) $datos[0],
290
                    'Folio' => (int) $datos[1],
291
                    'FchEmis' => (
292
                        !empty($datos[2]) && Date::validateAndConvert($datos[2], 'Y-m-d') !== null
293
                    ) ? $datos[2] : date('Y-m-d'),
294
                    'TpoTranCompra' => false,
295
                    'TpoTranVenta' => false,
296
                    'FmaPago' => false,
297
                    'FchCancel' => false,
298
                    'PeriodoDesde' => !empty($datos[20]) && Date::validateAndConvert($datos[20], 'Y-m-d') !== null
299
                        ? $datos[20]
300
                        : false,
301
                    'PeriodoHasta' => !empty($datos[21]) && Date::validateAndConvert($datos[21], 'Y-m-d') !== null
302
                        ? $datos[21]
303
                        : false,
304
                    'MedioPago' => false,
305
                    'TpoCtaPago' => false,
306
                    'NumCtaPago' => false,
307
                    'BcoPago' => false,
308
                    'TermPagoGlosa' => !empty($datos[19])
309
                        ? mb_substr(trim($datos[19]), 0, 100)
310
                        : false,
311
                    'FchVenc' => !empty($datos[3]) && Date::validateAndConvert($datos[3], 'Y-m-d') !== null
312
                        ? $datos[3]
313
                        : false,
314
                ],
315
                'Emisor' => [
316
                    'RUTEmisor' => false,
317
                    'RznSoc' => false,
318
                    'GiroEmis' => false,
319
                    'Telefono' => false,
320
                    'CorreoEmisor' => false,
321
                    'Acteco' => false,
322
                    'CdgSIISucur' => false,
323
                    'DirOrigen' => false,
324
                    'CmnaOrigen' => false,
325
                    'CdgVendedor' => false,
326
                ],
327
                'Receptor' => [
328
                    'RUTRecep' => str_replace('.', '', $datos[4]),
329
                    'CdgIntRecep' => false,
330
                    'RznSocRecep' => !empty($datos[5])
331
                        ? mb_substr(trim($datos[5]), 0, 100)
332
                        : false,
333
                    'GiroRecep' => !empty($datos[6])
334
                        ? mb_substr(trim($datos[6]), 0, 40)
335
                        : false,
336
                    'Contacto' => !empty($datos[7])
337
                        ? mb_substr(trim($datos[7]), 0, 80)
338
                        : false,
339
                    'CorreoRecep' => false,
340
                    'DirRecep' => !empty($datos[9])
341
                        ? mb_substr(trim($datos[9]), 0, 70)
342
                        : false,
343
                    'CmnaRecep' => !empty($datos[10])
344
                        ? mb_substr(trim($datos[10]), 0, 20)
345
                        : false,
346
                    'CiudadRecep' => false,
347
                ],
348
                'RUTSolicita' => false,
349
            ],
350
            'Detalle' => [],
351
        ];
352
353
    }
354
355
    /**
356
     * Agrega un ítem al documento.
357
     *
358
     * Procesa los datos de un ítem y lo agrega al arreglo de detalles. Valida
359
     * que los campos mínimos estén presentes y ajusta la longitud de los datos.
360
     *
361
     * @param array &$documento Documento al que se agregará el ítem. Modificado
362
     * directamente.
363
     * @param array $item  Datos del ítem. Los índices corresponden a:
364
     *   - 0: Código del ítem (opcional).
365
     *   - 1: Indicador de exención (opcional).
366
     *   - 2: Nombre del ítem (obligatorio).
367
     *   - 3: Descripción del ítem (opcional).
368
     *   - 4: Cantidad del ítem (obligatorio).
369
     *   - 5: Unidad de medida (opcional).
370
     *   - 6: Precio del ítem (obligatorio).
371
     *   - 7: Descuento (opcional, porcentaje o monto).
372
     *   - 8: Código de impuesto adicional (opcional).
373
     * @return void
374
     * @throws BatchProcessorException Si faltan datos obligatorios.
375
     */
376
    private function addItem(array &$documento, array $item): void
377
    {
378
        // Verificar datos mínimos obligatorios.
379
        if (empty($item[2])) {
380
            throw new BatchProcessorException(
381
                'Falta nombre del item.'
382
            );
383
        }
384
        if (empty($item[4])) {
385
            throw new BatchProcessorException(
386
                'Falta cantidad del item.'
387
            );
388
        }
389
        if (empty($item[6])) {
390
            throw new BatchProcessorException(
391
                'Falta precio del item.'
392
            );
393
        }
394
395
        // Crear el detalle del ítem.
396
        $detalle = [
397
            'NmbItem' => mb_substr(trim($item[2]), 0, 80),
398
            'QtyItem' => (float)str_replace(',', '.', $item[4]),
399
            'PrcItem' => (float)str_replace(',', '.', $item[6]),
400
        ];
401
402
        // Agregar código del ítem si está presente.
403
        if (!empty($item[0])) {
404
            $detalle['CdgItem'] = [
405
                'TpoCodigo' => 'INT1',
406
                'VlrCodigo' => mb_substr(trim($item[0]), 0, 35),
407
            ];
408
        }
409
410
        // Agregar indicador de exención si está presente.
411
        if (!empty($item[1])) {
412
            $detalle['IndExe'] = (int)$item[1];
413
        }
414
415
        // Agregar descripción del ítem si está presente.
416
        if (!empty($item[3])) {
417
            $detalle['DscItem'] = mb_substr(trim($item[3]), 0, 1000);
418
        }
419
420
        // Agregar unidad de medida si está presente.
421
        if (!empty($item[5])) {
422
            $detalle['UnmdItem'] = mb_substr(trim($item[5]), 0, 4);
423
        }
424
425
426
        // Procesar y agregar descuento si está presente.
427
        if (!empty($item[7])) {
428
            if (strpos($item[7], '%')) {
429
                $detalle['DescuentoPct'] = (float)substr($item[7], 0, -1);
430
            } else {
431
                $detalle['DescuentoMonto'] = (float)$item[7];
432
            }
433
        }
434
435
        // Agregar código de impuesto adicional si está presente.
436
        if (!empty($item[8])) {
437
            $detalle['CodImpAdic'] = (int)trim($item[8]);
438
        }
439
440
        // Agregar el detalle al documento.
441
        $documento['Detalle'][] = $detalle;
442
    }
443
444
    /**
445
     * Agrega información de transporte a un documento.
446
     *
447
     * Procesa los datos de transporte proporcionados y los agrega al arreglo
448
     * `Transporte` dentro del documento. Los datos incluyen información de
449
     * patente, transportista, chofer y destino.
450
     *
451
     * @param array &$documento Documento al que se agregará la información de
452
     * transporte. Se pasa por referencia para modificarlo.
453
     * @param array $transporte Datos de transporte a procesar. Los índices son:
454
     *   - 0: Patente del vehículo (opcional).
455
     *   - 1: RUT del transportista (opcional).
456
     *   - 2: RUT del chofer (opcional).
457
     *   - 3: Nombre del chofer (opcional).
458
     *   - 4: Dirección del destino (opcional).
459
     *   - 5: Comuna del destino (opcional).
460
     * @return void Modifica el documento directamente.
461
     */
462
    private function addTransport(array &$documento, array $transporte): void
463
    {
464
        $vacios = true;
465
466
        // Verificar si todos los datos de transporte están vacíos.
467
        foreach ($transporte as $t) {
468
            if (!empty($t)) {
469
                $vacios = false;
470
            }
471
        }
472
        if ($vacios) {
473
            return;
474
        }
475
476
        // Procesar cada dato de transporte y agregarlo al documento si está
477
        // presente.
478
        if ($transporte[0]) {
479
            $documento['Encabezado']['Transporte']['Patente'] = mb_substr(
480
                trim($transporte[0]),
481
                0,
482
                8
483
            );
484
        }
485
        if ($transporte[1]) {
486
            $documento['Encabezado']['Transporte']['RUTTrans'] = mb_substr(
487
                str_replace('.', '', trim($transporte[1])),
488
                0,
489
                10
490
            );
491
        }
492
        if ($transporte[2] && $transporte[3]) {
493
            $documento['Encabezado']['Transporte']['Chofer']['RUTChofer'] =
494
                mb_substr(
495
                    str_replace('.', '', trim($transporte[2])),
496
                    0,
497
                    10
498
                )
499
            ;
500
            $documento['Encabezado']['Transporte']['Chofer']['NombreChofer'] =
501
                mb_substr(
502
                    trim($transporte[3]),
503
                    0,
504
                    30
505
                )
506
            ;
507
        }
508
        if ($transporte[4]) {
509
            $documento['Encabezado']['Transporte']['DirDest'] = mb_substr(
510
                trim($transporte[4]),
511
                0,
512
                70
513
            );
514
        }
515
        if ($transporte[5]) {
516
            $documento['Encabezado']['Transporte']['CmnaDest'] = mb_substr(
517
                trim($transporte[5]),
518
                0,
519
                20
520
            );
521
        }
522
    }
523
524
    /**
525
     * Agrega una referencia a un documento.
526
     *
527
     * Procesa los datos de referencia y los agrega al arreglo `Referencia`
528
     * dentro del documento. Valida los campos obligatorios y ajusta su longitud
529
     * si es necesario.
530
     *
531
     * @param array &$documento Documento al que se agregará la referencia.
532
     * Se pasa por referencia para modificarlo.
533
     * @param array $referencia Datos de la referencia a agregar. Los índices
534
     * deben ser:
535
     *   - 0: Tipo del documento referenciado (obligatorio).
536
     *   - 1: Folio del documento referenciado (obligatorio).
537
     *   - 2: Fecha del documento en formato AAAA-MM-DD (obligatorio).
538
     *   - 3: Código de referencia (opcional).
539
     *   - 4: Razón de la referencia (opcional).
540
     * @return void Modifica el documento directamente.
541
     * @throws BatchProcessorException Si algún campo obligatorio está vacío o
542
     * no es válido.
543
     */
544
    private function addReference(array &$documento, array $referencia): void
545
    {
546
        $Referencia = [];
547
        $vacios = true;
548
        foreach ($referencia as $r) {
549
            if (!empty($r)) {
550
                $vacios = false;
551
            }
552
        }
553
        if ($vacios) {
554
            return;
555
        }
556
        if (empty($referencia[0])) {
557
            throw new BatchProcessorException(
558
                'Tipo del documento de referencia no puede estar vacío.'
559
            );
560
        }
561
        $Referencia['TpoDocRef'] = mb_substr(trim($referencia[0]), 0, 3);
562
        if (empty($referencia[1])) {
563
            throw new BatchProcessorException(
564
                'Folio del documento de referencia no puede estar vacío.'
565
            );
566
        }
567
        $Referencia['FolioRef'] = mb_substr(trim($referencia[1]), 0, 18);
568
569
        if (
570
            empty($referencia[2])
571
            && Date::validateAndConvert($referencia[2], 'Y-m-d') !== null
572
        ) {
573
            throw new BatchProcessorException(
574
                'Fecha del documento de referencia debe ser en formato AAAA-MM-DD.'
575
            );
576
        }
577
        $Referencia['FchRef'] = $referencia[2];
578
        if (!empty($referencia[3])) {
579
            $Referencia['CodRef'] = (int) $referencia[3];
580
        }
581
        if (!empty($referencia[4])) {
582
            $Referencia['RazonRef'] = mb_substr(trim($referencia[4]), 0, 90);
583
        }
584
        $documento['Referencia'][] = $Referencia;
585
    }
586
587
    /**
588
     * Obtiene la glosa de una moneda a partir de su código ISO.
589
     *
590
     * Este método busca en el repositorio de la entidad `AduanaMoneda` un
591
     * registro que coincida con el código ISO proporcionado. Si encuentra un
592
     * resultado, devuelve la glosa asociada; de lo contrario, retorna `null`.
593
     *
594
     * @param string $moneda Código ISO de la moneda que se desea buscar.
595
     * @return string|null La glosa de la moneda o `null` si no existe.
596
     */
597
    private function getCurrency(string $moneda): ?string
598
    {
599
        // Buscar la moneda a través del repositorio.
600
        $result = $this->repositoryManager
601
            ->getRepository(AduanaMoneda::class)
602
            ->findBy([
603
                'codigo_iso' => $moneda,
604
            ]);
605
606
        // Retornar null si no se encuentra ningún resultado.
607
        if (empty($result)) {
608
            return null;
609
        }
610
611
        // Retornar la glosa de la primera coincidencia.
612
        return $result[0]->getGlosa();
613
    }
614
}
615