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.

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 -88
  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-1755614564.0.0.dist-info → chellow-1759155233.0.0.dist-info}/METADATA +2 -2
  72. {chellow-1755614564.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-1755614564.0.0.dist-info → chellow-1759155233.0.0.dist-info}/WHEEL +0 -0
@@ -1,18 +1,15 @@
1
- from collections import defaultdict
2
1
  from decimal import Decimal
3
2
 
4
3
 
5
4
  from werkzeug.exceptions import BadRequest
6
5
 
7
6
  from chellow.edi_lib import (
8
- ct_datetime,
9
7
  parse_edi,
10
8
  to_date,
11
9
  to_decimal,
12
10
  to_finish_date,
13
- to_utc,
14
11
  )
15
- from chellow.utils import HH
12
+ from chellow.utils import HH, parse_mpan_core
16
13
 
17
14
 
18
15
  read_type_map = {
@@ -30,86 +27,78 @@ read_type_map = {
30
27
 
31
28
 
32
29
  TCOD_MAP = {
33
- "140114": ("reconciliation-gbp", None, None),
34
- "255204": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
35
- "345065": ("op-weekend-gbp", "op-weekend-rate", "op-weekend-kwh"),
36
- "350293": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
37
- "425779": ("ro-gbp", "ro-rate", "ro-kwh"),
38
- "534342": ("reconciliation-gbp", None, None),
39
- "583174": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
40
- "584867": ("aahedc-gbp", "aahedc-rate", "aahedc-kwh"),
41
- "946827": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
42
- "989534": ("bsuos-gbp", "bsuos-rate", "bsuos-kwh"),
43
- "117220": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
44
- "579387": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
45
- "558147": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
46
- "030025": ("ccl-gbp", "ccl-rate", "ccl-kwh"),
47
- "066540": ("ccl-gbp", "ccl-rate", "ccl-kwh"),
48
- "154164": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
49
- "281170": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
50
- "342094": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
51
- "378809": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
52
- "574015": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
53
- "810016": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
54
- "839829": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
55
- "649282": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
56
- "068476": ("day-gbp", "day-rate", "day-kwh"),
57
- "133186": ("nrg-gbp", "nrg-rate", "nrg-kwh"),
58
- "400434": ("day-gbp", "day-rate", "day-kwh"),
59
- "219182": (
60
- "duos-availability-gbp",
61
- "duos-availability-rate",
62
- "duos-availability-kva",
63
- ),
64
- "144424": (
65
- "duos-excess-availability-gbp",
66
- "duos-excess-availability-rate",
67
- "duos-excess-availability-kva",
68
- ),
69
- "301541": ("duos-fixed-gbp", None, None),
70
- "099335": ("duos-fixed-gbp", None, None),
71
- "873562": ("duos-fixed-gbp", None, None),
72
- "986159": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
73
- "838286": ("duos-reactive-gbp", "duos-reactive-rate", "duos-reactive-kvarh"),
74
- "242643": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
75
- "257304": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
76
- "661440": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
77
- "257305": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
78
- "661441": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
79
- "257303": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
80
- "661439": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
81
- "504364": ("ebrs-gbp", None, "ebrs-kwh"),
82
- "563023": ("ebrs-gbp", None, "ebrs-kwh"),
83
- "823408": ("ebrs-gbp", None, "ebrs-kwh"),
84
- "871593": ("ebrs-gbp", "ebrs-rate", "ebrs-kwh"),
85
- "873894": ("ebrs-gbp", "ebrs-rate", "ebrs-kwh"),
86
- "309707": ("fit-gbp", "fit-rate", "fit-kwh"),
87
- "310129": ("meter-rental-gbp", None, None),
88
- "452415": ("meter-rental-gbp", None, None),
89
- "371265": ("meter-rental-gbp", None, None),
90
- "544936": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
91
- "265091": ("night-gbp", "night-rate", "night-kwh"),
92
- "483457": ("peak-gbp", "peak-rate", "peak-kwh"),
93
- "975901": ("peak-shoulder-gbp", "peak-shoulder-rate", "peak-shoulder-kwh"),
94
- "994483": ("reconciliation-gbp", None, None),
95
- "637176": ("reconciliation-gbp", None, None),
96
- "913821": ("reconciliation-gbp", None, None),
97
- "307660": ("ro-gbp", "ro-rate", "ro-kwh"),
98
- "364252": ("ro-gbp", "ro-rate", "ro-kwh"),
99
- "378246": ("ro-gbp", "ro-rate", "ro-kwh"),
100
- "708848": ("ro-gbp", None, None),
101
- "632209": ("summer-night-gbp", "summer-night-rate", "summer-night-kwh"),
102
- "663682": ("summer-weekday-gbp", "summer-weekday-rate", "summer-weekday-kwh"),
103
- "299992": ("summer-weekend-gbp", "summer-weekend-rate", "summer-weekend-kwh"),
104
- "211000": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
105
- "790618": ("tnuos-gbp", None, None),
106
- "447769": ("triad-gbp", "triad-rate", "triad-kw"),
107
- "647721": ("triad-gbp", "triad-rate", "triad-kw"),
108
- "276631": ("triad-gbp", "triad-rate", "triad-kw"),
109
- "220894": ("winter-night-gbp", "winter-night-rate", "winter-night-kwh"),
110
- "264929": ("winter-weekday-gbp", "winter-weekday-rate", "winter-weekday-kwh"),
111
- "638187": ("winter-weekend-gbp", "winter-weekend-rate", "winter-weekend-kwh"),
112
- "700285": ("standing-gbp", "standing-rate", "standing-days"),
30
+ "140114": ("reconciliation", None, None),
31
+ "255204": ("meter-rental", "rate", "days"),
32
+ "345065": ("op-weekend", "rate", "kwh"),
33
+ "350293": ("capacity", "rate", "kwh"),
34
+ "425779": ("ro", "rate", "kwh"),
35
+ "534342": ("reconciliation", None, None),
36
+ "583174": ("meter-rental", "rate", "days"),
37
+ "584867": ("aahedc", "rate", "kwh"),
38
+ "946827": ("meter-rental", "rate", "days"),
39
+ "989534": ("bsuos", "rate", "kwh"),
40
+ "117220": ("capacity", "rate", "kwh"),
41
+ "579387": ("capacity", "rate", "kwh"),
42
+ "558147": ("capacity", "rate", "kwh"),
43
+ "030025": ("ccl", "rate", "kwh"),
44
+ "066540": ("ccl", "rate", "kwh"),
45
+ "154164": ("cfd-fit", "rate", "kwh"),
46
+ "281170": ("cfd-fit", "rate", "fit-kwh"),
47
+ "342094": ("cfd-fit", "rate", "kwh"),
48
+ "378809": ("cfd-fit", "rate", "kwh"),
49
+ "574015": ("cfd-fit", "rate", "kwh"),
50
+ "810016": ("cfd-fit", "rate", "kwh"),
51
+ "839829": ("cfd-fit", "rate", "kwh"),
52
+ "649282": ("cfd-fit", "rate", "kwh"),
53
+ "068476": ("day", "rate", "kwh"),
54
+ "133186": ("nrg", "rate", "kwh"),
55
+ "400434": ("day", "rate", "kwh"),
56
+ "219182": ("duos-availability", "rate", "kva"),
57
+ "144424": ("duos-excess-availability", "rate", "kva"),
58
+ "301541": ("duos-fixed", None, None),
59
+ "099335": ("duos-fixed", None, None),
60
+ "873562": ("duos-fixed", None, None),
61
+ "986159": ("duos-fixed", "rate", "days"),
62
+ "838286": ("duos-reactive", "rate", "kvarh"),
63
+ "242643": ("duos-fixed", "rate", "days"),
64
+ "257304": ("duos-amber", "rate", "kwh"),
65
+ "661440": ("duos-amber", "rate", "kwh"),
66
+ "257305": ("duos-green", "rate", "kwh"),
67
+ "661441": ("duos-green", "rate", "kwh"),
68
+ "257303": ("duos-red", "rate", "kwh"),
69
+ "661439": ("duos-red", "rate", "kwh"),
70
+ "504364": ("ebrs", None, "kwh"),
71
+ "563023": ("ebrs", None, "kwh"),
72
+ "823408": ("ebrs", None, "kwh"),
73
+ "871593": ("ebrs", "rate", "kwh"),
74
+ "873894": ("ebrs", "rate", "kwh"),
75
+ "309707": ("fit", "rate", "kwh"),
76
+ "310129": ("meter-rental", None, None),
77
+ "452415": ("meter-rental", None, None),
78
+ "371265": ("meter-rental", None, None),
79
+ "544936": ("meter-rental", "rate", "days"),
80
+ "265091": ("night", "rate", "kwh"),
81
+ "483457": ("peak", "rate", "kwh"),
82
+ "975901": ("peak-shoulder", "rate", "kwh"),
83
+ "994483": ("reconciliation", None, None),
84
+ "637176": ("reconciliation", None, None),
85
+ "913821": ("reconciliation", None, None),
86
+ "307660": ("ro", "rate", "kwh"),
87
+ "364252": ("ro", "rate", "kwh"),
88
+ "378246": ("ro", "rate", "kwh"),
89
+ "708848": ("ro", None, None),
90
+ "632209": ("summer-night", "rate", "kwh"),
91
+ "663682": ("summer-weekday", "rate", "kwh"),
92
+ "299992": ("summer-weekend", "rate", "kwh"),
93
+ "211000": ("tnuos", "rate", "days"),
94
+ "790618": ("tnuos", None, None),
95
+ "447769": ("triad", "rate", "kw"),
96
+ "647721": ("triad", "rate", "kw"),
97
+ "276631": ("triad", "rate", "kw"),
98
+ "220894": ("winter-night", "rate", "kwh"),
99
+ "264929": ("winter-weekday", "rate", "kwh"),
100
+ "638187": ("winter-weekend", "rate", "kwh"),
101
+ "700285": ("standing", "rate", "days"),
113
102
  }
114
103
 
115
104
  TPR_LOOKUP = {
@@ -125,10 +114,40 @@ def _process_BCD(elements, headers):
125
114
  issue_date = to_date(elements["IVDT"][0])
126
115
  reference = elements["INVN"][0]
127
116
  bill_type_code = elements["BTCD"][0]
117
+ sumo = elements["SUMO"]
128
118
 
129
119
  headers["issue_date"] = issue_date
120
+ headers["start_date"] = to_date(sumo[0])
121
+ headers["finish_date"] = to_date(sumo[1]) - HH
130
122
  headers["bill_type_code"] = bill_type_code
131
123
  headers["reference"] = reference
124
+ headers["elements"] = []
125
+ headers["reads"] = []
126
+ headers["breakdown"] = {}
127
+ headers["kwh"] = Decimal("0")
128
+
129
+
130
+ def _process_BTL(elements, headers):
131
+ uvlt = elements["UVLT"]
132
+ utva = elements["UTVA"]
133
+ tbtl = elements["TBTL"]
134
+
135
+ return {
136
+ "bill_type_code": headers["bill_type_code"],
137
+ "reference": headers["reference"],
138
+ "issue_date": headers["issue_date"],
139
+ "mpan_core": headers["mpan_core"],
140
+ "account": headers["account"],
141
+ "start_date": headers["start_date"],
142
+ "finish_date": headers["finish_date"],
143
+ "kwh": headers["kwh"],
144
+ "net": Decimal("0.00") + to_decimal(uvlt) / Decimal("100"),
145
+ "vat": Decimal("0.00") + to_decimal(utva) / Decimal("100"),
146
+ "gross": Decimal("0.00") + to_decimal(tbtl) / Decimal("100"),
147
+ "breakdown": headers["breakdown"],
148
+ "reads": headers["reads"],
149
+ "elements": headers["elements"],
150
+ }
132
151
 
133
152
 
134
153
  def _process_CCD1(elements, headers):
@@ -161,12 +180,7 @@ def _process_CCD1(elements, headers):
161
180
  units = "kWh"
162
181
  tpr_code = TPR_LOOKUP[tcod[1]]
163
182
 
164
- try:
165
- reads = headers["reads"]
166
- except KeyError:
167
- reads = headers["reads"] = []
168
-
169
- reads.append(
183
+ headers["reads"].append(
170
184
  {
171
185
  "msn": msn,
172
186
  "mpan": mpan,
@@ -184,189 +198,112 @@ def _process_CCD1(elements, headers):
184
198
 
185
199
 
186
200
  def _process_CCD2(elements, headers):
187
- breakdown = defaultdict(int)
201
+ breakdown = {}
188
202
 
189
203
  element_code = elements["TCOD"][0]
190
- headers["element_code"] = element_code
191
204
  try:
192
- eln_gbp, eln_rate, eln_cons = TCOD_MAP[element_code]
205
+ eln_name, eln_rate, eln_cons = TCOD_MAP[element_code]
193
206
  except KeyError:
194
207
  raise BadRequest(f"Can't find the element code {element_code} in the TCOD_MAP.")
195
208
 
196
- m = elements["MLOC"][0]
197
- mpan_core = " ".join((m[:2], m[2:6], m[6:10], m[10:]))
198
-
199
209
  cons = elements["CONS"]
200
- kwh = Decimal("0")
201
210
  if eln_cons is not None and len(cons[0]) > 0:
202
211
  el_cons = to_decimal(cons) / Decimal("1000")
203
- if eln_gbp == "duos-availability-gbp":
204
- breakdown[eln_cons] = [el_cons]
212
+ if eln_name == "duos-availability":
213
+ breakdown[eln_cons] = {el_cons}
205
214
  else:
206
- breakdown[eln_cons] = kwh = el_cons
215
+ breakdown[eln_cons] = el_cons
216
+ if eln_name == "ro":
217
+ headers["kwh"] += el_cons
207
218
 
208
219
  if eln_rate is not None:
209
220
  rate = to_decimal(elements["BPRI"]) / Decimal("100000")
210
- breakdown[eln_rate] = [rate]
211
-
212
- start_date = to_date(elements["CSDT"][0])
213
- headers["bill_start_date"] = start_date
214
-
215
- finish_date = to_date(elements["CEDT"][0]) - HH
216
- headers["bill_finish_date"] = finish_date
221
+ breakdown[eln_rate] = {rate}
217
222
 
223
+ net = Decimal("0.00")
218
224
  if "CTOT" in elements:
219
- net = Decimal("0.00") + to_decimal(elements["CTOT"]) / Decimal("100")
220
- else:
221
- net = Decimal("0.00")
222
-
223
- breakdown[eln_gbp] = net
224
-
225
- headers["mpan_core"] = mpan_core
226
-
227
- try:
228
- reads = headers["reads"]
229
- headers["reads"] = []
230
- except KeyError:
231
- reads = []
225
+ net += to_decimal(elements["CTOT"]) / Decimal("100")
232
226
 
233
- return {
234
- "bill_type_code": headers["bill_type_code"],
235
- "reference": headers["reference"] + "_" + eln_gbp[:-4],
236
- "issue_date": headers["issue_date"],
237
- "mpan_core": mpan_core,
238
- "account": mpan_core,
239
- "start_date": start_date,
240
- "finish_date": finish_date,
241
- "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0"),
242
- "net": net,
243
- "vat": Decimal("0.00"),
244
- "gross": net,
245
- "breakdown": breakdown,
246
- "reads": reads,
247
- }
227
+ headers["elements"].append(
228
+ {
229
+ "name": eln_name,
230
+ "start_date": to_date(elements["CSDT"][0]),
231
+ "finish_date": to_date(elements["CEDT"][0]) - HH,
232
+ "net": net,
233
+ "breakdown": breakdown,
234
+ }
235
+ )
248
236
 
249
237
 
250
238
  def _process_CCD3(elements, headers):
251
- breakdown = defaultdict(int)
239
+ breakdown = {}
252
240
 
253
241
  element_code = elements["TCOD"][0]
254
- headers["element_code"] = element_code
255
242
  try:
256
- eln_gbp, eln_rate, eln_cons = TCOD_MAP[element_code]
243
+ eln_name, eln_rate, eln_cons = TCOD_MAP[element_code]
257
244
  except KeyError:
258
245
  raise BadRequest(f"Can't find the element code {element_code} in the TCOD_MAP.")
259
246
 
260
- m = elements["MLOC"][0]
261
- mpan_core = " ".join((m[:2], m[2:6], m[6:10], m[10:]))
262
-
263
247
  cons = elements["CONS"]
264
248
  if eln_cons is not None and len(cons[0]) > 0:
265
249
  el_cons = to_decimal(cons) / Decimal("1000")
266
- breakdown[eln_cons] = kwh = el_cons
267
- else:
268
- kwh = Decimal("0")
250
+ breakdown[eln_cons] = el_cons
251
+ if eln_name == "ro":
252
+ headers["kwh"] += el_cons
269
253
 
270
254
  if eln_rate is not None:
271
255
  rate = to_decimal(elements["BPRI"]) / Decimal("100000")
272
- breakdown[eln_rate] = [rate]
273
-
274
- start_date = to_date(elements["CSDT"][0])
275
- headers["bill_start_date"] = start_date
276
-
277
- finish_date = to_date(elements["CEDT"][0]) - HH
278
- headers["bill_finish_date"] = finish_date
256
+ breakdown[eln_rate] = {rate}
279
257
 
258
+ net = Decimal("0.00")
280
259
  if "CTOT" in elements:
281
- net = Decimal("0.00") + to_decimal(elements["CTOT"]) / Decimal("100")
282
- else:
283
- net = Decimal("0.00")
284
-
285
- breakdown[eln_gbp] = net
260
+ net += to_decimal(elements["CTOT"]) / Decimal("100")
286
261
 
287
- headers["mpan_core"] = mpan_core
288
-
289
- try:
290
- reads = headers["reads"]
291
- headers["reads"] = []
292
- except KeyError:
293
- reads = []
294
-
295
- return {
296
- "bill_type_code": headers["bill_type_code"],
297
- "issue_date": headers["issue_date"],
298
- "reference": headers["reference"] + "_" + eln_gbp[:-4],
299
- "mpan_core": mpan_core,
300
- "account": mpan_core,
301
- "start_date": start_date,
302
- "finish_date": finish_date,
303
- "net": net,
304
- "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0"),
305
- "vat": Decimal("0.00"),
306
- "gross": net,
307
- "breakdown": breakdown,
308
- "reads": reads,
309
- }
262
+ headers["elements"].append(
263
+ {
264
+ "name": eln_name,
265
+ "start_date": to_date(elements["CSDT"][0]),
266
+ "finish_date": to_date(elements["CEDT"][0]) - HH,
267
+ "net": net,
268
+ "breakdown": breakdown,
269
+ }
270
+ )
310
271
 
311
272
 
312
273
  def _process_CCD4(elements, headers):
313
- breakdown = defaultdict(int)
274
+ breakdown = {}
314
275
 
315
276
  element_code = elements["TCOD"][0]
316
- headers["element_code"] = element_code
277
+
317
278
  try:
318
- eln_gbp, eln_rate, eln_cons = TCOD_MAP[element_code]
279
+ eln_name, eln_rate, eln_cons = TCOD_MAP[element_code]
319
280
  except KeyError:
320
281
  raise BadRequest(f"Can't find the element code {element_code} in the TCOD_MAP.")
321
282
 
322
- m = elements["MLOC"][0]
323
- mpan_core = " ".join((m[:2], m[2:6], m[6:10], m[10:]))
324
-
325
283
  cons = elements["CONS"]
326
284
  if eln_cons is not None and len(cons[0]) > 0:
327
285
  el_cons = to_decimal(cons, "1000")
328
- breakdown[eln_cons] = kwh = el_cons
286
+ breakdown[eln_cons] = el_cons
287
+ if eln_name == "ro":
288
+ headers["kwh"] += el_cons
329
289
 
330
290
  if eln_rate is not None:
331
291
  rate = to_decimal(elements["BPRI"], "100000")
332
292
  breakdown[eln_rate] = [rate]
333
293
 
334
- start_date = to_date(elements["CSDT"][0])
335
- headers["bill_start_date"] = start_date
336
-
337
- finish_date = to_date(elements["CEDT"][0]) - HH
338
- headers["bill_finish_date"] = finish_date
339
-
294
+ net = Decimal("0.00")
340
295
  if "CTOT" in elements:
341
- net = Decimal("0.00") + to_decimal(elements["CTOT"], "100")
342
- else:
343
- net = Decimal("0.00")
344
-
345
- breakdown[eln_gbp] = net
346
-
347
- headers["mpan_core"] = mpan_core
348
-
349
- try:
350
- reads = headers["reads"]
351
- del headers["reads"][:]
352
- except KeyError:
353
- reads = []
296
+ net += to_decimal(elements["CTOT"], "100")
354
297
 
355
- return {
356
- "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0.00"),
357
- "reference": headers["reference"] + "_" + eln_gbp[:-4],
358
- "issue_date": headers["issue_date"],
359
- "mpan_core": mpan_core,
360
- "account": mpan_core,
361
- "start_date": start_date,
362
- "finish_date": finish_date,
363
- "net": net,
364
- "vat": Decimal("0.00"),
365
- "gross": net,
366
- "breakdown": breakdown,
367
- "reads": reads,
368
- "bill_type_code": headers["bill_type_code"],
369
- }
298
+ headers["elements"].append(
299
+ {
300
+ "name": eln_name,
301
+ "start_date": to_date(elements["CSDT"][0]),
302
+ "finish_date": to_date(elements["CEDT"][0]) - HH,
303
+ "net": net,
304
+ "breakdown": breakdown,
305
+ }
306
+ )
370
307
 
371
308
 
372
309
  def _process_CDT(elements, headers):
@@ -374,10 +311,20 @@ def _process_CDT(elements, headers):
374
311
  headers["customer_number"] = customer_id
375
312
 
376
313
 
314
+ def _process_CLO(elements, headers):
315
+ cloc = elements["CLOC"]
316
+ headers["account"] = cloc[2]
317
+
318
+
377
319
  def _process_END(elements, headers):
378
320
  pass
379
321
 
380
322
 
323
+ def _process_MAN(elements, headers):
324
+ madn = elements["MADN"]
325
+ headers["mpan_core"] = parse_mpan_core("".join(madn[:3]))
326
+
327
+
381
328
  def _process_MHD(elements, headers):
382
329
  message_type = elements["TYPE"][0]
383
330
  if message_type == "UTLBIL":
@@ -396,21 +343,14 @@ def _process_VAT(elements, headers):
396
343
  vat_percentage = to_decimal(elements["VATP"]) / Decimal("1000")
397
344
  vat_net = Decimal("0.00") + to_decimal(elements["UVLA"]) / Decimal("100")
398
345
 
399
- return {
400
- "bill_type_code": headers["bill_type_code"],
401
- "account": headers["mpan_core"],
402
- "mpan_core": headers["mpan_core"],
403
- "reference": headers["reference"] + "_vat",
404
- "issue_date": headers["issue_date"],
405
- "start_date": headers["bill_start_date"],
406
- "finish_date": headers["bill_finish_date"],
407
- "kwh": Decimal("0.00"),
408
- "net": Decimal("0.00"),
409
- "vat": vat,
410
- "gross": vat,
411
- "breakdown": {"vat": {vat_percentage: {"vat": vat, "net": vat_net}}},
412
- "reads": [],
413
- }
346
+ breakdown = headers["breakdown"]
347
+
348
+ try:
349
+ vat_bd = breakdown["vat"]
350
+ except KeyError:
351
+ vat_bd = breakdown["vat"] = {}
352
+
353
+ vat_bd[vat_percentage] = {"vat": vat, "net": vat_net}
414
354
 
415
355
 
416
356
  def _process_NOOP(elements, headers):
@@ -419,17 +359,17 @@ def _process_NOOP(elements, headers):
419
359
 
420
360
  CODE_FUNCS = {
421
361
  "BCD": _process_BCD,
422
- "BTL": _process_NOOP,
362
+ "BTL": _process_BTL,
423
363
  "CCD1": _process_CCD1,
424
364
  "CCD2": _process_CCD2,
425
365
  "CCD3": _process_CCD3,
426
366
  "CCD4": _process_CCD4,
427
367
  "CDT": _process_CDT,
428
- "CLO": _process_NOOP,
368
+ "CLO": _process_CLO,
429
369
  "DNA": _process_NOOP,
430
370
  "END": _process_END,
431
371
  "FIL": _process_NOOP,
432
- "MAN": _process_NOOP,
372
+ "MAN": _process_MAN,
433
373
  "MHD": _process_MHD,
434
374
  "MTR": _process_MTR,
435
375
  "SDT": _process_NOOP,
@@ -441,21 +381,6 @@ CODE_FUNCS = {
441
381
  }
442
382
 
443
383
 
444
- def _customer_mods(headers, bill):
445
- if headers["customer_number"] == "WESSEXWAT":
446
- if (
447
- headers["element_code"] == "307660"
448
- and "ro-gbp" in bill["breakdown"]
449
- and bill["issue_date"] == to_utc(ct_datetime(2023, 4, 14))
450
- and bill["start_date"] == to_utc(ct_datetime(2023, 3, 1))
451
- and bill["finish_date"] == to_utc(ct_datetime(2023, 3, 31, 23, 30))
452
- ):
453
- bill["start_date"] = to_utc(ct_datetime(2021, 4, 1))
454
- bill["finish_date"] = to_utc(ct_datetime(2022, 3, 31, 23, 30))
455
-
456
- return bill
457
-
458
-
459
384
  class Parser:
460
385
  def __init__(self, f):
461
386
  self.edi_str = str(f.read(), "utf-8", errors="ignore")
@@ -465,7 +390,12 @@ class Parser:
465
390
  bills = []
466
391
  headers = {}
467
392
  bill = None
393
+ lines = []
468
394
  for self.line_number, line, seg_name, elements in parse_edi(self.edi_str):
395
+ if seg_name == "MHD":
396
+ lines = []
397
+ lines.append(line)
398
+
469
399
  try:
470
400
  func = CODE_FUNCS[seg_name]
471
401
  except KeyError:
@@ -485,7 +415,9 @@ class Parser:
485
415
  ) from e
486
416
 
487
417
  if bill is not None:
488
- bill["breakdown"]["raw-lines"] = [line]
489
- bills.append(_customer_mods(headers, bill))
418
+ bill["breakdown"]["raw-lines"] = lines
419
+ bills.append(bill)
420
+ if seg_name == "MTR":
421
+ lines = []
490
422
 
491
423
  return bills