Completed
Push — master ( 571f3c...a59117 )
by Luis Andrés
03:00
created

EcuadorIdentification::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Luilliarcec\LaravelEcuadorIdentification\Support;
4
5
use Luilliarcec\LaravelEcuadorIdentification\Exceptions\EcuadorIdentificationException;
6
7
/**
8
 * Class to validate Ecuadorian identity card, Natural ruc, Private ruc and Public ruc
9
 *
10
 * @link https://www.sri.gob.ec/web/guest/RUC#%C2%BFc%C3%B3mo-se
11
 * @link http://www.sri.gob.ec/DocumentosAlfrescoPortlet/descargar/1ee224e6-b84b-4a8f-8127-59f8cd99ae58/LOGARITMO_VALIDA_RUC.docx
12
 * @package Luilliarcec\LaravelEcuadorIdentification\Support
13
 */
14
class EcuadorIdentification
15
{
16
    /**
17
     * Natural person ruc
18
     */
19
    const NaturalPerson = 'NaturalPerson';
20
21
    /**
22
     * Private company ruc
23
     */
24
    const PrivateCompany = 'PrivateCompany';
25
26
    /**
27
     * Public company ruc
28
     */
29
    const PublicCompany = 'PublicCompany';
30
31
    /**
32
     * Error encapsulator variable
33
     *
34
     * @var string
35
     */
36
    private $error;
37
38
    /**
39
     * Number of provinces of Ecuador
40
     *
41
     * @var \Illuminate\Config\Repository
42
     */
43
    private $provinces;
44
45
    /**
46
     * Length of the different types of identification
47
     *
48
     * @var array
49
     */
50
    private $lenght;
51
52
    /**
53
     * Billing code for identification types
54
     *
55
     * @var array
56
     */
57
    private $billingCode;
58
59
    public function __construct()
60
    {
61
        $this->provinces = config('laravel-ecuador-identification.provinces');
62
63
        $this->lenght = [
64
            'ruc' => config('laravel-ecuador-identification.type-identifications.ruc.length'),
65
            'personal-identification' => config('laravel-ecuador-identification.type-identifications.personal-identification.length'),
66
        ];
67
68
        $this->billingCode = [
69
            'personal-identification' => config('laravel-ecuador-identification.type-identifications.personal-identification.billing-code'),
70
            'ruc' => config('laravel-ecuador-identification.type-identifications.ruc.billing-code'),
71
            'final-customer' => config('laravel-ecuador-identification.type-identifications.final-customer.billing-code'),
72
        ];
73
    }
74
75
    /**
76
     * Set Error
77
     *
78
     * @param string $error
79
     */
80
    protected function setError(string $error): void
81
    {
82
        $this->error = $error;
83
    }
84
85
    /**
86
     * Get Error
87
     *
88
     * @return string
89
     */
90
    public function getError(): string
91
    {
92
        return $this->error;
93
    }
94
95
    /**
96
     * Validates the Ecuadorian Identification Card
97
     *
98
     * @param string $number Number of Identification Card
99
     * @return string|null
100
     */
101
    public function validatePersonalIdentification($number)
102
    {
103
        $this->setError('');
104
105
        try {
106
            $this->initValidation($number, $this->lenght['personal-identification']);
107
            $this->provinceCodeValidation(substr($number, 0, 2));
108
            $this->thirdDigitValidation($number[2], self::NaturalPerson);
109
            $this->moduleTen($number);
110
        } catch (EcuadorIdentificationException $e) {
111
            $this->setError($e->getMessage());
112
            return null;
113
        }
114
115
        return $this->billingCode['personal-identification'];
116
    }
117
118
    /**
119
     * Validates the Ecuadorian RUC of Natural Person
120
     *
121
     * @param string $number Number of RUC Natural Person
122
     * @return string|null
123
     */
124 View Code Duplication
    public function validateNaturalPersonRuc($number)
125
    {
126
        $this->setError('');
127
128
        try {
129
            $this->initValidation($number, $this->lenght['ruc']);
130
            $this->provinceCodeValidation(substr($number, 0, 2));
131
            $this->thirdDigitValidation($number[2], self::NaturalPerson);
132
            $this->theLastDigitsValidation(substr($number, 10, 3), self::NaturalPerson);
133
            $this->moduleTen($number);
134
        } catch (EcuadorIdentificationException $e) {
135
            $this->setError($e->getMessage());
136
            return null;
137
        }
138
139
        return $this->billingCode['ruc'];
140
    }
141
142
    /**
143
     * Validates the Ecuadorian RUC of Private Companies
144
     *
145
     * @param string $number Number of RUC Private Companies
146
     * @return string|null
147
     */
148 View Code Duplication
    public function validatePrivateCompanyRuc($number)
149
    {
150
        $this->setError('');
151
152
        try {
153
            $this->initValidation($number, $this->lenght['ruc']);
154
            $this->provinceCodeValidation(substr($number, 0, 2));
155
            $this->thirdDigitValidation($number[2], self::PrivateCompany);
156
            $this->theLastDigitsValidation(substr($number, 10, 3), self::PrivateCompany);
157
            $this->moduleEleven($number, self::PrivateCompany);
158
        } catch (EcuadorIdentificationException $e) {
159
            $this->setError($e->getMessage());
160
            return null;
161
        }
162
163
        return $this->billingCode['ruc'];
164
    }
165
166
    /**
167
     * Validates the Ecuadorian RUC of Public Companies
168
     *
169
     * @param string $number Number of RUC Public Companies
170
     * @return string|null
171
     */
172 View Code Duplication
    public function validatePublicCompanyRuc($number)
173
    {
174
        $this->setError('');
175
176
        try {
177
            $this->initValidation($number, $this->lenght['ruc']);
178
            $this->provinceCodeValidation(substr($number, 0, 2));
179
            $this->thirdDigitValidation($number[2], self::PublicCompany);
180
            $this->theLastDigitsValidation(substr($number, 9, 4), self::PublicCompany);
181
            $this->moduleEleven($number, self::PublicCompany);
182
        } catch (EcuadorIdentificationException $e) {
183
            $this->setError($e->getMessage());
184
            return null;
185
        }
186
187
        return $this->billingCode['ruc'];
188
    }
189
190
    /**
191
     * Validates the Ecuadorian Final Consumer
192
     *
193
     * @param $number
194
     * @return string|null
195
     */
196
    public function validateFinalConsumer($number)
197
    {
198
        $this->setError('');
199
200
        try {
201
            if ($number != config('laravel-ecuador-identification.final-customer.unique-value')) {
202
                throw new EcuadorIdentificationException("Field is invalid");
203
            }
204
        } catch (EcuadorIdentificationException $e) {
205
            $this->setError($e->getMessage());
206
            return null;
207
        }
208
209
        return $this->billingCode['final-customer'];
210
    }
211
212
    /**
213
     * Validate that the number belongs to natural persons.
214
     *
215
     * @param $number
216
     * @return string|null
217
     */
218
    public function validateIsNaturalPersons($number)
219
    {
220
        return $this->validatePersonalIdentification($number) !== null ?
221
            $this->validatePersonalIdentification($number) : $this->validateNaturalPersonRuc($number);
222
    }
223
224
    /**
225
     * Validate that the number belongs to juridical persons.
226
     *
227
     * @param $number
228
     * @return string|null
229
     */
230
    public function validateIsJuridicalPersons($number)
231
    {
232
        return $this->validatePrivateCompanyRuc($number) !== null ?
233
            $this->validatePrivateCompanyRuc($number) : $this->validatePublicCompanyRuc($number);
234
    }
235
236
    /**
237
     * Validate the number with all types of documents.
238
     *
239
     * @param $number
240
     * @return string|null
241
     */
242
    public function validateAllIdentificatons($number)
243
    {
244
        $result = $this->validateFinalConsumer($number);
245
246
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
247
            return $result;
248
        }
249
250
        $result = $this->validatePersonalIdentification($number);
251
252
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
253
            return $result;
254
        }
