chellow 1755614564.0.0__py3-none-any.whl → 1759155233.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of chellow might be problematic. Click here for more details.
- chellow/e/bill_importer.py +136 -80
- chellow/e/bill_parsers/activity_mop_stark_xlsx.py +99 -86
- chellow/e/bill_parsers/annual_mop_stark_xlsx.py +78 -61
- chellow/e/bill_parsers/csv.py +139 -101
- chellow/e/bill_parsers/drax_edi.py +65 -88
- chellow/e/bill_parsers/engie_edi.py +187 -255
- chellow/e/bill_parsers/engie_xls.py +153 -167
- chellow/e/bill_parsers/haven_edi.py +189 -228
- chellow/e/bill_parsers/haven_edi_tprs.py +67 -67
- chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py +75 -66
- chellow/e/bill_parsers/settlement_dc_stark_xlsx.py +229 -126
- chellow/e/bill_parsers/sse_edi.py +107 -75
- chellow/e/bill_parsers/sww_xls.py +78 -91
- chellow/e/computer.py +1 -1
- chellow/e/views.py +626 -281
- chellow/edi_lib.py +4 -27
- chellow/models.py +92 -3
- chellow/reports/report_111.py +478 -616
- chellow/reports/report_247.py +96 -137
- chellow/templates/e/dc_batch.html +110 -157
- chellow/templates/e/dc_batch_add.html +2 -3
- chellow/templates/e/dc_batch_edit.html +42 -46
- chellow/templates/e/dc_batch_file.html +2 -3
- chellow/templates/e/dc_batch_file_edit.html +28 -40
- chellow/templates/e/dc_batch_upload_file.html +68 -0
- chellow/templates/e/dc_batches.html +2 -1
- chellow/templates/e/dc_batches_edit.html +26 -0
- chellow/templates/e/dc_bill.html +27 -5
- chellow/templates/e/dc_bill_add.html +4 -4
- chellow/templates/e/dc_bill_edit.html +43 -63
- chellow/templates/e/dc_bill_import.html +1 -1
- chellow/templates/e/dc_bill_import_contract.html +130 -0
- chellow/templates/e/dc_contract.html +1 -1
- chellow/templates/e/dc_element.html +41 -0
- chellow/templates/e/dc_element_add.html +36 -0
- chellow/templates/e/dc_element_edit.html +49 -0
- chellow/templates/e/dc_rate_script_edit.html +27 -43
- chellow/templates/e/mop_batch.html +105 -152
- chellow/templates/e/mop_batch_add.html +2 -3
- chellow/templates/e/mop_batch_edit.html +43 -51
- chellow/templates/e/mop_batch_upload_file.html +71 -5
- chellow/templates/e/mop_batches.html +2 -1
- chellow/templates/e/mop_batches_edit.html +26 -0
- chellow/templates/e/mop_bill.html +31 -8
- chellow/templates/e/mop_bill_add.html +7 -27
- chellow/templates/e/mop_bill_import.html +1 -1
- chellow/templates/e/mop_bill_import_contract.html +130 -0
- chellow/templates/e/mop_contract.html +4 -5
- chellow/templates/e/mop_element.html +41 -0
- chellow/templates/e/mop_element_add.html +36 -0
- chellow/templates/e/mop_element_edit.html +49 -0
- chellow/templates/e/supplier_batch.html +3 -7
- chellow/templates/e/supplier_batch_add.html +2 -2
- chellow/templates/e/supplier_batch_edit.html +1 -1
- chellow/templates/e/supplier_batch_file.html +3 -5
- chellow/templates/e/supplier_batch_file_add.html +18 -11
- chellow/templates/e/supplier_batch_upload_file.html +83 -9
- chellow/templates/e/supplier_batches.html +4 -4
- chellow/templates/e/supplier_batches_edit.html +26 -0
- chellow/templates/e/supplier_bill.html +29 -6
- chellow/templates/e/supplier_bill_add.html +3 -3
- chellow/templates/e/supplier_bill_import.html +1 -1
- chellow/templates/e/supplier_bill_import_contract.html +118 -0
- chellow/templates/e/supplier_contract.html +1 -1
- chellow/templates/e/supplier_element.html +45 -0
- chellow/templates/e/supplier_element_add.html +36 -0
- chellow/templates/e/supplier_element_edit.html +51 -0
- chellow/templates/report_run_bill_check.html +137 -179
- chellow/templates/report_run_row_bill_check.html +182 -179
- chellow/views.py +55 -65
- {chellow-1755614564.0.0.dist-info → chellow-1759155233.0.0.dist-info}/METADATA +2 -2
- {chellow-1755614564.0.0.dist-info → chellow-1759155233.0.0.dist-info}/RECORD +73 -60
- chellow/e/bill_parsers/drax_element_edi.py +0 -459
- chellow/templates/e/supplier_bill_imports.html +0 -421
- {chellow-1755614564.0.0.dist-info → chellow-1759155233.0.0.dist-info}/WHEEL +0 -0
|
@@ -4,11 +4,227 @@ from decimal import Decimal, InvalidOperation
|
|
|
4
4
|
from dateutil.relativedelta import relativedelta
|
|
5
5
|
|
|
6
6
|
from openpyxl import load_workbook
|
|
7
|
+
from openpyxl.utils import get_column_letter
|
|
7
8
|
|
|
8
9
|
from werkzeug.exceptions import BadRequest
|
|
9
10
|
|
|
10
11
|
from chellow.models import Session
|
|
11
|
-
from chellow.utils import parse_mpan_core, to_utc
|
|
12
|
+
from chellow.utils import parse_mpan_core, to_ct, to_utc
|
|
13
|
+
|
|
14
|
+
COPS = ("2", "3", "5", "10")
|
|
15
|
+
|
|
16
|
+
COLUMN_MAP = {
|
|
17
|
+
"quarterly unmetered charge": "unmetered charge",
|
|
18
|
+
"no. cop 5/10 meters": "no. cop 5 meters",
|
|
19
|
+
"annual cop 5/10 dc/da rate": "annual cop 5 dc/da rate",
|
|
20
|
+
"monthly cop 3 charge": "cop 3 charge",
|
|
21
|
+
"cop 5/10 charge": "cop 5 charge",
|
|
22
|
+
"monthly cop 5/10 charge": "cop 5 charge",
|
|
23
|
+
"total": "grand total",
|
|
24
|
+
"total ex vat": "grand total",
|
|
25
|
+
"total in vat": "grand total 2",
|
|
26
|
+
"total inc vat": "grand total 2",
|
|
27
|
+
"vat @ 20 %": "vat @ 20%",
|
|
28
|
+
"vat @20%": "vat @ 20%",
|
|
29
|
+
}
|
|
30
|
+
for cop in COPS:
|
|
31
|
+
COLUMN_MAP[f"quarterly cop {cop} charge"] = f"cop {cop} charge"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def make_column_lookup(sheet):
|
|
35
|
+
column_lookup = {}
|
|
36
|
+
for cell in sheet[11]:
|
|
37
|
+
title = str(cell.value).strip().lower()
|
|
38
|
+
try:
|
|
39
|
+
title = COLUMN_MAP[title]
|
|
40
|
+
except KeyError:
|
|
41
|
+
pass
|
|
42
|
+
if title in column_lookup:
|
|
43
|
+
title += " 2"
|
|
44
|
+
column_lookup[title] = get_column_letter(cell.column)
|
|
45
|
+
return column_lookup
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_ct_date(sheet, col, row):
|
|
49
|
+
cell = get_cell(sheet, col, row)
|
|
50
|
+
val = cell.value
|
|
51
|
+
if not isinstance(val, Datetime):
|
|
52
|
+
raise BadRequest(f"Problem reading {val} as a timestamp at {cell.coordinate}.")
|
|
53
|
+
return to_ct(val)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_start_date(sheet, col, row):
|
|
57
|
+
dt = get_ct_date(sheet, col, row)
|
|
58
|
+
return None if dt is None else to_utc(dt)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_cell(sheet, col, row):
|
|
62
|
+
try:
|
|
63
|
+
coordinates = f"{col}{row}"
|
|
64
|
+
return sheet[coordinates]
|
|
65
|
+
except IndexError:
|
|
66
|
+
raise BadRequest(f"Can't find the cell {coordinates} on sheet {sheet}.")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_str(sheet, col, row):
|
|
70
|
+
return get_cell(sheet, col, row).value.strip()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_dec(sheet, col, row):
|
|
74
|
+
cell = get_cell(sheet, col, row)
|
|
75
|
+
if cell.value is None:
|
|
76
|
+
return None
|
|
77
|
+
else:
|
|
78
|
+
try:
|
|
79
|
+
return Decimal(str(cell.value))
|
|
80
|
+
except InvalidOperation as e:
|
|
81
|
+
raise BadRequest(f"Problem parsing the number at {cell.coordinate}. {e}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_gbp(sheet, col, row):
|
|
85
|
+
gbp = get_dec(sheet, col, row)
|
|
86
|
+
if gbp is None:
|
|
87
|
+
return gbp
|
|
88
|
+
else:
|
|
89
|
+
return Decimal("0.00") + round(gbp, 2)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_int(sheet, col, row):
|
|
93
|
+
return int(get_cell(sheet, col, row).value)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _process_row(sess, sheet, row, issue_date, cl):
|
|
97
|
+
mpan_core = parse_mpan_core(str(get_int(sheet, "B", row)))
|
|
98
|
+
start_date_ct = get_ct_date(sheet, cl["bill from"], row)
|
|
99
|
+
start_date = to_utc(start_date_ct)
|
|
100
|
+
finish_date_ct = get_ct_date(sheet, cl["bill to"], row) + relativedelta(
|
|
101
|
+
hours=23, minutes=30
|
|
102
|
+
)
|
|
103
|
+
finish_date = to_utc(finish_date_ct)
|
|
104
|
+
|
|
105
|
+
elements = []
|
|
106
|
+
days = (finish_date_ct - start_date_ct).days + 1
|
|
107
|
+
|
|
108
|
+
el_titles = [("unmetered", "unmetered quant", "unmetered rate", "unmetered charge")]
|
|
109
|
+
for cop in ("2", "3", "5", "10"):
|
|
110
|
+
el_titles.append(
|
|
111
|
+
(
|
|
112
|
+
cop,
|
|
113
|
+
f"no. cop {cop} meters",
|
|
114
|
+
f"annual cop {cop} dc/da rate",
|
|
115
|
+
f"cop {cop} charge",
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
for cop, mpans_title, rate_title, net_title in el_titles:
|
|
120
|
+
if mpans_title in cl:
|
|
121
|
+
mpans = get_int(sheet, cl[mpans_title], row)
|
|
122
|
+
rate = get_dec(sheet, cl[rate_title], row)
|
|
123
|
+
net = get_gbp(sheet, cl[net_title], row)
|
|
124
|
+
if net not in (None, 0):
|
|
125
|
+
elements.append(
|
|
126
|
+
{
|
|
127
|
+
"name": "mpan",
|
|
128
|
+
"start_date": start_date,
|
|
129
|
+
"finish_date": finish_date,
|
|
130
|
+
"net": net,
|
|
131
|
+
"breakdown": {
|
|
132
|
+
"rate": {rate},
|
|
133
|
+
"days": days,
|
|
134
|
+
"mpan-days": mpans * days,
|
|
135
|
+
"cop": {cop},
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
for typ in ("adhoc", "regular"):
|
|
141
|
+
hand_visits_title = f"no. hand held visits ({typ})"
|
|
142
|
+
if hand_visits_title in cl:
|
|
143
|
+
hand_visits = get_dec(sheet, cl[hand_visits_title], row)
|
|
144
|
+
hand_rate = get_dec(sheet, cl[f"hand held visit ({typ}) rate"], row)
|
|
145
|
+
hand_gbp = get_gbp(sheet, cl[f"hand held visit ({typ}) charge"], row)
|
|
146
|
+
if hand_gbp != 0:
|
|
147
|
+
elements.append(
|
|
148
|
+
{
|
|
149
|
+
"name": "activity",
|
|
150
|
+
"start_date": start_date,
|
|
151
|
+
"finish_date": finish_date,
|
|
152
|
+
"net": hand_gbp,
|
|
153
|
+
"breakdown": {
|
|
154
|
+
"rate": {hand_rate},
|
|
155
|
+
"activity-name": {"hand_held_visit"},
|
|
156
|
+
"visits": hand_visits,
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
annual_visits_count = get_int(sheet, cl["no. annual site visits"], row)
|
|
162
|
+
annual_visits_rate = get_dec(sheet, cl["annual site visit rate"], row)
|
|
163
|
+
annual_visits_gbp = get_gbp(sheet, cl["annual site visit charge"], row)
|
|
164
|
+
if annual_visits_gbp != 0:
|
|
165
|
+
elements.append(
|
|
166
|
+
{
|
|
167
|
+
"name": "activitiy",
|
|
168
|
+
"start_date": start_date,
|
|
169
|
+
"finish_date": finish_date,
|
|
170
|
+
"net": annual_visits_gbp,
|
|
171
|
+
"breakdown": {
|
|
172
|
+
"rate": {annual_visits_rate},
|
|
173
|
+
"count": annual_visits_count,
|
|
174
|
+
"activity-name": {"annual_site_visit"},
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
breakdown = {
|
|
180
|
+
"raw_lines": [],
|
|
181
|
+
"settlement-status": ["settlement"],
|
|
182
|
+
}
|
|
183
|
+
net = get_gbp(sheet, cl["grand total"], row)
|
|
184
|
+
sum_el = sum(el["net"] for el in elements)
|
|
185
|
+
if net != sum_el:
|
|
186
|
+
elements.append(
|
|
187
|
+
{
|
|
188
|
+
"name": "activitiy",
|
|
189
|
+
"start_date": start_date,
|
|
190
|
+
"finish_date": finish_date,
|
|
191
|
+
"net": net - sum_el,
|
|
192
|
+
"breakdown": {
|
|
193
|
+
"activity-name": {"discrepancy"},
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
vat = Decimal("0.00")
|
|
199
|
+
if "vat @ 20%" in cl:
|
|
200
|
+
vat += get_gbp(sheet, cl["vat @ 20%"], row)
|
|
201
|
+
gross = Decimal("0.00")
|
|
202
|
+
if "grand total 2" in cl:
|
|
203
|
+
gross += get_gbp(sheet, cl["grand total 2"], row)
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
"bill_type_code": "N",
|
|
207
|
+
"kwh": Decimal(0),
|
|
208
|
+
"net": net,
|
|
209
|
+
"vat": vat,
|
|
210
|
+
"gross": gross,
|
|
211
|
+
"reads": [],
|
|
212
|
+
"breakdown": breakdown,
|
|
213
|
+
"account": mpan_core,
|
|
214
|
+
"issue_date": issue_date,
|
|
215
|
+
"start_date": start_date,
|
|
216
|
+
"finish_date": finish_date,
|
|
217
|
+
"mpan_core": mpan_core,
|
|
218
|
+
"reference": "_".join(
|
|
219
|
+
(
|
|
220
|
+
start_date_ct.strftime("%Y%m%d"),
|
|
221
|
+
finish_date_ct.strftime("%Y%m%d"),
|
|
222
|
+
to_ct(issue_date).strftime("%Y%m%d"),
|
|
223
|
+
mpan_core,
|
|
224
|
+
)
|
|
225
|
+
),
|
|
226
|
+
"elements": elements,
|
|
227
|
+
}
|
|
12
228
|
|
|
13
229
|
|
|
14
230
|
class Parser:
|
|
@@ -31,139 +247,26 @@ class Parser:
|
|
|
31
247
|
self._title_line = line
|
|
32
248
|
return line
|
|
33
249
|
|
|
34
|
-
def get_ct_date(self, col, row):
|
|
35
|
-
cell = self.get_cell(col, row)
|
|
36
|
-
val = cell.value
|
|
37
|
-
if not isinstance(val, Datetime):
|
|
38
|
-
raise BadRequest(
|
|
39
|
-
f"Problem reading {val} as a timestamp at {cell.coordinate}."
|
|
40
|
-
)
|
|
41
|
-
return val
|
|
42
|
-
|
|
43
|
-
def get_start_date(self, col, row):
|
|
44
|
-
dt = self.get_ct_date(col, row)
|
|
45
|
-
return None if dt is None else to_utc(dt)
|
|
46
|
-
|
|
47
|
-
def get_cell(self, col, row):
|
|
48
|
-
try:
|
|
49
|
-
coordinates = f"{col}{row}"
|
|
50
|
-
return self.sheet[coordinates]
|
|
51
|
-
except IndexError:
|
|
52
|
-
raise BadRequest(
|
|
53
|
-
f"Can't find the cell {coordinates} on sheet {self.sheet}."
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
def get_str(self, col, row):
|
|
57
|
-
return self.get_cell(col, row).value.strip()
|
|
58
|
-
|
|
59
|
-
def get_dec(self, col, row):
|
|
60
|
-
cell = self.get_cell(col, row)
|
|
61
|
-
try:
|
|
62
|
-
return Decimal(str(cell.value))
|
|
63
|
-
except InvalidOperation as e:
|
|
64
|
-
raise BadRequest(f"Problem parsing the number at {cell.coordinate}. {e}")
|
|
65
|
-
|
|
66
|
-
def get_int(self, col, row):
|
|
67
|
-
return int(self.get_cell(col, row).value)
|
|
68
|
-
|
|
69
250
|
def make_raw_bills(self):
|
|
70
|
-
row_index = None
|
|
71
251
|
try:
|
|
72
252
|
with Session() as sess:
|
|
73
253
|
bills = []
|
|
74
|
-
issue_date_str = self.
|
|
75
|
-
issue_date =
|
|
254
|
+
issue_date_str = get_str(self.sheet, "A", 7)
|
|
255
|
+
issue_date = to_utc(
|
|
256
|
+
to_ct(Datetime.strptime(issue_date_str[6:-3], "%d/%m/%Y %H:%M"))
|
|
257
|
+
)
|
|
258
|
+
column_lookup = make_column_lookup(self.sheet)
|
|
259
|
+
|
|
76
260
|
for row in range(12, len(self.sheet["A"]) + 1):
|
|
77
|
-
val = self.
|
|
261
|
+
val = get_cell(self.sheet, "A", row).value
|
|
78
262
|
if val is None or val == "":
|
|
79
|
-
|
|
263
|
+
continue
|
|
80
264
|
|
|
81
|
-
self._set_last_line(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
finish_date = self.get_start_date("E", row) + relativedelta(
|
|
85
|
-
hours=23, minutes=30
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
net = round(self.get_dec("AO", row), 2)
|
|
89
|
-
|
|
90
|
-
cop_3_meters = self.get_int("G", row)
|
|
91
|
-
cop_3_rate = self.get_dec("H", row)
|
|
92
|
-
cop_3_gbp = self.get_dec("I", row)
|
|
93
|
-
|
|
94
|
-
# Cop 5 meters
|
|
95
|
-
self.get_int("J", row)
|
|
96
|
-
cop_5_rate = self.get_dec("K", row)
|
|
97
|
-
cop_5_gbp = self.get_dec("L", row)
|
|
98
|
-
|
|
99
|
-
ad_hoc_visits = self.get_dec("P", row)
|
|
100
|
-
ad_hoc_rate = self.get_dec("Q", row)
|
|
101
|
-
ad_hoc_gbp = self.get_dec("R", row)
|
|
102
|
-
activity_names = set()
|
|
103
|
-
activity_gbp = Decimal("0")
|
|
104
|
-
if ad_hoc_gbp != 0:
|
|
105
|
-
activity_names.add("ad_hoc_visit")
|
|
106
|
-
activity_gbp += ad_hoc_gbp
|
|
107
|
-
|
|
108
|
-
annual_visits = self.get_int("S", row)
|
|
109
|
-
annual_rate = self.get_dec("T", row)
|
|
110
|
-
annual_gbp = self.get_dec("U", row)
|
|
111
|
-
if annual_gbp != 0:
|
|
112
|
-
activity_names.add("annual_visit")
|
|
113
|
-
activity_gbp += annual_gbp
|
|
114
|
-
|
|
115
|
-
if cop_3_meters > 0:
|
|
116
|
-
cop = "3"
|
|
117
|
-
mpan_rate = cop_3_rate
|
|
118
|
-
mpan_gbp = cop_3_gbp
|
|
119
|
-
else:
|
|
120
|
-
cop = "5"
|
|
121
|
-
mpan_rate = cop_5_rate
|
|
122
|
-
mpan_gbp = cop_5_gbp
|
|
123
|
-
|
|
124
|
-
breakdown = {
|
|
125
|
-
"raw_lines": [],
|
|
126
|
-
"cop": [cop],
|
|
127
|
-
"settlement-status": ["settlement"],
|
|
128
|
-
"mpan-rate": [mpan_rate],
|
|
129
|
-
"mpan-gbp": mpan_gbp,
|
|
130
|
-
"ad-hoc-visits": ad_hoc_visits,
|
|
131
|
-
"ad-hoc-rate": [ad_hoc_rate],
|
|
132
|
-
"ad-hoc-gbp-info": ad_hoc_gbp,
|
|
133
|
-
"annual-visits-count": annual_visits,
|
|
134
|
-
"annual-visits-rate": [annual_rate],
|
|
135
|
-
"annual-visits-gbp-info": annual_gbp,
|
|
136
|
-
}
|
|
137
|
-
if len(activity_names) > 0:
|
|
138
|
-
breakdown["activity-name"] = sorted(activity_names)
|
|
139
|
-
|
|
140
|
-
if activity_gbp != 0:
|
|
141
|
-
breakdown["activity-gbp"] = activity_gbp
|
|
142
|
-
|
|
143
|
-
bills.append(
|
|
144
|
-
{
|
|
145
|
-
"bill_type_code": "N",
|
|
146
|
-
"kwh": Decimal(0),
|
|
147
|
-
"vat": Decimal("0.00"),
|
|
148
|
-
"net": net,
|
|
149
|
-
"gross": net,
|
|
150
|
-
"reads": [],
|
|
151
|
-
"breakdown": breakdown,
|
|
152
|
-
"account": mpan_core,
|
|
153
|
-
"issue_date": issue_date,
|
|
154
|
-
"start_date": start_date,
|
|
155
|
-
"finish_date": finish_date,
|
|
156
|
-
"mpan_core": mpan_core,
|
|
157
|
-
"reference": "_".join(
|
|
158
|
-
(
|
|
159
|
-
start_date.strftime("%Y%m%d"),
|
|
160
|
-
finish_date.strftime("%Y%m%d"),
|
|
161
|
-
issue_date.strftime("%Y%m%d"),
|
|
162
|
-
mpan_core,
|
|
163
|
-
)
|
|
164
|
-
),
|
|
165
|
-
}
|
|
265
|
+
self._set_last_line(row, val)
|
|
266
|
+
bill = _process_row(
|
|
267
|
+
sess, self.sheet, row, issue_date, column_lookup
|
|
166
268
|
)
|
|
269
|
+
bills.append(bill)
|
|
167
270
|
sess.rollback()
|
|
168
271
|
|
|
169
272
|
except BadRequest as e:
|
|
@@ -3,7 +3,14 @@ from decimal import Decimal
|
|
|
3
3
|
|
|
4
4
|
from werkzeug.exceptions import BadRequest
|
|
5
5
|
|
|
6
|
-
from chellow.edi_lib import
|
|
6
|
+
from chellow.edi_lib import (
|
|
7
|
+
parse_edi,
|
|
8
|
+
to_ct_date,
|
|
9
|
+
to_date,
|
|
10
|
+
to_decimal,
|
|
11
|
+
to_finish_date,
|
|
12
|
+
to_gbp,
|
|
13
|
+
)
|
|
7
14
|
from chellow.models import Era, Session
|
|
8
15
|
from chellow.utils import HH, ct_datetime, parse_mpan_core, to_utc
|
|
9
16
|
|
|
@@ -196,6 +203,45 @@ def _process_BCD(elements, headers):
|
|
|
196
203
|
headers["finish_date"] = to_finish_date(sumo[1])
|
|
197
204
|
|
|
198
205
|
|
|
206
|
+
def _process_BTL(elements, headers):
|
|
207
|
+
uvlt = elements["UVLT"]
|
|
208
|
+
utva = elements["UTVA"]
|
|
209
|
+
tbtl = elements["TBTL"]
|
|
210
|
+
|
|
211
|
+
if headers["mpan_core"] is None:
|
|
212
|
+
sess = headers["sess"]
|
|
213
|
+
era = (
|
|
214
|
+
sess.query(Era)
|
|
215
|
+
.filter(Era.imp_supplier_account == headers["account"])
|
|
216
|
+
.first()
|
|
217
|
+
)
|
|
218
|
+
if era is not None:
|
|
219
|
+
headers["mpan_core"] = era.imp_mpan_core
|
|
220
|
+
sess.close()
|
|
221
|
+
|
|
222
|
+
if headers["is_ebatch"]:
|
|
223
|
+
for r in headers["reads"]:
|
|
224
|
+
if r["pres_type_code"] == "C":
|
|
225
|
+
r["pres_type_code"] = "E"
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
"bill_type_code": headers["bill_type_code"],
|
|
229
|
+
"account": headers["account"],
|
|
230
|
+
"mpan_core": headers["mpan_core"],
|
|
231
|
+
"reference": headers["reference"],
|
|
232
|
+
"issue_date": headers["issue_date"],
|
|
233
|
+
"start_date": headers["start_date"],
|
|
234
|
+
"finish_date": headers["finish_date"],
|
|
235
|
+
"kwh": headers["kwh"],
|
|
236
|
+
"net": to_gbp(uvlt),
|
|
237
|
+
"vat": to_gbp(utva),
|
|
238
|
+
"gross": to_gbp(tbtl),
|
|
239
|
+
"breakdown": headers["breakdown"],
|
|
240
|
+
"reads": headers["reads"],
|
|
241
|
+
"elements": headers["elements"],
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
199
245
|
def _process_MHD(elements, headers):
|
|
200
246
|
message_type = elements["TYPE"][0]
|
|
201
247
|
sess = headers["sess"]
|
|
@@ -204,7 +250,7 @@ def _process_MHD(elements, headers):
|
|
|
204
250
|
headers["kwh"] = Decimal("0")
|
|
205
251
|
headers["reads"] = []
|
|
206
252
|
headers["breakdown"] = defaultdict(int, {"raw-lines": []})
|
|
207
|
-
headers["
|
|
253
|
+
headers["elements"] = []
|
|
208
254
|
headers["errors"] = []
|
|
209
255
|
headers["sess"] = sess
|
|
210
256
|
headers["mpan_core"] = None
|
|
@@ -215,9 +261,6 @@ def _process_MHD(elements, headers):
|
|
|
215
261
|
headers["issue_date"] = None
|
|
216
262
|
headers["start_date"] = None
|
|
217
263
|
headers["finish_date"] = None
|
|
218
|
-
headers["net"] = Decimal("0.00")
|
|
219
|
-
headers["vat"] = Decimal("0.00")
|
|
220
|
-
headers["gross"] = Decimal("0.00")
|
|
221
264
|
headers["message_type"] = message_type
|
|
222
265
|
|
|
223
266
|
|
|
@@ -323,6 +366,11 @@ def _process_CCD2(elements, headers):
|
|
|
323
366
|
|
|
324
367
|
adjf = elements["ADJF"]
|
|
325
368
|
cona = elements["CONA"]
|
|
369
|
+
nuct = elements["NUCT"]
|
|
370
|
+
csdt = elements["CSDT"]
|
|
371
|
+
cedt = elements["CEDT"]
|
|
372
|
+
ctot = elements["CTOT"]
|
|
373
|
+
cppu = elements["CPPU"]
|
|
326
374
|
|
|
327
375
|
coefficient = Decimal(adjf[1]) / Decimal(100000)
|
|
328
376
|
pres_reading_value = Decimal(prrd[0])
|
|
@@ -338,26 +386,28 @@ def _process_CCD2(elements, headers):
|
|
|
338
386
|
if tpr == "kW":
|
|
339
387
|
units = "kW"
|
|
340
388
|
tpr = None
|
|
341
|
-
|
|
389
|
+
el_name = "md"
|
|
342
390
|
elif tpr == "kVA":
|
|
343
391
|
units = "kVA"
|
|
344
392
|
tpr = None
|
|
345
|
-
|
|
393
|
+
el_name = "md"
|
|
346
394
|
else:
|
|
347
395
|
units = "kWh"
|
|
348
396
|
headers["kwh"] += to_decimal(cona) / Decimal("1000")
|
|
349
|
-
|
|
397
|
+
el_name = tpr
|
|
350
398
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
399
|
+
headers["elements"].append(
|
|
400
|
+
{
|
|
401
|
+
"name": el_name,
|
|
402
|
+
"breakdown": {
|
|
403
|
+
cona[1].lower(): to_decimal(nuct) / Decimal("1000"),
|
|
404
|
+
"rate": {to_decimal(cppu) / Decimal("100000")},
|
|
405
|
+
},
|
|
406
|
+
"start_date": to_date(csdt[0]),
|
|
407
|
+
"finish_date": to_finish_date(cedt[0]),
|
|
408
|
+
"net": to_gbp(ctot),
|
|
409
|
+
}
|
|
410
|
+
)
|
|
361
411
|
|
|
362
412
|
if mpan_core in WRONG_TPRS and pres_read_date == to_utc(
|
|
363
413
|
ct_datetime(2020, 4, 1, 23, 30)
|
|
@@ -400,12 +450,9 @@ def _process_CCD3(elements, headers):
|
|
|
400
450
|
tmod = elements["TMOD"]
|
|
401
451
|
tmod0 = tmod[0]
|
|
402
452
|
if tmod0 == "CCL":
|
|
403
|
-
|
|
404
|
-
elif tmod0 in
|
|
405
|
-
|
|
406
|
-
kwh_prefix = "fit-msp-"
|
|
407
|
-
elif tmod0 == "FITARR":
|
|
408
|
-
prefix = kwh_prefix = "fit-reconciliation-"
|
|
453
|
+
el_name = "ccl"
|
|
454
|
+
elif tmod0 in ("CQFITC", "CMFITC", "FITARR"):
|
|
455
|
+
el_name = "fit"
|
|
409
456
|
else:
|
|
410
457
|
tpr_code = tmod0
|
|
411
458
|
if tpr_code not in tmod_map:
|
|
@@ -413,66 +460,51 @@ def _process_CCD3(elements, headers):
|
|
|
413
460
|
f"The TPR code {tpr_code} can't be found in the TPR "
|
|
414
461
|
f"list for mpan {headers['mpan']}."
|
|
415
462
|
)
|
|
416
|
-
|
|
463
|
+
el_name = tmod_map[tpr_code]
|
|
417
464
|
|
|
418
465
|
nuct = elements["NUCT"]
|
|
419
|
-
breakdown = headers["breakdown"]
|
|
420
|
-
breakdown[kwh_prefix + "kwh"] += to_decimal(nuct) / Decimal("1000")
|
|
421
466
|
cppu = elements["CPPU"]
|
|
422
|
-
|
|
423
|
-
rate_key = prefix + "rate"
|
|
424
|
-
if rate_key not in breakdown:
|
|
425
|
-
breakdown[rate_key] = set()
|
|
426
|
-
breakdown[rate_key].add(to_decimal(cppu) / Decimal("100000"))
|
|
427
|
-
|
|
428
467
|
ctot = elements["CTOT"]
|
|
429
|
-
|
|
468
|
+
csdt = elements["CSDT"]
|
|
469
|
+
start_date = to_date(csdt[0])
|
|
470
|
+
cedt = elements["CEDT"]
|
|
471
|
+
finish_date = to_finish_date(cedt[0])
|
|
472
|
+
headers["elements"].append(
|
|
473
|
+
{
|
|
474
|
+
"name": el_name,
|
|
475
|
+
"net": to_gbp(ctot),
|
|
476
|
+
"start_date": start_date,
|
|
477
|
+
"finish_date": finish_date,
|
|
478
|
+
"breakdown": {
|
|
479
|
+
"kwh": to_decimal(nuct) / Decimal("1000"),
|
|
480
|
+
"rate": {to_decimal(cppu) / Decimal("100000")},
|
|
481
|
+
},
|
|
482
|
+
}
|
|
483
|
+
)
|
|
430
484
|
|
|
431
485
|
|
|
432
486
|
def _process_CCD4(elements, headers):
|
|
433
487
|
ndrp = elements["NDRP"]
|
|
434
|
-
breakdown = headers["breakdown"]
|
|
435
|
-
if len(ndrp[0]) > 0:
|
|
436
|
-
breakdown["standing-days"] += to_decimal(ndrp)
|
|
437
488
|
ctot = elements["CTOT"]
|
|
489
|
+
csdt = elements["CSDT"]
|
|
490
|
+
cedt = elements["CEDT"]
|
|
491
|
+
|
|
438
492
|
if len(ctot[0]) > 0:
|
|
439
|
-
breakdown
|
|
493
|
+
breakdown = {}
|
|
494
|
+
el = {
|
|
495
|
+
"name": "standing",
|
|
496
|
+
"net": to_gbp(ctot),
|
|
497
|
+
"start_date": to_date(csdt[0]),
|
|
498
|
+
"finish_date": to_finish_date(cedt[0]),
|
|
499
|
+
"breakdown": breakdown,
|
|
500
|
+
}
|
|
501
|
+
if len(ndrp[0]) > 0:
|
|
502
|
+
breakdown["days"] = to_decimal(ndrp)
|
|
503
|
+
headers["elements"].append(el)
|
|
440
504
|
|
|
441
505
|
|
|
442
506
|
def _process_MTR(elements, headers):
|
|
443
|
-
|
|
444
|
-
if headers["mpan_core"] is None:
|
|
445
|
-
sess = headers["sess"]
|
|
446
|
-
era = (
|
|
447
|
-
sess.query(Era)
|
|
448
|
-
.filter(Era.imp_supplier_account == headers["account"])
|
|
449
|
-
.first()
|
|
450
|
-
)
|
|
451
|
-
if era is not None:
|
|
452
|
-
headers["mpan_core"] = era.imp_mpan_core
|
|
453
|
-
sess.close()
|
|
454
|
-
|
|
455
|
-
if headers["is_ebatch"]:
|
|
456
|
-
for r in headers["reads"]:
|
|
457
|
-
if r["pres_type_code"] == "C":
|
|
458
|
-
r["pres_type_code"] = "E"
|
|
459
|
-
|
|
460
|
-
raw_bill = {
|
|
461
|
-
"bill_type_code": headers["bill_type_code"],
|
|
462
|
-
"account": headers["account"],
|
|
463
|
-
"mpan_core": headers["mpan_core"],
|
|
464
|
-
"reference": headers["reference"],
|
|
465
|
-
"issue_date": headers["issue_date"],
|
|
466
|
-
"start_date": headers["start_date"],
|
|
467
|
-
"finish_date": headers["finish_date"],
|
|
468
|
-
"kwh": headers["kwh"],
|
|
469
|
-
"net": headers["net"],
|
|
470
|
-
"vat": headers["vat"],
|
|
471
|
-
"gross": headers["gross"],
|
|
472
|
-
"breakdown": headers["breakdown"],
|
|
473
|
-
"reads": headers["reads"],
|
|
474
|
-
}
|
|
475
|
-
return raw_bill
|
|
507
|
+
pass
|
|
476
508
|
|
|
477
509
|
|
|
478
510
|
def _process_MAN(elements, headers):
|
|
@@ -482,11 +514,11 @@ def _process_MAN(elements, headers):
|
|
|
482
514
|
|
|
483
515
|
def _process_VAT(elements, headers):
|
|
484
516
|
uvla = elements["UVLA"]
|
|
485
|
-
|
|
517
|
+
to_decimal(uvla) / Decimal("100")
|
|
486
518
|
uvtt = elements["UVTT"]
|
|
487
|
-
|
|
519
|
+
to_decimal(uvtt) / Decimal("100")
|
|
488
520
|
ucsi = elements["UCSI"]
|
|
489
|
-
|
|
521
|
+
to_decimal(ucsi) / Decimal("100")
|
|
490
522
|
|
|
491
523
|
|
|
492
524
|
def _process_NOOP(elements, headers):
|
|
@@ -495,7 +527,7 @@ def _process_NOOP(elements, headers):
|
|
|
495
527
|
|
|
496
528
|
CODE_FUNCS = {
|
|
497
529
|
"BCD": _process_BCD,
|
|
498
|
-
"BTL":
|
|
530
|
+
"BTL": _process_BTL,
|
|
499
531
|
"CCD1": _process_CCD1,
|
|
500
532
|
"CCD2": _process_CCD2,
|
|
501
533
|
"CCD3": _process_CCD3,
|