Coverage for jbank/aeb43.py: 0%

81 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-27 13:36 +0700

1from typing import Tuple, List, Optional, Dict, Any, Union 

2from django.core.exceptions import ValidationError 

3from django.utils.translation import gettext as _ 

4from pytz import timezone 

5import os 

6from jbank.parsers import parse_filename_suffix, parse_records, convert_date_fields, convert_decimal_fields 

7 

8AEB43_STATEMENT_SUFFIXES = ["TXT", "AEB43"] 

9 

10DEBIT_REC_TYPE = "1" # 1=debit, 2=credit 

11 

12ACCOUNT_HEADER_RECORD: List[Tuple[str, str, str]] = [ 

13 ("registration_code", "9(2)", "P"), # 11 

14 ("entity_key", "X(4)", "P"), 

15 ("office_key", "X(4)", "P"), 

16 ("account_number", "X(10)", "P"), 

17 ("initial_date", "9(6)", "P"), 

18 ("final_date", "9(6)", "P"), 

19 ("initial_balance_debit_or_credit_code", "9(1)", "P"), # 1=debit, 2=credit 

20 ("initial_balance", "X(14)", "P"), 

21 ("currency_key", "X(3)", "P"), 

22 ("information_mode", "X(1)", "P"), 

23 ("name", "X(26)", "P"), 

24 ("free", "X(3)", "P"), 

25] 

26 

27ACCOUNT_HEADER_DATES = ["initial_date", "final_date"] 

28ACCOUNT_HEADER_DECIMALS = [("initial_balance", "initial_balance_debit_or_credit_code")] 

29 

30TRANSACTION_RECORD: List[Tuple[str, str, str]] = [ 

31 ("registration_code", "9(2)", "P"), # 22 

32 ("free", "X(4)", "P"), 

33 ("origin_office_code", "X(4)", "P"), 

34 ("transaction_date", "X(6)", "P"), 

35 ("value_date", "X(6)", "P"), 

36 ("common_concept", "X(2)", "P"), 

37 ("own_concept", "X(3)", "P"), 

38 ("debit_or_credit_code", "X(1)", "P"), # 1=debit, 2=credit 

39 ("amount", "X(14)", "P"), # cents, left-padded with zeros 

40 ("document_number", "X(10)", "P"), 

41 ("reference_1", "X(12)", "P"), 

42 ("reference_2", "X(16)", "P"), 

43] 

44 

45TRANSACTION_DATES = ["transaction_date", "value_date"] 

46TRANSACTION_DECIMALS = [("amount", "debit_or_credit_code")] 

47 

48CONCEPT_RECORD: List[Tuple[str, str, str]] = [ 

49 ("registration_code", "9(2)", "P"), # 23 

50 ("data_code", "X(2)", "P"), 

51 ("concept", "X(38)", "P"), 

52 ("concept", "X(38)", "P"), 

53] 

54 

55AMOUNT_EQUIVALENCE_RECORD: List[Tuple[str, str, str]] = [ 

56 ("registration_code", "9(2)", "P"), # 24 

57 ("data_code", "X(2)", "P"), 

58 ("currency_key_origin", "X(3)", "P"), 

59 ("amount", "X(14)", "P"), 

60 ("free", "X(59)", "P"), 

61] 

62 

63AMOUNT_EQUIVALENCE_DECIMALS = [("amount", "data_code")] 

64 

65ACCOUNT_SUMMARY_RECORD: List[Tuple[str, str, str]] = [ 

66 ("registration_code", "9(2)", "P"), # 33 

67 ("entity_key", "X(4)", "P"), 

68 ("office_key", "X(4)", "P"), 

69 ("account_number", "X(10)", "P"), 

70 ("no_of_notes_must", "X(5)", "P"), 

71 ("total_amount_debits", "X(14)", "P"), 

72 ("no_of_notes_to_have", "X(5)", "P"), 

73 ("total_amount_credits", "X(14)", "P"), 

74 ("final_balance_debit_or_credit_code", "X(1)", "P"), 

75 ("final_balance", "X(14)", "P"), 

76 ("currency_code", "X(3)", "P"), 

77 ("free", "X(4)", "P"), 

78] 

79 

80ACCOUNT_SUMMARY_DECIMALS: List[Union[Tuple[str, str], str]] = [ 

81 ("final_balance", "final_balance_debit_or_credit_code"), 

82 "total_amount_credits", 

83 "total_amount_debits", 

84] 