255
256
        $result = $this->validateNaturalPersonRuc($number);
257
258
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
259
            return $result;
260
        }
261
262
        $result = $this->validatePrivateCompanyRuc($number);
263
264
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
265
            return $result;
266
        }
267
268
        $result = $this->validatePublicCompanyRuc($number);
269
270
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
271
            return $result;
272
        }
273
274
        return null;
275
    }
276
277
    /**
278
     * Initial validation of the identification, not empty, only digits, not less than the given length.
279
     *
280
     * @param string $value CI or RUC
281
     * @param int $len Number of characters required
282
     * @return bool
283
     * @throws EcuadorIdentificationException When the value is empty, when the value isn't digits and
284
     * when the value doesn't have the required length
285
     */
286
    private function initValidation($value, $len)
287
    {
288
        if (empty($value)) {
289
            throw new EcuadorIdentificationException('Field must have a value.');
290
        }
291
292
        if (!ctype_digit($value)) {
293
            throw new EcuadorIdentificationException('Must be digits.');
294
        }
295
296
        if (strlen($value) != $len) {
297
            throw new EcuadorIdentificationException("Must be {$len} digits.");
298
        }
299
300
        return true;
301
    }
302
303
    /**
304
     * Validate the province code (first two numbers of CI/RUC)
305
     * The first 2 positions correspond to the province where it was issued,
306
     * so the first two numbers will not be greater than 24 or less than 1
307
     *
308
     * @param string $value First two numbers of CI/RUC
309
     * @return boolean
310
     * @throws EcuadorIdentificationException When the province code is not between 1 and 24
311
     */
