chellow 1757320031.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.

Files changed (75) hide show
  1. chellow/e/bill_importer.py +136 -80
  2. chellow/e/bill_parsers/activity_mop_stark_xlsx.py +99 -86
  3. chellow/e/bill_parsers/annual_mop_stark_xlsx.py +78 -61
  4. chellow/e/bill_parsers/csv.py +139 -101
  5. chellow/e/bill_parsers/drax_edi.py +65 -89
  6. chellow/e/bill_parsers/engie_edi.py +187 -255
  7. chellow/e/bill_parsers/engie_xls.py +153 -167
  8. chellow/e/bill_parsers/haven_edi.py +189 -228
  9. chellow/e/bill_parsers/haven_edi_tprs.py +67 -67
  10. chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py +75 -66
  11. chellow/e/bill_parsers/settlement_dc_stark_xlsx.py +229 -126
  12. chellow/e/bill_parsers/sse_edi.py +107 -75
  13. chellow/e/bill_parsers/sww_xls.py +78 -91
  14. chellow/e/computer.py +1 -1
  15. chellow/e/views.py +626 -281
  16. chellow/edi_lib.py +4 -27
  17. chellow/models.py +92 -3
  18. chellow/reports/report_111.py +478 -616
  19. chellow/reports/report_247.py +96 -137
  20. chellow/templates/e/dc_batch.html +110 -157
  21. chellow/templates/e/dc_batch_add.html +2 -3
  22. chellow/templates/e/dc_batch_edit.html +42 -46
  23. chellow/templates/e/dc_batch_file.html +2 -3
  24. chellow/templates/e/dc_batch_file_edit.html +28 -40
  25. chellow/templates/e/dc_batch_upload_file.html +68 -0
  26. chellow/templates/e/dc_batches.html +2 -1
  27. chellow/templates/e/dc_batches_edit.html +26 -0
  28. chellow/templates/e/dc_bill.html +27 -5
  29. chellow/templates/e/dc_bill_add.html +4 -4
  30. chellow/templates/e/dc_bill_edit.html +43 -63
  31. chellow/templates/e/dc_bill_import.html +1 -1
  32. chellow/templates/e/dc_bill_import_contract.html +130 -0
  33. chellow/templates/e/dc_contract.html +1 -1
  34. chellow/templates/e/dc_element.html +41 -0
  35. chellow/templates/e/dc_element_add.html +36 -0
  36. chellow/templates/e/dc_element_edit.html +49 -0
  37. chellow/templates/e/dc_rate_script_edit.html +27 -43
  38. chellow/templates/e/mop_batch.html +105 -152
  39. chellow/templates/e/mop_batch_add.html +2 -3
  40. chellow/templates/e/mop_batch_edit.html +43 -51
  41. chellow/templates/e/mop_batch_upload_file.html +71 -5
  42. chellow/templates/e/mop_batches.html +2 -1
  43. chellow/templates/e/mop_batches_edit.html +26 -0
  44. chellow/templates/e/mop_bill.html +31 -8
  45. chellow/templates/e/mop_bill_add.html +7 -27
  46. chellow/templates/e/mop_bill_import.html +1 -1
  47. chellow/templates/e/mop_bill_import_contract.html +130 -0
  48. chellow/templates/e/mop_contract.html +4 -5
  49. chellow/templates/e/mop_element.html +41 -0
  50. chellow/templates/e/mop_element_add.html +36 -0
  51. chellow/templates/e/mop_element_edit.html +49 -0
  52. chellow/templates/e/supplier_batch.html +3 -7
  53. chellow/templates/e/supplier_batch_add.html +2 -2
  54. chellow/templates/e/supplier_batch_edit.html +1 -1
  55. chellow/templates/e/supplier_batch_file.html +3 -5
  56. chellow/templates/e/supplier_batch_file_add.html +18 -11
  57. chellow/templates/e/supplier_batch_upload_file.html +83 -9
  58. chellow/templates/e/supplier_batches.html +4 -4
  59. chellow/templates/e/supplier_batches_edit.html +26 -0
  60. chellow/templates/e/supplier_bill.html +29 -6
  61. chellow/templates/e/supplier_bill_add.html +3 -3
  62. chellow/templates/e/supplier_bill_import.html +1 -1
  63. chellow/templates/e/supplier_bill_import_contract.html +118 -0
  64. chellow/templates/e/supplier_contract.html +1 -1
  65. chellow/templates/e/supplier_element.html +45 -0
  66. chellow/templates/e/supplier_element_add.html +36 -0
  67. chellow/templates/e/supplier_element_edit.html +51 -0
  68. chellow/templates/report_run_bill_check.html +137 -179
  69. chellow/templates/report_run_row_bill_check.html +182 -179
  70. chellow/views.py +55 -65
  71. {chellow-1757320031.0.0.dist-info → chellow-1759155233.0.0.dist-info}/METADATA +2 -2
  72. {chellow-1757320031.0.0.dist-info → chellow-1759155233.0.0.dist-info}/RECORD +73 -60
  73. chellow/e/bill_parsers/drax_element_edi.py +0 -459
  74. chellow/templates/e/supplier_bill_imports.html +0 -421
  75. {chellow-1757320031.0.0.dist-info → chellow-1759155233.0.0.dist-info}/WHEEL +0 -0
