PlantillaDOM::getInvoiceTotalsPartial()   F
last analyzed

Complexity

Conditions 17
Paths 257

Size

Total Lines 91
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 17
eloc 66
nc 257
nop 3
dl 0
loc 91
rs 3.6708
c 1
b 0
f 0

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
 * Copyright (C) 2025 Joe Nilson <[email protected]>
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Lesser General Public License as
7
 * published by the Free Software Foundation, either version 3 of the
8
 * License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU Lesser General Public License for more details.
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
namespace FacturaScripts\Plugins\fsRepublicaDominicana\Lib\PlantillasPDF;
19
20
use DeepCopy\DeepCopy;
0 ignored issues
show
Bug introduced by
The type DeepCopy\DeepCopy was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use FacturaScripts\Core\DataSrc\Impuestos;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Core\DataSrc\Impuestos was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use FacturaScripts\Core\Model\Base\BusinessDocument;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Core\Model\Base\BusinessDocument was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use FacturaScripts\Dinamic\Lib\PlantillasPDF\Helper\PaymentMethodBankDataHelper;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Dinamic\L...entMethodBankDataHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use FacturaScripts\Dinamic\Lib\PlantillasPDF\Helper\ReceiptBankDataHelper;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Dinamic\L...r\ReceiptBankDataHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use FacturaScripts\Dinamic\Model\AgenciaTransporte;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Dinamic\Model\AgenciaTransporte was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use FacturaScripts\Dinamic\Model\Agente;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Dinamic\Model\Agente was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
use FacturaScripts\Dinamic\Model\Contacto;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Dinamic\Model\Contacto was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use FacturaScripts\Plugins\PlantillasPDF\Lib\PlantillasPDF\BaseTemplate;
0 ignored issues
show
Bug introduced by
The type FacturaScripts\Plugins\P...ntillasPDF\BaseTemplate was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
use FacturaScripts\Core\Tools;
30
31
class PlantillaDOM extends BaseTemplate
32
{
33
34
    public function addInvoiceFooter($model): void
35
    {
36
        $i18n = Tools::lang();
0 ignored issues
show
Unused Code introduced by
The assignment to $i18n is dead and can be removed.
Loading history...
37
    }
38
39
    public function addInvoiceHeader($model): void
40
    {
41
        // TODO: Implement addInvoiceHeader() method.
42
        $html = $this->getInvoiceHeaderBilling($model)
43
            . $this->getInvoiceHeaderShipping($model);
44
        $this->writeHTML('<table class="table-big table-border"><tr>' . $html . '</tr></table><br/>');
45
    }
46
47
    public function addInvoiceLines($model): void
48
    {
49
        $lines = $model->getLines();
50
        $this->autoHideLineColumns($lines);
51
52
        $tHead = '<thead><tr>';
53
        foreach ($this->getInvoiceLineFields() as $field) {
54
            $tHead .= '<th class="' . $field['css'] . '" align="' . $field['align'] . '">' . $field['title'] . '</th>';
55
        }
56
        $tHead .= '</tr></thead>';
57
58
        $tBody = '';
59
        $numLinea = 1;
60
        $tLines = [];
61
        foreach ($lines as $line) {
62
            $tLines[] = $line;
63
            $line->numlinea = $numLinea;
64
            $tBody .= '<tr>';
65
            foreach ($this->getInvoiceLineFields() as $field) {
66
                $tBody .= '<td class="' . $field['css'] . '" align="' . $field['align'] . '" valign="top">' . $this->getInvoiceLineValue($model, $line, $field) . '</td>';
67
            }
68
            $tBody .= '</tr>';
69
            $numLinea++;
70
71
            if (property_exists($line, 'salto_pagina') && $line->salto_pagina) {
72
                $this->writeHTML('<div class="table-lines"><table class="table-big table-list">' . $tHead . $tBody . '</table></div>');
73
                $this->writeHTML($this->getInvoiceTotalsPartial($model, $tLines, 'mt-20'));
74
                $this->mpdf->AddPage();
75
                $tBody = '';
76
            }
77
        }
78
79
        $this->writeHTML('<div class="table-lines"><table class="table-big table-list">' . $tHead . $tBody . '</table></div>');
80
81
        // clonamos el documento y añadimos los totales para ver si salta de página
82
        $copier = new DeepCopy();
83
        $clonedPdf = $copier->copy($this->mpdf);
84
        $clonedPdf->writeHTML($this->getInvoiceTotalsFinal($model, 'mt-20'));
85
86
        // comprobamos si clonedPdf tiene más páginas que el original
87
        if (count($clonedPdf->pages) > count($this->mpdf->pages)) {
88
            $this->mpdf->AddPage();
89
        }
90
91
        // si tiene las mismas páginas, añadimos los totales
92
        $this->writeHTML($this->getInvoiceTotalsFinal($model, 'mt-20'));
93
    }
94
95
    protected function css(): string
96
    {
97
        return parent::css()
98
            . '.title {border-bottom: 2px solid ' . $this->get('color1') . ';}'
99
            . '.table-border {border-top: 1px solid ' . $this->get('color1') . '; border-bottom: 1px solid ' . $this->get('color1') . ';}'
100
            . '.table-dual {border-top: 1px solid ' . $this->get('color1') . '; border-bottom: 1px solid ' . $this->get('color1') . ';}'
101
            . '.table-list {border-spacing: 0px; border-top: 1px solid ' . $this->get('color1') . '; border-bottom: 1px solid ' . $this->get('color1') . ';}'
102
            . '.table-list tr:nth-child(even) {background-color: ' . $this->get('color3') . ';}'
103
            . '.table-list th {background-color: ' . $this->get('color1') . '; color: ' . $this->get('color2') . '; padding: 5px; text-transform: uppercase;}'
104
            . '.table-list td {padding: 5px;}'
105
            . '.thanks-title {'
106
            . 'font-size: ' . $this->get('titlefontsize') . 'px; font-weight: bold; color: ' . $this->get('color1') . '; '
107
            . 'text-align: right; width: 50%; padding: 15px; border-end: 1px solid ' . $this->get('color1') . ';'
108
            . '}'
109
            . '.color-navy {color: navy;}'
110
            . '.color-blue {color: blue;}'
111
            . '.color-template {color: ' . $this->get('color1') .';}'
112
            . '.thanks-text {padding: 15px;}'
113
            . '.imagetext {margin-top: 15px; text-align: ' . $this->get('endalign') . ';}'
114
            . '.imagefooter {text-align: ' . $this->get('footeralign') . ';}';
115
    }
116
117
    protected function getSubjectIdFiscalStr($model): string
118
    {
119
        return empty($model->cifnif) ? '' : '<b>' . $model->getSubject()->tipoidfiscal . '</b>: ' . $model->cifnif;
120
    }
121
122
    protected function getInvoiceHeaderBilling($model): string
123
    {
124
        if ($this->format->hidebillingaddress) {
125
            return '';
126
        }
127
128
        $subject = $model->getSubject();
129
        $address = isset($model->codproveedor) && !isset($model->direccion) ? $subject->getDefaultAddress() : $model;
130
        $customerCode = $this->get('showcustomercode') ? $model->subjectColumnValue() : '';
131
        $customerEmail = $this->get('showcustomeremail') && !empty($subject->email) ? '<br>' . Tools::lang()->trans('email') . ': ' . $subject->email : '';
132
        $break = empty($model->cifnif) ? '' : '<br/>';
133
        return '<td align="left" valign="top">'
134
            . '<br/><b> ' . $this->getSubjectTitle($model) . ':</b> ' . $customerCode
135
            . $this->getSubjectName($model) . $break . $this->getSubjectIdFiscalStr($model)
136
            . '<br/><b>' . Tools::lang()->trans('address'). ':</b> ' .$this->combineAddress($address) . $this->getInvoiceHeaderBillingPhones($subject)
137
            . '<br/><b>' . Tools::lang()->trans('email'). ':</b> ' .$customerEmail
138
            . '<br/>'
139
            . '</td>';
140
    }
141
142
    protected function getInvoiceHeaderShipping($model): string
143
    {
144
        if ($this->format->hideshippingaddress) {
145
            return '';
146
        }
147
148
        $contacto = new Contacto();
149
        if ($this->get('hideshipping') ||
150
            !isset($model->idcontactoenv) ||
151
            empty($model->idcontactoenv) ||
152
            $model->idcontactoenv == $model->idcontactofact ||
153
            false === $contacto->load($model->idcontactoenv)) {
154
            return '';
155
        }
156
157
        return '<td><b>' . Tools::lang()->trans('shipping-address') . '</b>'
158
            . '<br/>' . $this->combineAddress($contacto, true) . '</td>';
159
    }
160
161
    protected function getInvoiceHeaderBillingPhones($subject): string
162
    {
163
        if (true !== $this->get('showcustomerphones')) {
164
            return '';
165
        }
166
167
        $strPhones = $this->getPhones($subject->telefono1, $subject->telefono2);
168
        if (empty($strPhones)) {
169
            return '';
170
        }
171
172
        return '<br/>' . $strPhones;
173
    }
174
175
    protected function headerLeft(): string
176
    {
177
        $i18n = Tools::lang();
178
        $contactData = [];
179
        foreach (['telefono1', 'telefono2', 'email', 'web'] as $field) {
180
            if ($this->empresa->{$field}) {
181
                $contactData[] = $this->empresa->{$field};
182
            }
183
        }
184
185
        $title = $this->showHeaderTitle ? '<h1 class="title">' . $this->get('headertitle') . '</h1>' . $this->spacer() : '';
186
        if ($this->isSketchInvoice()) {
187
            $title .= '<div class="color-red font-big font-bold">' . Tools::lang()->trans('invoice-is-sketch') . '</div>';
188
        }
189
190
        $descTC = ($this->headerModel->tipocomprobante < 30) ? "<b>NCF:</b> " : "<b>e-NCF:</b> ";
191
        $fechaVencimiento = ($this->headerModel->ncffechavencimiento !== null) ? '<b>Fecha Vencimiento:</b> ' . $this->headerModel->ncffechavencimiento : '';
192
        $fechaEmision = '<b>Fecha Emisi&oacute;n:</b> ' . $this->headerModel->fecha;
193
194
        return '<table class="table-big">'
195
            . '<tr>'
196
                . '<td valign="top"><img src="' . $this->logoPath . '" height="' . $this->get('logosize') . '"/>' . '</td>'
197
                . '<td align="right" valign="top">' . $title . '</td>'
198
            . '</tr>'
199
            . '<tr>'
200
                . '<td align="left" valign="top">'
201
                . '<p><b>' . $this->empresa->nombre . '</b>'
202
                . '<br/>' . $this->empresa->tipoidfiscal . ': ' . $this->empresa->cifnif
203
                . '<br/>' . Tools::lang()->trans('address') . ': ' . $this->combineAddress($this->empresa) . '</p>'
204
                . '<p>' . implode(' · ', $contactData) . '</p>'
205
                . $fechaEmision
206
                . '</td>'
207
                . '<td align="right" valign="top">'
208
                .  '<div class="color-template font-big font-bold">' . $this->headerModel->descripcionTipoComprobante() . '</div>'
209
                .  $descTC
210
                .  $this->headerModel->numeroncf . '<br/>'
211
                .  $fechaVencimiento . '<br/>'
212
                .  '<b>' . $i18n->trans('payment-method') . ':</b> '
213
                .  PaymentMethodBankDataHelper::get($this->headerModel)
214
                . '</td>'
215
            . '</tr>'
216
            . '</table>';
217
    }
218
    protected function headerRight(): string
219
    {
220
        $contactData = [];
221
        foreach (['telefono1', 'telefono2', 'email', 'web'] as $field) {
222
            if ($this->empresa->{$field}) {
223
                $contactData[] = $this->empresa->{$field};
224
            }
225
        }
226
227
        $title = $this->showHeaderTitle ? '<h1 class="title">' . $this->get('headertitle') . '</h1>' . $this->spacer() : '';
228
        if ($this->isSketchInvoice()) {
229
            $title .= '<div class="color-red font-big font-bold">' . Tools::lang()->trans('invoice-is-sketch') . '</div>';
230
        }
231
232
        return '<table class="table-big">'
233
            . '<tr>'
234
            . '<td> Titulo ' . $title
235
            . '<p><b>' . $this->empresa->nombre . '</b>'
236
            . '<br/>' . $this->empresa->tipoidfiscal . ': ' . $this->empresa->cifnif
237
            . '<br/>' . $this->combineAddress($this->empresa) . '</p>' . $this->spacer()
238
            . '<p>' . implode(' · ', $contactData) . '</p>'
239
            . '</td>'
240
            . '<td align="right"><img src="' . $this->logoPath . '" height="' . $this->get('logosize') . '"/></td>'
241
            . '</tr>'
242
            . '</table>';
243
    }
244
245
    protected function getInvoiceTotalsFinal($model, $css = ''): string
246
    {
247
        $observations = '';
248
        if (!empty($this->getObservations($model))) {
249
            $observations .= '<p><b>' . Tools::lang()->trans('observations') . '</b><br/>'
250
                . $this->getObservations($model) . '</p>';
251
        }
252
253
        return $this->format->hidetotals ?
254
            $observations :
255
            $this->getInvoiceTotalsPartial($model, [], $css) . $observations;
256
    }
257
258
    protected function getInvoiceTotalsPartial($model, array $lines = [], string $css = ''): string
259
    {
260
        if ($this->format->hidetotals) {
261
            return '';
262
        }
263
264
        $i18n = Tools::lang();
265
        $trs = '';
266
        $fields = [
267
            'netosindto' => $i18n->trans('subtotal'),
268
            'dtopor1' => $i18n->trans('global-dto'),
269
            'dtopor2' => $i18n->trans('global-dto-2'),
270
            'neto' => $i18n->trans('net'),
271
            'totaliva' => $i18n->trans('taxes'),
272
            'totalrecargo' => $i18n->trans('re'),
273
            'totalirpf' => $i18n->trans('retention'),
274
            'totalsuplidos' => $i18n->trans('supplied-amount'),
275
            'total' => $i18n->trans('total'),
276
        ];
277
278
        $lines = empty($lines) ? $model->getLines() : $lines;
279
        $this->getTotalsModel($model, $lines);
280
        $taxes = $this->getTaxesRows($model, $lines);
281
        $irpfs = $this->format->hide_breakdowns ? [] : $this->getIrpfs($model, $lines);
282
283
        // pintamos los irpfs
284
        foreach ($irpfs as $irpf) {
285
            $trs .= '<tr>'
286
                . '<td align="right"><b>' . $irpf['name'] . '</b>:</td>'
287
                . '<td class="nowrap" align="right">' . Tools::money($irpf['total'], $model->coddivisa) . '</td>'
288
                . '</tr>';
289
        }
290
291
        // ocultamos el neto si no hay impuestos o si hay un impuesto y el neto es igual al neto sin dto
292
        if (empty($taxes['iva']) || (count($taxes['iva']) == 1 && $model->neto == $model->netosindto)) {
293
            unset($fields['neto']);
294
            unset($fields['totaliva']);
295
        }
296
297
        // si tenemos marcada la opción de ocultar desgloses, eliminamos todos los campos excepto el total
298
        if ($this->format->hide_breakdowns) {
299
            $fields = ['total' => $i18n->trans('total')];
300
        }
301
302
        foreach ($fields as $key => $title) {
303
            if (empty($model->{$key}) || $key === 'totalirpf') {
304
                continue;
305
            }
306
307
            switch ($key) {
308
                case 'dtopor1':
309
                case 'dtopor2':
310
                    $trs .= '<tr>'
311
                        . '<td align="right"><b>' . $title . '</b>:</td>'
312
                        . '<td class="nowrap" align="right">' . Tools::number($model->{$key}) . '%</td>'
313
                        . '</tr>';
314
                    break;
315
316
                case 'total':
317
                    $trs .= '<tr>'
318
                        . '<td class="text-end"><b>' . $title . '</b>:</td>'
319
                        . '<td class="text-end nowrap">' . Tools::money($model->{$key}, $model->coddivisa) . '</td>'
320
                        . '</tr>';
321
                    break;
322
323
                case 'netosindto':
324
                    if ($model->netosindto == $model->neto) {
325
                        break;
326
                    }
327
                // no break
328
                default:
329
                    $trs .= '<tr>'
330
                        . '<td align="right"><b>' . $title . '</b>:</td>'
331
                        . '<td class="nowrap" align="right">' . Tools::money($model->{$key}, $model->coddivisa) . '</td>'
332
                        . '</tr>';
333
                    break;
334
            }
335
        }
336
337
        return '<table class="table-big table-border ' . $css . '">'
338
            . '<tr>'
339
            . '<td> ' . $this->getInvoiceTaxes($model, $lines) . '</td>'
340
            . '<td align="right" valign="top"><table>' . $trs . '</table></td>'
341
            . '</tr>'
342
            . '<tr>'
343
            . '<td>'
344
            . '</td>'
345
            . '</tr>'
346
            . '</table>'
347
            . '&nbsp;<b>Son:</b> ' .  $this->convert_to_words($model->total) . ' pesos dominicanos'
348
            . '<br/>';
349
    }
350
351
    protected function getInvoiceTaxes($model, array $lines, string $class = 'table-big'): string
352
    {
353
        if ($this->format->hide_vat_breakdown) {
354
            return '';
355
        }
356
357
        $taxes = $this->getTaxesRows($model, $lines);
358
        if (empty($taxes['iva']) && empty($model->totalirpf)) {
359
            return '';
360
        }
361
362
        $i18n = Tools::lang();
363
364
        $trs = '';
365
        foreach ($taxes['iva'] as $row) {
366
            $trs .= '<tr>'
367
                . '<td class="nowrap" align="left">' . Impuestos::get($row['codimpuesto'])->descripcion . '</td>'
368
                . '<td class="nowrap" align="center">' . Tools::money($row['neto'], $model->coddivisa) . '</td>'
369
                . '<td class="nowrap" align="center">' . Tools::number($row['iva']) . '%</td>'
370
                . '<td class="nowrap" align="center">' . Tools::money($row['totaliva'], $model->coddivisa) . '</td>';
371
372
            if (empty($model->totalrecargo)) {
373
                $trs .= '</tr>';
374
                continue;
375
            }
376
377
            $trs .= '<td class="nowrap" align="center">' . (empty($row['recargo']) ? '-' : Tools::number($row['recargo']) . '%') . '</td>'
378
                . '<td class="nowrap" align="right">' . (empty($row['totalrecargo']) ? '-' : Tools::money($row['totalrecargo'])) . '</td>'
379
                . '</tr>';
380
        }
381
382
        if (empty($model->totalrecargo)) {
383
            return '<table class="' . $class . '">'
384
                . '<thead>'
385
                . '<tr>'
386
                . '<th align="left">' . $i18n->trans('tax') . '</th>'
387
                . '<th align="center">' . $i18n->trans('tax-base') . '</th>'
388
                . '<th align="center">' . $i18n->trans('percentage') . '</th>'
389
                . '<th align="center">' . $i18n->trans('amount') . '</th>'
390
                . '</tr>'
391
                . '</thead>'
392
                . $trs
393
                . '</table>';
394
        }
395
396
        return '<table class="' . $class . '">'
397
            . '<tr>'
398
            . '<th align="left">' . $i18n->trans('tax') . '</th>'
399
            . '<th align="center">' . $i18n->trans('tax-base') . '</th>'
400
            . '<th align="center">' . $i18n->trans('tax') . '</th>'
401
            . '<th align="center">' . $i18n->trans('amount') . '</th>'
402
            . '<th align="center">' . $i18n->trans('re') . '</th>'
403
            . '<th align="right">' . $i18n->trans('amount') . '</th>'
404
            . '</tr>'
405
            . $trs
406
            . '</table>';
407
    }
408
409
    private function convert_to_words($number): ?string
410
    {
411
        $words = array(
412
            0 => 'cero',
413
            1 => 'un',
414
            2 => 'dos',
415
            3 => 'tres',
416
            4 => 'cuatro',
417
            5 => 'cinco',
418
            6 => 'seis',
419
            7 => 'siete',
420
            8 => 'ocho',
421
            9 => 'nueve',
422
            10 => 'diez',
423
            11 => 'once',
424
            12 => 'doce',
425
            13 => 'trece',
426
            14 => 'catorce',
427
            15 => 'quince',
428
            16 => 'dieciséis',
429
            17 => 'diecisiete',
430
            18 => 'dieciocho',
431
            19 => 'diecinueve',
432
            20 => 'veinte',
433
            30 => 'treinta',
434
            40 => 'cuarenta',
435
            50 => 'cincuenta',
436
            60 => 'sesenta',
437
            70 => 'setenta',
438
            80 => 'ochenta',
439
            90 => 'noventa',
440
            100 => 'cien',
441
            200 => 'doscientos',
442
            300 => 'trescientos',
443
            400 => 'cuatrocientos',
444
            500 => 'quinientos',
445
            600 => 'seiscientos',
446
            700 => 'setecientos',
447
            800 => 'ochocientos',
448
            900 => 'novecientos'
449
        );
450
451
        if ($number == 0) {
452
            return ucfirst($words[0]);
453
        }
454
455
        if ($number == 1) {
456
            return ucfirst("uno");
457
        }
458
459
        if ($number <= 20) {
460
            return ucfirst($words[$number]);
461
        }
462
463
        if ($number < 100) {
464
            return ucfirst($words[10 * floor($number / 10)]
465
                . ($number % 10 > 0 ? ' y ' . $words[$number % 10] : ''));
466
        }
467
468
        if ($number < 1000) {
469
            $hundreds = floor($number / 100) * 100;
470
            return ucfirst($words[$hundreds]
471
                . ($number % 100 > 0 ? ($hundreds == 100 ? 'to ' : ' ')
472
                    . $this->convert_to_words($number % 100) : ''));
473
        }
474
475
        if ($number < 1000000) {
476
            $thousands = floor($number / 1000);
477
            return ucfirst(($thousands > 1 ? $this->convert_to_words($thousands) . ' ' : '')
478
                . 'mil'
479
                . ($number % 1000 > 0 ? ' ' . $this->convert_to_words($number % 1000) : ''));
480
        }
481
482
        if ($number < 1000000000) {
483
            $millions = floor($number / 1000000);
484
            return ucfirst(($millions > 1 ? $this->convert_to_words($millions) . ' ' : 'un ')
485
                . 'millón'
486
                . ($millions > 1 ? 'es' : '')
487
                . ($number % 1000000 > 0 ? ' ' . $this->convert_to_words($number % 1000000) : ''));
488
        }
489
490
        $billions = floor($number / 1000000000);
491
        return ucfirst(($billions > 1 ? $this->convert_to_words($billions) . ' ' : 'un ')
492
            . 'billón'
493
            . ($billions > 1 ? 'es' : '')
494
            . ($number % 1000000000 > 0 ? ' ' . $this->convert_to_words($number % 1000000000) : ''));
495
    }
496
}