From 9d1c3ce3c95e0f596cc65096dfa4a1f157c353c5 Mon Sep 17 00:00:00 2001 From: Daniel Venancio Date: Thu, 4 May 2023 14:13:45 -0300 Subject: [PATCH] [ADD] Recopi validation --- src/erpbrasil/base/fiscal/recopi.py | 162 ++++++++++++++++++++ tests/test_base_fiscal_recopi_validation.py | 38 +++++ 2 files changed, 200 insertions(+) create mode 100644 src/erpbrasil/base/fiscal/recopi.py create mode 100644 tests/test_base_fiscal_recopi_validation.py diff --git a/src/erpbrasil/base/fiscal/recopi.py b/src/erpbrasil/base/fiscal/recopi.py new file mode 100644 index 0000000..6ac6e24 --- /dev/null +++ b/src/erpbrasil/base/fiscal/recopi.py @@ -0,0 +1,162 @@ +# coding=utf-8 +# Copyright (C) 2023 Daniel Venancio - KMEE +# License MIT - See https://opensource.org/license/mit +import datetime as dt + +CURRENT_YEAR = dt.date.today().year +CURRENT_MONTH = dt.date.today().month +CURRENT_DAY = dt.date.today().day +VERIFICATION_DIGIT_WEIGHT = [ + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 10, + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2, + 1, +] + + +def is_valid_recopi(recopi): + if _check_recopi_size(recopi): + if _check_recopi_format(recopi): + return True + return False + + +def generate_valid_recopi(): + recopi_date = _generate_recopi_date() + recopi_hour = _generate_recopi_hour() + recopi_four_digits = _generate_recopi_four_digits() + recopi_verification_digits = _generate_recopi_verification_digits( + recopi_date + recopi_hour + recopi_four_digits + ) + recopi = recopi_date + recopi_hour + recopi_four_digits + recopi_verification_digits + return recopi + + +def _check_recopi_size(recopi): + if len(recopi) == 20: + return True + return False + + +def _check_recopi_format(recopi): + return ( + _check_recopi_date(recopi) + and _check_recopi_time(recopi) + and _check_recopi_verification_digits(recopi) + ) + + +def _check_recopi_date(recopi): + recopi_year, recopi_month, recopi_day = get_recopi_date(recopi) + try: + dt.date( + year=recopi_year, month=recopi_month, day=recopi_day + ) # _check if it's a calendar date + if recopi_year > CURRENT_YEAR: + return True + elif recopi_year == CURRENT_YEAR and recopi_month > CURRENT_MONTH: + return True + elif recopi_year < 2013: + return True + + return False + except Exception: + return False + + +def get_recopi_date(recopi): + recopi_year = int(recopi[0:4]) + recopi_month = int(recopi[4:6]) + recopi_day = int(recopi[6:8]) + return recopi_year, recopi_month, recopi_day + + +def _check_recopi_time(recopi): + recopi_hour, recopi_minute, recopi_second = get_recopi_time(recopi) + try: + dt.datetime( + year=2003, + month=4, + day=21, + hour=recopi_hour, + minute=recopi_minute, + second=recopi_second, + ) + return True + except Exception: + return False + + +def get_recopi_time(recopi): + recopi_hour = int(recopi[8:10]) + recopi_minute = int(recopi[10:12]) + recopi_second = int(recopi[12:14]) + return recopi_hour, recopi_minute, recopi_second + + +def _check_recopi_verification_digits(recopi): + return _check_recopi_first_verification_digit( + recopi + ) and _check_recopi_second_verification_digit(recopi) + + +def _check_recopi_first_verification_digit(recopi): + if calculate_first_verification_digit(recopi) == int(recopi[18]): + return True + return False + + +def calculate_first_verification_digit(recopi): + sum = 0 + for count in range(0, 18): + sum = sum + (int(recopi[count]) * VERIFICATION_DIGIT_WEIGHT[count + 1]) + return 11 - (sum % 11) + + +def _check_recopi_second_verification_digit(recopi): + if calculate_second_verification_digit(recopi) == int(recopi[19]): + return True + return False + + +def calculate_second_verification_digit(recopi): + sum = 0 + for count in range(0, 19): + sum = sum + (int(recopi[count]) * VERIFICATION_DIGIT_WEIGHT[count]) + return 11 - (sum % 11) + + +def _generate_recopi_date(): + return str(CURRENT_YEAR + 1) + "0101" + + +def _generate_recopi_hour(): + return "080000" + + +def _generate_recopi_four_digits(): + return "1234" + + +def _generate_recopi_verification_digits(inicial_digits): + first_verification_digit = str(calculate_first_verification_digit(inicial_digits)) + second_verification_digit = str( + calculate_second_verification_digit(inicial_digits + first_verification_digit) + ) + + return first_verification_digit + second_verification_digit diff --git a/tests/test_base_fiscal_recopi_validation.py b/tests/test_base_fiscal_recopi_validation.py new file mode 100644 index 0000000..6387abd --- /dev/null +++ b/tests/test_base_fiscal_recopi_validation.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# Copyright (C) 2023 Daniel Venancio - KMEE +# License MIT - See https://opensource.org/license/mit +from unittest import TestCase + +from erpbrasil.base.fiscal import recopi + + +class RecopiValidation(TestCase): + def setUp(self): + self.valid_recopi = recopi.generate_valid_recopi() + self.invalid_recopi_date = "20220229114609734393" + self.invalid_recopi_no_calendar = "20250229114609734393" + self.invalid_recopi_time = "20240229274609734393" + self.invalid_recopi_verification_digit1 = "20231106114609734372" + self.invalid_recopi_verification_digit2 = "20231106114609734343" + + def test_validate_recopi(self): + test_result = recopi.is_valid_recopi(self.valid_recopi) + self.assertEqual(True, test_result) + + def test_validate_recopi_date(self): + test_result = recopi.is_valid_recopi(self.invalid_recopi_date) + self.assertEqual(False, test_result) + + test_result = recopi.is_valid_recopi(self.invalid_recopi_no_calendar) + self.assertEqual(False, test_result) + + def test_validate_recopi_time(self): + test_result = recopi.is_valid_recopi(self.invalid_recopi_time) + self.assertEqual(False, test_result) + + def test_validate_recopi_verification_digits(self): + test_result = recopi.is_valid_recopi(self.invalid_recopi_verification_digit1) + self.assertEqual(False, test_result) + + test_result = recopi.is_valid_recopi(self.invalid_recopi_verification_digit2) + self.assertEqual(False, test_result)