312
    private function provinceCodeValidation($value)
313
    {
314
        if ($value < 1 || $value > $this->provinces) {
315
            throw new EcuadorIdentificationException("In your province code must be between 01 and {$this->provinces}.");
316
        }
317
318
        return true;
319
    }
320
321
    /**
322
     * Valid the third digit
323
     *
324
     * It allows the third digit of the document to be valid.
325
     * Depending on the type field (type of identification) validations are performed.
326
     *
327
     * NATURAL_PERSON
328
     * For Certificates and RUC of natural persons the third digit is less than 6 so
329
     * it must be between 0 and 5 (0,1,2,3,4,5)
330
     *
331
     * PRIVATE_COMPANY
332
     * For RUC of private companies the third digit must be equal to 9.
333
     *
334
     * PUBLIC_COMPANY
335
     * For RUC of public companies the third digit must be equal to 6.
336
     *
337
     * @param string $value Third digit of CI/RUC
338
     * @param string $type Type of identifier
339
     * @return boolean
340
     * @throws EcuadorIdentificationException When it does not comply with the validation according to the type of identifier
341
     */
342
    private function thirdDigitValidation($value, $type)
343
    {
344
        switch ($type) {
345
            case self::NaturalPerson:
346
                $min = config('laravel-ecuador-identification.personal-identification.third-digit.min');
347
                $max = config('laravel-ecuador-identification.personal-identification.third-digit.max');
348
                if ($value < $min || $value > $max)
349
                    throw new EcuadorIdentificationException("Field must have the third digit between {$min} and {$max}.");
350
                break;
351
352 View Code Duplication
            case self::PublicCompany:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
353
                $thirdDigit = config('laravel-ecuador-identification.public-ruc.third-digit');
354
                if ($value != $thirdDigit)
355
                    throw new EcuadorIdentificationException("Field must have the third digit equal to {$thirdDigit}.");
356
                break;
357
358 View Code Duplication
            case self::PrivateCompany:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
359
                $thirdDigit = config('laravel-ecuador-identification.private-ruc.third-digit');
360
                if ($value != $thirdDigit)
361
                    throw new EcuadorIdentificationException("Field must have the third digit equal to {$thirdDigit}.");
362
                break;
363
364
            default:
365
                throw new EcuadorIdentificationException('Field does not have this type of identification.');
366
        }
367
368
        return true;
369
    }
