Issues (232)

Lib/PlantillasPDF/PlantillaDOM.php (10 issues)

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