85 

86END_OF_FILE_RECORD: List[Tuple[str, str, str]] = [ 

87 ("registration_code", "9(2)", "P"), # 88 

88 ("nine", "X(18)", "P"), 

89 ("no_of_records", "X(6)", "P"), 

90 ("free", "X(54)", "P"), 

91] 

92 

93 

94def parse_aeb43_statements_from_file(filename: str) -> list: 

95 if parse_filename_suffix(filename).upper() not in AEB43_STATEMENT_SUFFIXES: 

96 raise ValidationError( 

97 _('File {filename} has unrecognized ({suffixes}) suffix for file type "{file_type}"').format( 

98 filename=filename, suffixes=", ".join(AEB43_STATEMENT_SUFFIXES), file_type="AEB43" 

99 ) 

100 ) 

101 with open(filename, "rt", encoding="UTF-8") as fp: 

102 return parse_aeb43_statements(fp.read(), filename=os.path.basename(filename)) # type: ignore 

103 

104 

105def parse_aeb43_statements(content: str, filename: str) -> list: # pylint: disable=too-many-locals,unused-argument 

106 lines = content.split("\n") 

107 nlines = len(lines) 

108 line_number = 0 

109 tz = timezone("Europe/Madrid") 

110 batches: List[dict] = [] 

111 header: Optional[Dict[str, Any]] = None 

112 records: List[Dict[str, Any]] = [] 

113 summary: Optional[Dict[str, Any]] = None 

114 eof: Optional[Dict[str, Any]] = None 

115 rec_count = 0 

116 

117 while line_number < nlines: 

118 line_number += 1 

119 line = lines[line_number - 1] 

120 if line.strip() == "": 

121 line_number += 1 

122 continue 

123 record_type = line[:2] 

124 

125 if record_type == "11": 

126 header = parse_records(lines[line_number - 1], ACCOUNT_HEADER_RECORD, line_number=line_number) 

127 convert_date_fields(header, ACCOUNT_HEADER_DATES, tz) 

128 convert_decimal_fields(header, ACCOUNT_HEADER_DECIMALS, DEBIT_REC_TYPE) 

129 rec_count += 1 

130 elif record_type == "33": 

131 summary = parse_records(lines[line_number - 1], ACCOUNT_SUMMARY_RECORD, line_number=line_number) 

132 convert_decimal_fields(summary, ACCOUNT_SUMMARY_DECIMALS, DEBIT_REC_TYPE) 

133 batches.append({"header": header, "records": records, "summary": summary}) 

134 records = [] 

135 header = summary = None 

136 rec_count += 1 

137 elif record_type == "22": 

138 tx_rec = parse_records(lines[line_number - 1], TRANSACTION_RECORD, line_number=line_number) 

139 convert_date_fields(tx_rec, TRANSACTION_DATES, tz) 

140 convert_decimal_fields(tx_rec, TRANSACTION_DECIMALS, DEBIT_REC_TYPE) 

141 records.append(tx_rec) 

142 rec_count += 1 

143 elif record_type == "23": 

144 sub_rec = parse_records(lines[line_number - 1], CONCEPT_RECORD, line_number=line_number) 

145 prev = records[len(records) - 1] 

146 prev.setdefault("concept_records", []) 

147 prev["concept_records"].append(sub_rec) # type: ignore 

148 rec_count += 1 

149 elif record_type == "24": 

150 sub_rec = parse_records(lines[line_number - 1], AMOUNT_EQUIVALENCE_RECORD, line_number=line_number) 

151 convert_decimal_fields(sub_rec, AMOUNT_EQUIVALENCE_DECIMALS, DEBIT_REC_TYPE) 

152 prev = records[len(records) - 1] 

153 prev.setdefault("amount_equivalence_records", []) 

154 prev["amount_equivalence_records"].append(sub_rec) # type: ignore 

155 rec_count += 1 

156 elif record_type == "88": 

157 eof = parse_records(lines[line_number - 1], END_OF_FILE_RECORD, line_number=line_number) 

158 

159 if eof is None: 

160 raise ValidationError(_("EOF record missing")) 

161 if int(eof["no_of_records"]) != rec_count: 

162 raise ValidationError(_("Number of records does not match EOF record")) 

163 return batches