370
371
    /**
372
     * Validation of the last digits
373
     *
374
     * Public Ruc => 0001
375
     * Other Ruc => 001
376
     *
377
     * @param string $value The last digits
378
     * @param string $type Type of identifier
379
     * @return boolean
380
     * @throws EcuadorIdentificationException When not equal to 001
381
     */
382
    private function theLastDigitsValidation($value, $type)
383
    {
384
        switch ($type) {
385 View Code Duplication
            case self::NaturalPerson:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
386
                $lastDigits = config('laravel-ecuador-identification.natural-ruc.last-digits');
387
                if ($value != $lastDigits) {
388
                    throw new EcuadorIdentificationException("Field does not have the last digits equal to {$lastDigits}");
389
                }
390
                break;
391
392 View Code Duplication
            case self::PrivateCompany:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
393
                $lastDigits = config('laravel-ecuador-identification.private-ruc.last-digits');
394
                if ($value != $lastDigits) {
395
                    throw new EcuadorIdentificationException("Field does not have the last digits equal to {$lastDigits}");
396
                }
397
                break;
398
399 View Code Duplication
            case self::PublicCompany:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
                $lastDigits = config('laravel-ecuador-identification.public-ruc.last-digits');
401
                if ($value != $lastDigits) {
402
                    throw new EcuadorIdentificationException("Field does not have the last digits equal to {$lastDigits}");
403
                }
404
                break;
405
406
            default:
407
                throw new EcuadorIdentificationException('Field does not have this type of identification.');
408
        }
409
410
        return true;
411
    }
412
413
    /**
414
     * Module 10 Algorithm to validate if Certificates and RUC of natural person are valid.
415
     *
416
     * Coefficients used to validate the tenth digit of the Certificates,
417
     * are:  2, 1, 2, 1, 2, 1, 2, 1, 2
418
     *
419
     * Step 1: Multiply each digit of the card by the coefficient,
420
     * except for the verification digit (tenth digit),
421
     * if it is greater than 10 sums between digits. Example:
422
     *
423
     * 2  1  2  1  2  1  2  1  2  (Coefficients)
424
     * 1  7  1  0  0  3  4  0  6  (Certificate)
425
     * 2  7  2  0  0  3  8  0  [12] => continue to step 2.
426
     *
427
     * Step 2: If any of the multiplication results is greater than 10,
428
     * it is added between digits of the result. Example: [12] => 1 + 2 = Result (3)
429
     *
430
     * Step 3: The result of the multiplications is added. Example:
431
     * 2  7  2  0  0  3  8  0  3 = Result (25)
432
     *
433
     * Step 4: The result of the sum is divided by 10 and the remainder of the division is obtained
434
     * If the remainder is 0 the check digit is 0
435
     * Otherwise, the residue is subtracted from 10
436
     *
437
     * If the result is equal to the verification digit, the value is correct.
438
     *
439
     * @param string $number Certificates or RUC of natural person
440
     * @return boolean
441
     * @throws EcuadorIdentificationException The verified digit does not match the verification digit.
442
     */
443
    protected function moduleTen($number)
444
    {
445
        $check_digit_position = config('laravel-ecuador-identification.personal-identification.check-digit-position');
446
        $coefficients = config('laravel-ecuador-identification.personal-identification.coefficients');
447
448
        $check_digit_value = $number[$check_digit_position - 1];
449
        $numbers = str_split(substr($number, 0, 9));
450
451
        $total = 0;
452
453
        foreach ($numbers as $key => $value) {
454
            $proceeds = ($value * $coefficients[$key]);
455
456
            if ($proceeds >= 10) {
457
                $proceeds = str_split($proceeds);
458
                $proceeds = array_sum($proceeds);
459
            }
460
461
            $total += $proceeds;
462
        }
463
464
        $residue = $total % 10;
465
466
        $verified_digit_value = $residue == 0 ? 0 : 10 - $residue;
467
468
        if ($verified_digit_value != $check_digit_value) {
469
            throw new EcuadorIdentificationException('Field is invalid');
470
        }
471
472
        return true;
473
    }
