Coverage for jbank/aeb43.py : 0%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from typing import Tuple, List, Optional, Dict, Any
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
8AEB43_STATEMENT_SUFFIXES = ["TXT", "AEB43"]
10DEBIT_REC_TYPE = "1" # 1=debit, 2=credit
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 ("debt_or_credit_code", "9(1)", "P"),
20 ("initial_balance_amount", "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]
27ACCOUNT_HEADER_DATES = ["final_date", "initial_date"]
28ACCOUNT_HEADER_DECIMALS = [("initial_balance_amount", "debt_or_credit_code")]
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 ("date_of_transaction", "X(6)", "P"),
35 ("value_date", "X(6)", "P"),
36 ("common_concept", "X(2)", "P"),
37 ("own_concept", "X(3)", "P"),
38 ("debt_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]
45TRANSACTION_DATES = ["date_of_transaction", "value_date"]
46TRANSACTION_DECIMALS = [("amount", "debt_or_credit_code")]
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]
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_of_the_movement", "X(3)", "P"),
59 ("amount", "X(14)", "P"),
60 ("free", "X(59)", "P"),
61]
63AMOUNT_EQUIVALENCE_DECIMALS = [("amount", "debt_or_credit_code")]
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_amounts_debit", "X(14)", "P"),
72 ("no_of_notes_to_have", "X(5)", "P"),
73 ("total_amounts_credit", "X(14)", "P"),
74 ("ending_balance_code", "X(1)", "P"),
75 ("final_balance", "X(14)", "P"),
76 ("currency_code", "X(3)", "P"),
77 ("free", "X(4)", "P"),
78]
80ACCOUNT_SUMMARY_DECIMALS = ["final_balance", "total_amounts_debit", "total_amounts_credit"]
82END_OF_FILE_RECORD: List[Tuple[str, str, str]] = [
83 ("registration_code", "9(2)", "P"), # 88
84 ("nine", "X(18)", "P"),
85 ("no_of_records", "X(6)", "P"),
86 ("free", "X(54)", "P"),
87]
90def parse_aeb43_statements_from_file(filename: str) -> list:
91 if parse_filename_suffix(filename).upper() not in AEB43_STATEMENT_SUFFIXES:
92 raise ValidationError(
93 _('File {filename} has unrecognized ({suffixes}) suffix for file type "{file_type}"').format(
94 filename=filename, suffixes=", ".join(AEB43_STATEMENT_SUFFIXES), file_type="AEB43"
95 )
96 )
97 with open(filename, "rt", encoding="UTF-8") as fp:
98 return parse_aeb43_statements(fp.read(), filename=os.path.basename(filename)) # type: ignore
101def parse_aeb43_statements(content: str, filename: str) -> list: # pylint: disable=too-many-locals,unused-argument
102 lines = content.split("\n")
103 nlines = len(lines)
104 line_number = 0
105 tz = timezone("Europe/Madrid")
106 batches: List[dict] = []
107 header: Optional[Dict[str, Any]] = None
108 records: List[Dict[str, Any]] = []
109 summary: Optional[Dict[str, Any]] = None
110 eof: Optional[Dict[str, Any]] = None
111 rec_count = 0
113 while line_number < nlines:
114 line_number += 1
115 line = lines[line_number - 1]
116 if line.strip() == "":
117 line_number += 1
118 continue
119 record_type = line[:2]
121 if record_type == "11":
122 header = parse_records(lines[line_number - 1], ACCOUNT_HEADER_RECORD, line_number=line_number)
123 convert_date_fields(header, ACCOUNT_HEADER_DATES, tz)
124 convert_decimal_fields(header, ACCOUNT_HEADER_DECIMALS, DEBIT_REC_TYPE)
125 rec_count += 1
126 elif record_type == "33":
127 summary = parse_records(lines[line_number - 1], ACCOUNT_SUMMARY_RECORD, line_number=line_number)
128 convert_decimal_fields(summary, ACCOUNT_SUMMARY_DECIMALS, DEBIT_REC_TYPE)
129 batches.append({"header": header, "records": records, "summary": summary})
130 records = []
131 header = summary = None
132 rec_count += 1
133 elif record_type == "22":
134 tx_rec = parse_records(lines[line_number - 1], TRANSACTION_RECORD, line_number=line_number)
135 convert_date_fields(tx_rec, TRANSACTION_DATES, tz)
136 convert_decimal_fields(tx_rec, TRANSACTION_DECIMALS, DEBIT_REC_TYPE)
137 records.append(tx_rec)
138 rec_count += 1
139 elif record_type == "23":
140 sub_rec = parse_records(lines[line_number - 1], CONCEPT_RECORD, line_number=line_number)
141 prev = records[len(records) - 1]
142 prev.setdefault("concept_records", [])
143 prev["concept_records"].append(sub_rec) # type: ignore
144 rec_count += 1
145 elif record_type == "24":
146 sub_rec = parse_records(lines[line_number - 1], AMOUNT_EQUIVALENCE_RECORD, line_number=line_number)
147 convert_decimal_fields(sub_rec, AMOUNT_EQUIVALENCE_DECIMALS, DEBIT_REC_TYPE)
148 prev = records[len(records) - 1]
149 prev.setdefault("amount_equivalence_records", [])
150 prev["amount_equivalence_records"].append(sub_rec) # type: ignore
151 rec_count += 1
152 elif record_type == "88":
153 eof = parse_records(lines[line_number - 1], END_OF_FILE_RECORD, line_number=line_number)
155 if eof is None:
156 raise ValidationError(_("EOF record missing"))
157 if int(eof["no_of_records"]) != rec_count:
158 raise ValidationError(_("Number of records does not match EOF record"))
159 return batches