Coverage for jbank/svm.py : 94%

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 os.path import basename
2from typing import Union, Dict, List, Optional, Any
3from django.core.exceptions import ValidationError
4from django.utils.translation import gettext as _
5from pytz import timezone
6from jbank.parsers import parse_filename_suffix, parse_records, convert_date_fields, convert_decimal_fields
8SVM_STATEMENT_SUFFIXES = ("SVM", "TXT", "KTL")
10SVM_FILE_HEADER_DATES = (("record_date", "record_time"),)
12SVM_FILE_HEADER_TYPES = ("0",)
14SVM_FILE_HEADER = (
15 ("statement_type", "9(1)", "P"),
16 ("record_date", "9(6)", "P"),
17 ("record_time", "9(4)", "P"),
18 ("institution_identifier", "X(2)", "P"),
19 ("service_identifier", "X(9)", "P"),
20 ("currency_identifier", "X(1)", "P"),
21 ("pad01", "X(67)", "P"),
22)
24SVM_FILE_RECORD_TYPES = ("3", "5")
26SVM_FILE_RECORD_DECIMALS = ("amount",)
28SVM_FILE_RECORD_DATES = (
29 "record_date",
30 "paid_date",
31)
33SVM_FILE_RECORD = (
34 ("record_type", "9(1)", "P"), # 3=viitesiirto, 5=suoraveloitus
35 ("account_number", "9(14)", "P"),
36 ("record_date", "9(6)", "P"),
37 ("paid_date", "9(6)", "P"),
38 ("archive_identifier", "X(16)", "P"),
39 ("remittance_info", "X(20)", "P"),
40 ("payer_name", "X(12)", "P"),
41 ("currency_identifier", "X(1)", "P"), # 1=eur
42 ("name_source", "X", "V"),
43 ("amount", "9(10)", "P"),
44 ("correction_identifier", "X", "V"), # 0=normal, 1=correction
45 ("delivery_method", "X", "P"), # A=asiakkaalta, K=konttorista, J=pankin jarjestelmasta
46 ("receipt_code", "X", "P"),
47)
49SVM_FILE_SUMMARY_TYPES = ("9",)
51SVM_FILE_SUMMARY_DECIMALS = (
52 "record_amount",
53 "correction_amount",
54)
56SVM_FILE_SUMMARY = (
57 ("record_type", "9(1)", "P"), # 9
58 ("record_count", "9(6)", "P"),
59 ("record_amount", "9(11)", "P"),
60 ("correction_count", "9(6)", "P"),
61 ("correction_amount", "9(11)", "P"),
62 ("pad01", "X(5)", "P"),
63)
66def parse_svm_batches_from_file(filename: str) -> list:
67 if parse_filename_suffix(filename).upper() not in SVM_STATEMENT_SUFFIXES:
68 raise ValidationError(
69 _('File {filename} has unrecognized ({suffixes}) suffix for file type "{file_type}"').format(
70 filename=filename, suffixes=", ".join(SVM_STATEMENT_SUFFIXES), file_type="saapuvat viitemaksut"
71 )
72 )
73 with open(filename, "rt", encoding="ISO-8859-1") as fp:
74 return parse_svm_batches(fp.read(), filename=basename(filename)) # type: ignore
77def parse_svm_batches(content: str, filename: str) -> list:
78 lines = content.split("\n")
79 nlines = len(lines)
80 line_number = 1
81 tz = timezone("Europe/Helsinki")
82 batches = []
83 header: Optional[Dict[str, Union[int, str]]] = None
84 records: List[Dict[str, Union[int, str]]] = []
85 summary: Optional[Dict[str, Union[int, str]]] = None
87 while line_number <= nlines:
88 line = lines[line_number - 1]
89 if line.strip() == "":
90 line_number += 1
91 continue
92 record_type = line[:1]
94 if record_type in SVM_FILE_HEADER_TYPES:
95 if header:
96 batches.append(combine_svm_batch(header, records, summary))
97 header, records, summary = None, [], None
98 header = parse_records(lines[line_number - 1], SVM_FILE_HEADER, line_number=line_number)
99 convert_date_fields(header, SVM_FILE_HEADER_DATES, tz)
100 line_number += 1
101 elif record_type in SVM_FILE_RECORD_TYPES:
102 record = parse_records(line, SVM_FILE_RECORD, line_number=line_number)
103 convert_date_fields(record, SVM_FILE_RECORD_DATES, tz)
104 convert_decimal_fields(record, SVM_FILE_RECORD_DECIMALS)
105 line_number += 1
106 records.append(record)
107 elif record_type in SVM_FILE_SUMMARY_TYPES:
108 summary = parse_records(line, SVM_FILE_SUMMARY, line_number=line_number)
109 convert_decimal_fields(summary, SVM_FILE_SUMMARY_DECIMALS)
110 line_number += 1
111 else:
112 raise ValidationError(_("Unknown record type on {}({}): {}").format(filename, line_number, record_type))
114 batches.append(combine_svm_batch(header, records, summary))
115 return batches
118def combine_svm_batch(
119 header: Optional[Dict[str, Any]], records: List[Dict[str, Union[int, str]]], summary: Optional[Dict[str, Any]]
120) -> Dict[str, Any]:
121 data = {"header": header, "records": records}
122 if summary is not None:
123 data["summary"] = summary
124 return data