474
475
    /**
476
     * Module 11 Algorithm to validate if RUC of Public Companies and Private Companies are valid.
477
     *
478
     * For Public Companies (Third Digit => [6]):
479
     * => The verifier digit is the ninth digit.
480
     * => Coefficients used to validate the ninth digit of the Public Company RUC, (Third digit = [6])
481
     * are:  3, 2, 7, 6, 5, 4, 3, 2
482
     *
483
     *
484
     * For Private Companies (Third Digit => [9]):
485
     * => The verifier digit is the tenth digit.
486
     * => Coefficients used to validate the tenth digit of the Private Company RUC, (Third digit = [9])
487
     * are:  4, 3, 2, 7, 6, 5, 4, 3, 2
488
     *
489
     * Step 1: Multiply each digit of the RUC with its respective coefficient,
490
     * except the verification digit. Example:
491
     *
492
     * Public Companies
493
     * 3   2   7   6  5  4  3  2  (Coefficients)
494
     * 1   7   6   0  0  0  1  0  [4]  0  0  0  1  (Public RUC) [4] => Ninth Digit (Check Digit)
495
     * 3   14  42  0  0  0  3  0 => Multiplication Result
496
     *
497
     * Private Companies
498
     * 4   3   2   7   6   5   4   3   2  (Coefficients)
499
     * 1   7   9   0   0   8   5   7   8   [3]  0  0  1   (Private RUC) [3] => Tenth Digit (Check Digit)
500
     * 4   21  18  0   0   40  20  21  16 => Multiplication Result
501
     *
502
     * Step 2: The multiplication results are added
503
     *
504
     * Public Companies
505
     * 3  14  42  0  0  0  3  0 = Result (62)
506
     *
507
     * * Private Companies
508
     * 4  21  18  0  0  40  20  21  16 = Result (140)
509
     *
510
     * Step 3: The result of the sum is divided to 11 and the remainder of the division is obtained.
511
     * If the remainder is 0 the check digit is 0
512
     * Otherwise, the residue is subtracted from 11
513
     *
514
     * If the result is equal to the verification digit, the value is correct.
515
     *
516
     * @param string $number Private Company RUC or Public Compnay RUC
517
     * @param string $type Type of identifier
518
     * @return boolean
519
     * @throws EcuadorIdentificationException The verified digit does not match the verification digit.
520
     */
521
    protected function moduleEleven($number, $type)
522
    {
523
        switch ($type) {
524 View Code Duplication
            case self::PrivateCompany:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
525
                $coefficients = config('laravel-ecuador-identification.private-ruc.coefficients');
526
                $check_digit_position = config('laravel-ecuador-identification.private-ruc.check-digit-position');
527
                $check_digit_value = $number[$check_digit_position - 1];
528
                $numbers = str_split(substr($number, 0, 9));
529
                break;
530 View Code Duplication
            case self::PublicCompany:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
531
                $coefficients = config('laravel-ecuador-identification.public-ruc.coefficients');
532
                $check_digit_position = config('laravel-ecuador-identification.public-ruc.check-digit-position');
533
                $check_digit_value = $number[$check_digit_position - 1];
534
                $numbers = str_split(substr($number, 0, 8));
535
                break;
536
            default:
537
                throw new EcuadorIdentificationException('Field does not have this type of identification.');
538
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
539
        }
540
541
        $total = 0;
542
543
        foreach ($numbers as $key => $value) {
544
            $proceeds = ($value * $coefficients[$key]);
545
            $total += $proceeds;
546
        }
547
548
        $residue = $total % 11;
549
550
        $verified_digit_value = $residue == 0 ? 0 : 11 - $residue;
551
552
        if ($verified_digit_value != $check_digit_value) {
553
            throw new EcuadorIdentificationException('Field is invalid');
554
        }
555
556
        return true;
557
    }
558
}
559