@@ -1,459 +0,0 @@
1
- from collections import defaultdict
2
- from decimal import Decimal
3
-
4
-
5
- from werkzeug.exceptions import BadRequest
6
-
7
- from chellow.edi_lib import (
8
- ct_datetime,
9
- parse_edi,
10
- to_date,
11
- to_decimal,
12
- to_finish_date,
13
- to_utc,
14
- )
15
- from chellow.utils import HH, parse_mpan_core
16
-
17
-
18
- read_type_map = {
19
- "00": "N",
20
- "09": "N3",
21
- "04": "C",
22
- "02": "E",
23
- "11": "E3",
24
- "01": "EM",
25
- "03": "W",
26
- "06": "X",
27
- "05": "CP",
28
- "12": "IF",
29
- }
30
-
31
-
32
- TMOD_MAP = {
33
- "139039": ("aahedc-gbp", "aahedc-rate", "aahedc-kwh"),
34
- "064305": ("fit-gbp", None, None),
35
- "590346": ("cfd-operational-gbp", None, None),
36
- "269100": ("bsuos-gbp", "bsuos-rate", "bsuos-kwh"),
37
- "422733": ("ccl-gbp", "ccl-rate", "ccl-kwh"),
38
- "273237": ("cfd-operational-gbp", "cfd-operational-rate", "cfd-operational-kwh"),
39
- "954379": ("cfd-interim-gbp", "cfd-interim-rate", "cfd-interim-kwh"),
40
- "538249": ("capaity-gbp", None, None),
41
- "568307": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
42
- "020330": ("eii-gbp", None, None),
43
- "439724": ("eii-gbp", None, None),
44
- "247610": ("eii-gbp", None, None),
45
- "930504": ("eii-gbp", None, None),
46
- "331201": ("eii-gbp", None, None),
47
- "307253": ("eii-gbp", "eii-rate", "eii-kwh"),
48
- "065950": ("eii-gbp", "eii-rate", "eii-kwh"),
49
- "095469": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
50
- "637050": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
51
- "489920": ("elexon-gbp", "elexon-rate", "elexon-nbp-kwh"),
52
- "704107": ("fit-gbp", None, None),
53
- "019090": ("rego-gbp", "rego-rate", "rego-kwh"),
54
- "033667": ("management-gbp", "management-rate", "management-kwh"),
55
- "091890": ("shape-gbp", "shape-rate", "shape-kwh"),
56
- "122568": ("nrg-msp-gbp", "nrg-rate", "nrg-msp-kwh"),
57
- "716514": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
58
- "769979": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
59
- "794486": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
60
- "797790": ("duos-reactive-gbp", "duos-reactive-rate", "duos-reactive-kvarh"),
61
- "709522": (
62
- "duos-excess-availability-gbp",
63
- "duos-excess-availability-rate",
64
- "duos-excess-availability-kva",
65
- ),
66
- "644819": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
67
- "806318": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
68
- "209269": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
69
- "229128": ("ro-gbp", "ro-rate", "ro-kwh"),
70
- "012069": ("tnuos-gbp", None, None),
71
- }
72
-
73
- TPR_LOOKUP = {
74
- "Day": "00043",
75
- "Off Peak / Weekends": "00210",
76
- "Night": "00210",
77
- "Default Rate": "00043",
78
- "Single": "00210",
79
- }
80
-
81
-
82
- def _process_BCD(elements, headers):
83
- issue_date = to_date(elements["IVDT"][0])
84
- reference = elements["INVN"][0]
85
- bill_type_code = elements["BTCD"][0]
86
-
87
- headers["issue_date"] = issue_date
88
- headers["bill_type_code"] = bill_type_code
89
- headers["reference"] = reference
90
-
91
-
92
- def _process_BTL(elements, headers):
93
- for bill in headers["bills"]:
94
- bill["mpan_core"] = headers["mpan_core"]
95
- bill["account"] = headers["account"]
96
- _customer_mods(headers, bill)
97
- return headers["bills"]
98
-
99
-
100
- def _process_CCD1(elements, headers):
101
- tcod = elements["TCOD"]
102
- pres_read_date = to_finish_date(elements["PRDT"][0])
103
-
104
- prev_read_date = to_finish_date(elements["PVDT"][0])
105
-
106
- m = elements["MLOC"][0]
107
- mpan = " ".join((m[13:15], m[15:18], m[18:], m[:2], m[2:6], m[6:10], m[10:13]))
108
-
109
- prrd = elements["PRRD"]
110
- if len(prrd) < 4:
111
- return
112
- pres_read_type = read_type_map[prrd[1]]
113
- prev_read_type = read_type_map[prrd[3]]
114
-
115
- coefficient = Decimal(elements["ADJF"][1]) / Decimal(100000)
116
- pres_reading_value = Decimal(prrd[0])
117
- prev_reading_value = Decimal(prrd[2])
118
- msn = elements["MTNR"][0]
119
- tpr_code = elements["TMOD"][0]
120
- if tpr_code == "kW":
121
- units = "kW"
122
- tpr_code = None
123
- elif tpr_code == "kVA":
124
- units = "kVA"
125
- tpr_code = None
126
- else:
127
- units = "kWh"
128
- tpr_code = TPR_LOOKUP[tcod[1]]
129
-
130
- try:
131
- reads = headers["reads"]
132
- except KeyError:
133
- reads = headers["reads"] = []
134
-
135
- reads.append(
136
- {
137
- "msn": msn,
138
- "mpan": mpan,
139
- "coefficient": coefficient,
140
- "units": units,
141
- "tpr_code": tpr_code,
142
- "prev_date": prev_read_date,
143
- "prev_value": prev_reading_value,
144
- "prev_type_code": prev_read_type,
145
- "pres_date": pres_read_date,
146
- "pres_value": pres_reading_value,
147
- "pres_type_code": pres_read_type,
148
- }
149
- )
150
-
151
-
152
- def _process_CCD2(elements, headers):
153
- breakdown = defaultdict(int)
154
-
155
- element_code = elements["TMOD"][0]
156
- headers["element_code"] = element_code
157
- try:
158
- eln_gbp, eln_rate, eln_cons = TMOD_MAP[element_code]
159
- except KeyError:
160
- raise BadRequest(f"Can't find the element code {element_code} in the TMOD_MAP.")
161
-
162
- cons = elements["CONS"]
163
- kwh = Decimal("0")
164
- if eln_cons is not None and len(cons[0]) > 0:
165
- el_cons = to_decimal(cons) / Decimal("1000")
166
- if eln_gbp == "duos-availability-gbp":
167
- breakdown[eln_cons] = [el_cons]
168
- else:
169
- breakdown[eln_cons] = kwh = el_cons
170
-
171
- if eln_rate is not None:
172
- rate = to_decimal(elements["BPRI"]) / Decimal("100000")
173
- breakdown[eln_rate] = [rate]
174
-
175
- start_date = to_date(elements["CSDT"][0])
176
- headers["bill_start_date"] = start_date
177
-
178
- finish_date = to_date(elements["CEDT"][0]) - HH
179
- headers["bill_finish_date"] = finish_date
180
-
181
- if "CTOT" in elements:
182
- net = Decimal("0.00") + to_decimal(elements["CTOT"]) / Decimal("100")
183
- else:
184
- net = Decimal("0.00")
185
-
186
- breakdown[eln_gbp] = net
187
- breakdown["raw-lines"] = [headers["line"]]
188
-
189
- try:
190
- reads = headers["reads"]
191
- headers["reads"] = []
192
- except KeyError:
193
- reads = []
194
-
195
- bill = {
196
- "bill_type_code": headers["bill_type_code"],
197
- "reference": headers["reference"] + "_" + eln_gbp[:-4],
198
- "issue_date": headers["issue_date"],
199
- "start_date": start_date,
200
- "finish_date": finish_date,
201
- "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0"),
202
- "net": net,
203
- "vat": Decimal("0.00"),
204
- "gross": net,
205
- "breakdown": breakdown,
206
- "reads": reads,
207
- }
208
- headers["bills"].append(bill)
209
-
210
-
211
- def _process_CCD3(elements, headers):
212
- breakdown = defaultdict(int)
213
-
214
- element_code = elements["TMOD"][0]
215
- headers["element_code"] = element_code
216
- try:
217
- eln_gbp, eln_rate, eln_cons = TMOD_MAP[element_code]
218
- except KeyError:
219
- raise BadRequest(f"Can't find the element code {element_code} in the TMOD_MAP.")
220
-
221
- cons = elements["CONS"]
222
- if eln_cons is not None and len(cons[0]) > 0:
223
- el_cons = to_decimal(cons) / Decimal("1000")
224
- breakdown[eln_cons] = kwh = el_cons
225
- else:
226
- kwh = Decimal("0")
227
-
228
- bpri = elements["BPRI"]
229
- if len(bpri[0]) > 0:
230
- rate = to_decimal(bpri) / Decimal("100000")
231
- breakdown[eln_rate] = [rate]
232
-
233
- start_date = to_date(elements["CSDT"][0])
234
- headers["bill_start_date"] = start_date
235
-
236
- finish_date = to_date(elements["CEDT"][0]) - HH
237
- headers["bill_finish_date"] = finish_date
238
-
239
- if "CTOT" in elements:
240
- net = Decimal("0.00") + to_decimal(elements["CTOT"]) / Decimal("100")
241
- else:
242
- net = Decimal("0.00")
243
-
244
- breakdown[eln_gbp] = net
245
- breakdown["raw-lines"] = [headers["line"]]
246
-
247
- try:
248
- reads = headers["reads"]
249
- headers["reads"] = []
250
- except KeyError:
251
- reads = []
252
-
253
- bill = {
254
- "bill_type_code": headers["bill_type_code"],
255
- "issue_date": headers["issue_date"],
256
- "reference": headers["reference"] + "_" + eln_gbp[:-4],
257
- "start_date": start_date,
258
- "finish_date": finish_date,
259
- "net": net,
260
- "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0"),
261
- "vat": Decimal("0.00"),
262
- "gross": net,
263
- "breakdown": breakdown,
264
- "reads": reads,
265
- }
266
-
267
- headers["bills"].append(bill)
268
-
269
-
270
- def _process_CCD4(elements, headers):
271
- breakdown = defaultdict(int)
272
-
273
- element_code = elements["TMOD"][0]
274
- headers["element_code"] = element_code
275
- try:
276
- eln_gbp, eln_rate, eln_cons = TMOD_MAP[element_code]
277
- except KeyError:
278
- raise BadRequest(f"Can't find the element code {element_code} in the TMOD_MAP.")
279
-
280
- cons = elements["CONS"]
281
- if eln_cons is not None and len(cons[0]) > 0:
282
- el_cons = to_decimal(cons, "1000")
283
- breakdown[eln_cons] = kwh = el_cons
284
-
285
- if eln_rate is not None:
286
- rate = to_decimal(elements["BPRI"], "100000")
287
- breakdown[eln_rate] = [rate]
288
-
289
- start_date = to_date(elements["CSDT"][0])
290
- headers["bill_start_date"] = start_date
291
-
292
- finish_date = to_date(elements["CEDT"][0]) - HH
293
- headers["bill_finish_date"] = finish_date
294
-
295
- if "CTOT" in elements:
296
- net = Decimal("0.00") + to_decimal(elements["CTOT"], "100")
297
- else:
298
- net = Decimal("0.00")
299
-
300
- breakdown[eln_gbp] = net
301
- breakdown["raw-lines"] = [headers["line"]]
302
-
303
- try:
304
- reads = headers["reads"]
305
- del headers["reads"][:]
306
- except KeyError:
307
- reads = []
308
-
309
- bill = {
310
- "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0.00"),
311
- "reference": headers["reference"] + "_" + eln_gbp[:-4],
312
- "issue_date": headers["issue_date"],
313
- "start_date": start_date,
314
- "finish_date": finish_date,
315
- "net": net,
316
- "vat": Decimal("0.00"),
317
- "gross": net,
318
- "breakdown": breakdown,
319
- "reads": reads,
320
- "bill_type_code": headers["bill_type_code"],
321
- }
322
- headers["bills"].append(bill)
323
-
324
-
325
- def _process_CDT(elements, headers):
326
- customer_id = elements["CIDN"][0]
327
- headers["customer_number"] = customer_id
328
-
329
-
330
- def _process_CLO(elements, headers):
331
- cloc = elements["CLOC"]
332
- headers["account"] = cloc[1]
333
-
334
-
335
- def _process_END(elements, headers):
336
- pass
337
-
338
-
339
- def _process_MAN(elements, headers):
340
- madn = elements["MADN"]
341
-
342
- headers["mpan_core"] = parse_mpan_core("".join(madn[0:3]))
343
-
344
-
345
- def _process_MHD(elements, headers):
346
- message_type = elements["TYPE"][0]
347
- if message_type == "UTLBIL":
348
- keep_keys = {"customer_number"}
349
- keep = {k: headers[k] for k in keep_keys}
350
- headers.clear()
351
- headers.update(keep)
352
- headers["bills"] = []
353
-
354
-
355
- def _process_MTR(elements, headers):
356
- pass
357
-
358
-
359
- def _process_VAT(elements, headers):
360
- vat = Decimal("0.00") + to_decimal(elements["UVTT"]) / Decimal("100")
361
- vat_percentage = to_decimal(elements["VATP"]) / Decimal("1000")
362
- vat_net = Decimal("0.00") + to_decimal(elements["UVLA"]) / Decimal("100")
363
-
364
- bill = {
365
- "bill_type_code": headers["bill_type_code"],
366
- "account": headers["account"],
367
- "mpan_core": headers["mpan_core"],
368
- "reference": headers["reference"] + "_vat",
369
- "issue_date": headers["issue_date"],
370
- "start_date": headers["bill_start_date"],
371
- "finish_date": headers["bill_finish_date"],
372
- "kwh": Decimal("0.00"),
373
- "net": Decimal("0.00"),
374
- "vat": vat,
375
- "gross": vat,
376
- "breakdown": {
377
- "raw-lines": [headers["line"]],
378
- "vat": {vat_percentage: {"vat": vat, "net": vat_net}},
379
- },
380
- "reads": [],
381
- }
382
- headers["bills"].append(bill)
383
-
384
-
385
- def _process_NOOP(elements, headers):
386
- pass
387
-
388
-
389
- CODE_FUNCS = {
390
- "BCD": _process_BCD,
391
- "BTL": _process_BTL,
392
- "CCD1": _process_CCD1,
393
- "CCD2": _process_CCD2,
394
- "CCD3": _process_CCD3,
395
- "CCD4": _process_CCD4,
396
- "CDT": _process_CDT,
397
- "CLO": _process_CLO,
398
- "DNA": _process_NOOP,
399
- "END": _process_END,
400
- "FIL": _process_NOOP,
401
- "MAN": _process_MAN,
402
- "MHD": _process_MHD,
403
- "MTR": _process_MTR,
404
- "SDT": _process_NOOP,
405
- "STX": _process_NOOP,
406
- "TYP": _process_NOOP,
407
- "TTL": _process_NOOP,
408
- "VAT": _process_VAT,
409
- "VTS": _process_NOOP,
410
- }
411
-
412
-
413
- def _customer_mods(headers, bill):
414
- if headers["customer_number"] == "WESSEXWAT":
415
- if (
416
- headers["element_code"] == "307660"
417
- and "ro-gbp" in bill["breakdown"]
418
- and bill["issue_date"] == to_utc(ct_datetime(2023, 4, 14))
419
- and bill["start_date"] == to_utc(ct_datetime(2023, 3, 1))
420
- and bill["finish_date"] == to_utc(ct_datetime(2023, 3, 31, 23, 30))
421
- ):
422
- bill["start_date"] = to_utc(ct_datetime(2021, 4, 1))
423
- bill["finish_date"] = to_utc(ct_datetime(2022, 3, 31, 23, 30))
424
-
425
- return bill
426
-
427
-
428
- class Parser:
429
- def __init__(self, f):
430
- self.edi_str = str(f.read(), "utf-8", errors="ignore")
431
- self.line_number = None
432
-
433
- def make_raw_bills(self):
434
- bills = []
435
- headers = {"bills": []}
436
- for self.line_number, line, seg_name, elements in parse_edi(self.edi_str):
437
- headers["line"] = line
438
- try:
439
- func = CODE_FUNCS[seg_name]
440
- except KeyError:
441
- raise BadRequest(f"Code {seg_name} not recognized.")
442
-
443
- try:
444
- bills_chunk = func(elements, headers)
445
- except BadRequest as e:
446
- raise BadRequest(
447
- f"{e.description} on line {self.line_number} line {line} "
448
- f"seg_name {seg_name} elements {elements}"
449
- )
450
- except BaseException as e:
451
- raise BadRequest(
452
- f"{e} on line {self.line_number} line {line} "
453
- f"seg_name {seg_name} elements {elements}"
454
- ) from e
455
-
456
- if bills_chunk is not None:
457
- bills.extend(bills_chunk)
458
-
459
- return bills