Passed
Push — master ( 63d1f8...7c50e7 )
by Joe Nilson
02:33
created

NumberToLetterConverter   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 114
c 1
b 0
f 0
dl 0
loc 185
rs 10
wmc 25

3 Methods

Rating   Name   Duplication   Size   Complexity  
A convertGroup() 0 20 6
F convertNumber() 0 61 17
A to_word() 0 15 2
1
<?php
2
/*
3
 * Copyright (C) 2021 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
/**
19
 * Clase que implementa un conversor de números a letras.
20
 * @author AxiaCore S.A.S
21
 * https://github.com/axiacore/number-to-letter-php
22
 */
23
class NumberToLetterConverter
24
{
25
    private $UNIDADES = array(
26
        '',
27
        'UN ',
28
        'DOS ',
29
        'TRES ',
30
        'CUATRO ',
31
        'CINCO ',
32
        'SEIS ',
33
        'SIETE ',
34
        'OCHO ',
35
        'NUEVE ',
36
        'DIEZ ',
37
        'ONCE ',
38
        'DOCE ',
39
        'TRECE ',
40
        'CATORCE ',
41
        'QUINCE ',
42
        'DIECISEIS ',
43
        'DIECISIETE ',
44
        'DIECIOCHO ',
45
        'DIECINUEVE ',
46
        'VEINTE '
47
    );
48
49
    private $DECENAS = array(
50
        'VEINTI',
51
        'TREINTA ',
52
        'CUARENTA ',
53
        'CINCUENTA ',
54
        'SESENTA ',
55
        'SETENTA ',
56
        'OCHENTA ',
57
        'NOVENTA ',
58
        'CIEN '
59
    );
60
61
    private $CENTENAS = array(
62
        'CIENTO ',
63
        'DOSCIENTOS ',
64
        'TRESCIENTOS ',
65
        'CUATROCIENTOS ',
66
        'QUINIENTOS ',
67
        'SEISCIENTOS ',
68
        'SETECIENTOS ',
69
        'OCHOCIENTOS ',
70
        'NOVECIENTOS '
71
    );
72
73
    private $MONEDAS = [
74
        ['country' => 'Colombia', 'currency' => 'COP', 'singular' => 'PESO COLOMBIANO', 'plural' => 'PESOS COLOMBIANOS', 'symbol', '$'],
75
        ['country' => 'Estados Unidos', 'currency' => 'USD', 'singular' => 'DÓLAR', 'plural' => 'DÓLARES', 'symbol', 'US$'],
76
        ['country' => 'El Salvador', 'currency' => 'USD', 'singular' => 'DÓLAR', 'plural' => 'DÓLARES', 'symbol', 'US$'],
77
        ['country' => 'Europa', 'currency' => 'EUR', 'singular' => 'EURO', 'plural' => 'EUROS', 'symbol', '€'],
78
        ['country' => 'México', 'currency' => 'MXN', 'singular' => 'PESO MEXICANO', 'plural' => 'PESOS MEXICANOS', 'symbol', '$'],
79
        ['country' => 'Perú', 'currency' => 'PEN', 'singular' => 'NUEVO SOL', 'plural' => 'NUEVOS SOLES', 'symbol', 'S/'],
80
        ['country' => 'Reino Unido', 'currency' => 'GBP', 'singular' => 'LIBRA', 'plural' => 'LIBRAS', 'symbol', '£'],
81
        ['country' => 'Argentina', 'currency' => 'ARS', 'singular' => 'PESO', 'plural' => 'PESOS', 'symbol', '$'],
82
        ['country' => 'República Dominicana', 'currency' => 'DOP', 'singular' => 'PESO DOMINICANO', 'plural' => 'PESOS DOMINICANOS', 'symbol', 'RD$']
83
    ];
84
85
    private $separator = FS_NF2;
0 ignored issues
show
Bug introduced by
The constant FS_NF2 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
86
    private $decimal_mark = FS_NF1;
0 ignored issues
show
Bug introduced by
The constant FS_NF1 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
87
    private $glue = ' CON ';
88
89
    /**
90
     * Evalua si el número contiene separadores o decimales
91
     * formatea y ejecuta la función conversora
92
     * @param number $number número a convertir
93
     * @param string $miMoneda clave de la moneda
94
     * @return string completo
95
     */
96
    public function to_word($number, $miMoneda = null)
97
    {
98
        if (strpos($number, $this->decimal_mark) === false) {
99
            $convertedNumber = array(
100
                $this->convertNumber($number, $miMoneda, 'entero')
101
            );
102
        } else {
103
            $number = explode($this->decimal_mark, str_replace($this->separator, '', trim($number)));
104
105
            $convertedNumber = array(
106
                $this->convertNumber($number[0], $miMoneda, 'entero'),
0 ignored issues
show
Bug introduced by
$number[0] of type string is incompatible with the type double|integer expected by parameter $number of NumberToLetterConverter::convertNumber(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

106
                $this->convertNumber(/** @scrutinizer ignore-type */ $number[0], $miMoneda, 'entero'),
Loading history...
107
                $this->convertNumber($number[1], $miMoneda, 'decimal'),
108
            );
109
        }
110
        return implode($this->glue, array_filter($convertedNumber));
111
    }
112
113
    /**
114
     * Convierte número a letras
115
     * @param number $number
116
     * @param string $miMoneda
117
     * @param string $type tipo de dígito (entero/decimal)
118
     * @return string $converted string convertido
119
     */
120
    private function convertNumber($number, $miMoneda = null, string $type = 'entero')
121
    {
122
123
        $converted = '';
124
        $moneda = '';
125
        if ($miMoneda !== null) {
126
            try {
127
                $moneda = array_filter($this->MONEDAS, static function ($m) use ($miMoneda) {
128
                    return ($m['currency'] === $miMoneda);
129
                });
130
131
                $moneda = array_values($moneda);
132
133
                if (count($moneda) <= 0) {
134
                    throw new Exception("Tipo de moneda inválido");
135
                    //return;
136
                }
137
                ($number < 2 ? $moneda = $moneda[0]['singular'] : $moneda = $moneda[0]['plural']);
138
            } catch (Exception $e) {
139
                echo $e->getMessage();
140
                //return;
141
            }
142
        }
143
144
        if (($number < 0) || ($number > 999999999)) {
145
            return ($type === 'decimal')?' 00/100' : '';
146
        }
147
148
        $numberStr = (string) $number;
149
        $numberStrFill = str_pad($numberStr, 9, '0', STR_PAD_LEFT);
150
        $millones = substr($numberStrFill, 0, 3);
151
        $miles =  substr($numberStrFill, 3, 3);
152
        $cientos = substr($numberStrFill, 6);
153
154
        if ($millones > 0) {
155
            if ($millones === '001') {
156
                $converted .= 'UN MILLON ';
157
            } elseif ($millones > 0) {
158
                $converted .= sprintf('%sMILLONES ', $this->convertGroup($millones));
159
            }
160
        }
161
162
        if ($miles > 0) {
163
            if ($miles === '001') {
164
                $converted .= 'MIL ';
165
            } elseif ($miles > 0) {
166
                $converted .= sprintf('%sMIL ', $this->convertGroup($miles));
167
            }
168
        }
169
170
        if ($cientos > 0) {
171
            if ($cientos === '001') {
172
                $converted .= 'UN ';
173
            } elseif ($cientos > 0) {
174
                $converted .= sprintf('%s ', $this->convertGroup($cientos));
175
            }
176
        }
177
178
        $converted .= $moneda;
179
180
        return $converted;
181
    }
182
183
    /**
184
     * Define el tipo de representación decimal (centenas/millares/millones)
185
     * @param string $n
186
     * @return string $output
187
     */
188
    private function convertGroup($n)
189
    {
190
        $output = '';
191
192
        if ($n === '100') {
193
            $output = "CIEN ";
194
        } elseif ($n[0] !== '0') {
195
            $output = $this->CENTENAS[(int) $n[0] - 1];
196
        }
197
198
        $k = (int) substr($n, 1);
199
200
        if ($k <= 20) {
201
            $output .= $this->UNIDADES[$k];
202
        } elseif (($k > 30) && ($n[2] !== '0')) {
203
            $output .= sprintf('%sY %s', $this->DECENAS[(int)$n[1] - 2], $this->UNIDADES[(int)$n[2]]);
204
        } else {
205
            $output .= sprintf('%s%s', $this->DECENAS[(int)$n[1] - 2], $this->UNIDADES[(int)$n[2]]);
206
        }
207
        return $output;
208
    }
209
}