chellow 1750675713.0.0__py3-none-any.whl → 1751459327.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.

@@ -4,15 +4,8 @@ from decimal import Decimal
4
4
 
5
5
  from werkzeug.exceptions import BadRequest
6
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
7
+ from chellow.edi_lib import parse_edi, to_date, to_decimal, to_finish_date
8
+ from chellow.utils import HH, parse_mpan_core
16
9
 
17
10
 
18
11
  read_type_map = {
@@ -28,127 +21,136 @@ read_type_map = {
28
21
  "12": "IF",
29
22
  }
30
23
 
31
-
32
- TMOD_MAP = {
33
- "139039": ("aahedc-gbp", "aahedc-rate", "aahedc-kwh"),
24
+ ELEMENT_MAP = {
25
+ "AAH": {
26
+ "AAHEDC": {
27
+ "139039": ("aahedc-gbp", "aahedc-rate", "aahedc-kwh"),
28
+ },
29
+ },
30
+ "ADH": {
31
+ "ADHOC": {
32
+ "020330": ("eii-gbp", None, None),
33
+ "637050": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
34
+ },
35
+ },
36
+ "BUS": {
37
+ "BSUOS": {
38
+ "269100": ("bsuos-gbp", "bsuos-rate", "bsuos-kwh"),
39
+ },
40
+ },
41
+ "CCL": {
42
+ "CCL": {
43
+ "422733": ("ccl-gbp", "ccl-rate", "ccl-kwh"),
44
+ },
45
+ },
46
+ "CFD": {
47
+ "CFD001": {
48
+ "273237": (
49
+ "cfd-operational-gbp",
50
+ "cfd-operational-rate",
51
+ "cfd-operational-kwh",
52
+ ),
53
+ "954379": ("cfd-interim-gbp", "cfd-interim-rate", "cfd-interim-kwh"),
54
+ "538249": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
55
+ "568307": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
56
+ },
57
+ },
58
+ "DCA": {
59
+ "DCDA": {
60
+ "095469": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
61
+ },
62
+ },
63
+ "DUS": {
64
+ "DUS001": {
65
+ "794486": (
66
+ "duos-availability-gbp",
67
+ "duos-availability-rate",
68
+ "duos-availability-kva",
69
+ ),
70
+ "644819": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
71
+ "797790": (
72
+ "duos-reactive-gbp",
73
+ "duos-reactive-rate",
74
+ "duos-reactive-kvarh",
75
+ ),
76
+ "806318": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
77
+ "716514": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
78
+ "769979": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
79
+ "709522": (
80
+ "duos-excess-availability-gbp",
81
+ "duos-excess-availability-rate",
82
+ "duos-excess-availability-kva",
83
+ ),
84
+ "209269": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
85
+ },
86
+ "DUS002": {
87
+ "797790": (
88
+ "duos-reactive-gbp",
89
+ "duos-reactive-rate",
90
+ "duos-reactive-kvarh",
91
+ ),
92
+ "806318": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
93
+ "716514": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
94
+ "709522": (
95
+ "duos-excess-availability-gbp",
96
+ "duos-excess-availability-rate",
97
+ "duos-excess-availability-kva",
98
+ ),
99
+ "769979": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
100
+ "644819": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
101
+ "794486": (
102
+ "duos-availability-gbp",
103
+ "duos-availability-rate",
104
+ "duos-availability-kva",
105
+ ),
106
+ "209269": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
107
+ },
108
+ "DUSDIS": {
109
+ "122568": ("nrg-gsp-losses-gbp", "nrg-rate", "nrg-gsp-losses-kwh"),
110
+ },
111
+ "DUSTRN": {
112
+ "122568": ("nrg-nbp-losses-gbp", "nrg-rate", "nrg-bp-losses-kwh"),
113
+ },
114
+ },
115
+ "ELX": {
116
+ "ELEXON": {
117
+ "489920": ("elexon-gbp", "elexon-rate", "elexon-nbp-kwh"),
118
+ },
119
+ },
120
+ "FIT": {
121
+ "FIT_LV": {
122
+ "704107": ("fit-gbp", "fit-rate", "fit-kwh"),
123
+ },
124
+ },
125
+ "NRG": {
126
+ "HH0002": {
127
+ "033667": ("management-gbp", "management-rate", "management-kwh"),
128
+ "091890": ("shape-gbp", "shape-rate", "shape-kwh"),
129
+ "122568": ("nrg-msp-gbp", "nrg-rate", "nrg-msp-kwh"),
130
+ },
131
+ },
132
+ "REN": {
133
+ "REN001": {
134
+ "229128": ("ro-gbp", "ro-rate", "ro-kwh"),
135
+ },
136
+ "REN002": {
137
+ "019090": ("rego-gbp", "rego-rate", "rego-kwh"),
138
+ },
139
+ },
140
+ "TUS": {
141
+ "TNUOS": {
142
+ "012069": ("tnuos-gbp", "tnuos-rate", None),
143
+ },
144
+ },
34
145
  "064305": ("fit-gbp", None, None),
35
146
  "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
147
  "439724": ("eii-gbp", None, None),
43
148
  "247610": ("eii-gbp", None, None),
44
149
  "930504": ("eii-gbp", None, None),
45
150
  "331201": ("eii-gbp", None, None),
46
151
  "307253": ("eii-gbp", "eii-rate", "eii-kwh"),
47
152
  "065950": ("eii-gbp", "eii-rate", "eii-kwh"),
48
- "095469": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
49
- "489920": ("elexon-gbp", "elexon-rate", "elexon-nbp-kwh"),
50
- "704107": ("fit-gbp", None, None),
51
- "019090": ("rego-gbp", "rego-rate", "rego-kwh"),
52
- "033667": ("management-gbp", "management-rate", "management-kwh"),
53
- "091890": ("shape-gbp", "shape-rate", "shape-kwh"),
54
- "122568": ("nrg-msp-gbp", "nrg-rate", "nrg-msp-kwh"),
55
- "716514": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
56
- "769979": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
57
- "794486": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
58
- "797790": ("duos-reactive-gbp", "duos-reactive-rate", "duos-reactive-kvarh"),
59
- "709522": (
60
- "duos-excess-availability-gbp",
61
- "duos-excess-availability-rate",
62
- "duos-excess-availability-kva",
63
- ),
64
- "644819": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
65
- "806318": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
66
- "209269": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
67
- "229128": ("ro-gbp", "ro-rate", "ro-kwh"),
68
- "012069": ("tnuos-gbp", None, None),
69
153
  }
70
- """
71
- "140114": ("reconciliation-gbp", None, None),
72
- "255204": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
73
- "345065": ("op-weekend-gbp", "op-weekend-rate", "op-weekend-kwh"),
74
- "350293": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
75
- "425779": ("ro-gbp", "ro-rate", "ro-kwh"),
76
- "534342": ("reconciliation-gbp", None, None),
77
- "583174": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
78
- "584867": ("aahedc-gbp", "aahedc-rate", "aahedc-kwh"),
79
- "946827": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
80
- "989534": ("bsuos-gbp", "bsuos-rate", "bsuos-kwh"),
81
- "117220": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
82
- "579387": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
83
- "558147": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
84
- "030025": ("ccl-gbp", "ccl-rate", "ccl-kwh"),
85
- "066540": ("ccl-gbp", "ccl-rate", "ccl-kwh"),
86
- "154164": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
87
- "281170": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
88
- "342094": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
89
- "378809": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
90
- "574015": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
91
- "810016": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
92
- "839829": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
93
- "649282": ("cfd-fit-gbp", "cfd-fit-rate", "cfd-fit-kwh"),
94
- "068476": ("day-gbp", "day-rate", "day-kwh"),
95
- "133186": ("day-gbp", "day-rate", "day-kwh"),
96
- "400434": ("day-gbp", "day-rate", "day-kwh"),
97
- "219182": (
98
- "duos-availability-gbp",
99
- "duos-availability-rate",
100
- "duos-availability-kva",
101
- ),
102
- "144424": (
103
- "duos-excess-availability-gbp",
104
- "duos-excess-availability-rate",
105
- "duos-excess-availability-kva",
106
- ),
107
- "301541": ("duos-fixed-gbp", None, None),
108
- "099335": ("duos-fixed-gbp", None, None),
109
- "873562": ("duos-fixed-gbp", None, None),
110
- "986159": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
111
- "838286": ("duos-reactive-gbp", "duos-reactive-rate", "duos-reactive-kvarh"),
112
- "242643": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
113
- "257304": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
114
- "661440": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
115
- "257305": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
116
- "661441": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
117
- "257303": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
118
- "661439": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
119
- "504364": ("ebrs-gbp", None, "ebrs-kwh"),
120
- "563023": ("ebrs-gbp", None, "ebrs-kwh"),
121
- "823408": ("ebrs-gbp", None, "ebrs-kwh"),
122
- "871593": ("ebrs-gbp", "ebrs-rate", "ebrs-kwh"),
123
- "873894": ("ebrs-gbp", "ebrs-rate", "ebrs-kwh"),
124
- "309707": ("fit-gbp", "fit-rate", "fit-kwh"),
125
- "310129": ("meter-rental-gbp", None, None),
126
- "452415": ("meter-rental-gbp", None, None),
127
- "371265": ("meter-rental-gbp", None, None),
128
- "544936": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
129
- "265091": ("night-gbp", "night-rate", "night-kwh"),
130
- "483457": ("peak-gbp", "peak-rate", "peak-kwh"),
131
- "975901": ("peak-shoulder-gbp", "peak-shoulder-rate", "peak-shoulder-kwh"),
132
- "994483": ("reconciliation-gbp", None, None),
133
- "637176": ("reconciliation-gbp", None, None),
134
- "913821": ("reconciliation-gbp", None, None),
135
- "307660": ("ro-gbp", "ro-rate", "ro-kwh"),
136
- "364252": ("ro-gbp", "ro-rate", "ro-kwh"),
137
- "378246": ("ro-gbp", "ro-rate", "ro-kwh"),
138
- "708848": ("ro-gbp", None, None),
139
- "632209": ("summer-night-gbp", "summer-night-rate", "summer-night-kwh"),
140
- "663682": ("summer-weekday-gbp", "summer-weekday-rate", "summer-weekday-kwh"),
141
- "299992": ("summer-weekend-gbp", "summer-weekend-rate", "summer-weekend-kwh"),
142
- "211000": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
143
- "790618": ("tnuos-gbp", None, None),
144
- "447769": ("triad-gbp", "triad-rate", "triad-kw"),
145
- "647721": ("triad-gbp", "triad-rate", "triad-kw"),
146
- "276631": ("triad-gbp", "triad-rate", "triad-kw"),
147
- "220894": ("winter-night-gbp", "winter-night-rate", "winter-night-kwh"),
148
- "264929": ("winter-weekday-gbp", "winter-weekday-rate", "winter-weekday-kwh"),
149
- "638187": ("winter-weekend-gbp", "winter-weekend-rate", "winter-weekend-kwh"),
150
- "700285": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
151
- """
152
154
 
153
155
  TPR_LOOKUP = {
154
156
  "Day": "00043",
@@ -163,12 +165,37 @@ def _process_BCD(elements, headers):
163
165
  issue_date = to_date(elements["IVDT"][0])
164
166
  reference = elements["INVN"][0]
165
167
  bill_type_code = elements["BTCD"][0]
168
+ sumo = elements["SUMO"]
169
+ headers["start_date"] = to_date(sumo[0])
170
+ headers["finish_date"] = to_date(sumo[1]) - HH
166
171
 
167
172
  headers["issue_date"] = issue_date
168
173
  headers["bill_type_code"] = bill_type_code
169
174
  headers["reference"] = reference
170
175
 
171
176
 
177
+ def _process_BTL(elements, headers):
178
+ uvlt = elements["UVLT"]
179
+ utva = elements["UTVA"]
180
+ tbtl = elements["TBTL"]
181
+
182
+ return {
183
+ "mpan_core": headers["mpan_core"],
184
+ "account": headers["account"],
185
+ "bill_type_code": headers["bill_type_code"],
186
+ "reference": headers["reference"],
187
+ "issue_date": headers["issue_date"],
188
+ "start_date": headers["start_date"],
189
+ "finish_date": headers["finish_date"],
190
+ "kwh": headers["kwh"],
191
+ "net": Decimal("0.00") + to_decimal(uvlt) / Decimal("100"),
192
+ "vat": Decimal("0.00") + to_decimal(utva) / Decimal("100"),
193
+ "gross": Decimal("0.00") + to_decimal(tbtl) / Decimal("100"),
194
+ "breakdown": headers["breakdown"],
195
+ "reads": headers["reads"],
196
+ }
197
+
198
+
172
199
  def _process_CCD1(elements, headers):
173
200
  tcod = elements["TCOD"]
174
201
  pres_read_date = to_finish_date(elements["PRDT"][0])
@@ -227,12 +254,11 @@ def _process_CCD2(elements, headers):
227
254
  element_code = elements["TMOD"][0]
228
255
  headers["element_code"] = element_code
229
256
  try:
230
- eln_gbp, eln_rate, eln_cons = TMOD_MAP[element_code]
257
+ eln_gbp, eln_rate, eln_cons = ELEMENT_MAP[element_code]
231
258
  except KeyError:
232
- raise BadRequest(f"Can't find the element code {element_code} in the TMOD_MAP.")
233
-
234
- m = elements["MLOC"][0]
235
- mpan_core = " ".join((m[:2], m[2:6], m[6:10], m[10:]))
259
+ raise BadRequest(
260
+ f"Can't find the element code {element_code} in the ELEMENT_MAP."
261
+ )
236
262
 
237
263
  cons = elements["CONS"]
238
264
  kwh = Decimal("0")
@@ -259,8 +285,7 @@ def _process_CCD2(elements, headers):
259
285
  net = Decimal("0.00")
260
286
 
261
287
  breakdown[eln_gbp] = net
262
-
263
- headers["mpan_core"] = mpan_core
288
+ breakdown["raw-lines"] = [headers["line"]]
264
289
 
265
290
  try:
266
291
  reads = headers["reads"]
@@ -268,12 +293,10 @@ def _process_CCD2(elements, headers):
268
293
  except KeyError:
269
294
  reads = []
270
295
 
271
- return {
296
+ bill = {
272
297
  "bill_type_code": headers["bill_type_code"],
273
298
  "reference": headers["reference"] + "_" + eln_gbp[:-4],
274
299
  "issue_date": headers["issue_date"],
275
- "mpan_core": mpan_core,
276
- "account": mpan_core,
277
300
  "start_date": start_date,
278
301
  "finish_date": finish_date,
279
302
  "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0"),
@@ -283,83 +306,59 @@ def _process_CCD2(elements, headers):
283
306
  "breakdown": breakdown,
284
307
  "reads": reads,
285
308
  }
309
+ headers["bills"].append(bill)
286
310
 
287
311
 
288
312
  def _process_CCD3(elements, headers):
289
- breakdown = defaultdict(int)
313
+ breakdown = headers["breakdown"]
290
314
 
291
- element_code = elements["TMOD"][0]
292
- headers["element_code"] = element_code
315
+ supplier_code = elements["CCDE"][2]
316
+ tariff_code = elements["TCOD"][0]
317
+ mod_code = elements["TMOD"][0]
293
318
  try:
294
- eln_gbp, eln_rate, eln_cons = TMOD_MAP[element_code]
319
+ eln_gbp, eln_rate, eln_cons = ELEMENT_MAP[supplier_code][tariff_code][mod_code]
295
320
  except KeyError:
296
- raise BadRequest(f"Can't find the element code {element_code} in the TMOD_MAP.")
297
-
298
- m = elements["MLOC"][0]
299
- mpan_core = " ".join((m[:2], m[2:6], m[6:10], m[10:]))
321
+ raise BadRequest(
322
+ f"Can't find the element key {supplier_code} -> {tariff_code} -> "
323
+ f"{mod_code} in the ELEMENT_MAP."
324
+ )
300
325
 
301
326
  cons = elements["CONS"]
302
- if eln_cons is not None and len(cons[0]) > 0:
327
+ kwh = Decimal("0")
328
+ if len(cons[0]) > 0:
303
329
  el_cons = to_decimal(cons) / Decimal("1000")
304
- breakdown[eln_cons] = kwh = el_cons
305
- else:
306
- kwh = Decimal("0")
307
-
308
- if eln_rate is not None:
309
- rate = to_decimal(elements["BPRI"]) / Decimal("100000")
310
- breakdown[eln_rate] = [rate]
311
-
312
- start_date = to_date(elements["CSDT"][0])
313
- headers["bill_start_date"] = start_date
314
-
315
- finish_date = to_date(elements["CEDT"][0]) - HH
316
- headers["bill_finish_date"] = finish_date
330
+ kwh = el_cons
331
+ breakdown[eln_cons] += kwh
332
+
333
+ bpri = elements["BPRI"]
334
+ if len(bpri[0]) > 0:
335
+ rate = to_decimal(bpri) / Decimal("100000")
336
+ if eln_rate in breakdown:
337
+ breakdown[eln_rate].add(rate)
338
+ else:
339
+ breakdown[eln_rate] = {rate}
317
340
 
318
341
  if "CTOT" in elements:
319
342
  net = Decimal("0.00") + to_decimal(elements["CTOT"]) / Decimal("100")
320
343
  else:
321
344
  net = Decimal("0.00")
322
345
 
323
- breakdown[eln_gbp] = net
324
-
325
- headers["mpan_core"] = mpan_core
326
-
327
- try:
328
- reads = headers["reads"]
329
- headers["reads"] = []
330
- except KeyError:
331
- reads = []
346
+ breakdown[eln_gbp] += net
332
347
 
333
- return {
334
- "bill_type_code": headers["bill_type_code"],
335
- "issue_date": headers["issue_date"],
336
- "reference": headers["reference"] + "_" + eln_gbp[:-4],
337
- "mpan_core": mpan_core,
338
- "account": mpan_core,
339
- "start_date": start_date,
340
- "finish_date": finish_date,
341
- "net": net,
342
- "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0"),
343
- "vat": Decimal("0.00"),
344
- "gross": net,
345
- "breakdown": breakdown,
346
- "reads": reads,
347
- }
348
+ if eln_gbp == "nrg-msp-gbp":
349
+ headers["kwh"] += kwh
348
350
 
349
351
 
350
352
  def _process_CCD4(elements, headers):
351
- breakdown = defaultdict(int)
353
+ breakdown = headers["breakdown"]
352
354
 
353
355
  element_code = elements["TMOD"][0]
354
356
  headers["element_code"] = element_code
355
357
  try:
356
- eln_gbp, eln_rate, eln_cons = TMOD_MAP[element_code]
358
+ eln_gbp, eln_rate, eln_cons = ELEMENT_MAP[element_code]
357
359
  except KeyError:
358
360
  raise BadRequest(f"Can't find the element code {element_code} in the TMOD_MAP.")
359
361
 
360
- m = elements["MLOC"][0]
361
- mpan_core = " ".join((m[:2], m[2:6], m[6:10], m[10:]))
362
-
363
362
  cons = elements["CONS"]
364
363
  if eln_cons is not None and len(cons[0]) > 0:
365
364
  el_cons = to_decimal(cons, "1000")
@@ -381,8 +380,7 @@ def _process_CCD4(elements, headers):
381
380
  net = Decimal("0.00")
382
381
 
383
382
  breakdown[eln_gbp] = net
384
-
385
- headers["mpan_core"] = mpan_core
383
+ breakdown["raw-lines"] = [headers["line"]]
386
384
 
387
385
  try:
388
386
  reads = headers["reads"]
@@ -390,12 +388,10 @@ def _process_CCD4(elements, headers):
390
388
  except KeyError:
391
389
  reads = []
392
390
 
393
- return {
391
+ bill = {
394
392
  "kwh": kwh if eln_gbp == "ro-gbp" else Decimal("0.00"),
395
393
  "reference": headers["reference"] + "_" + eln_gbp[:-4],
396
394
  "issue_date": headers["issue_date"],
397
- "mpan_core": mpan_core,
398
- "account": mpan_core,
399
395
  "start_date": start_date,
400
396
  "finish_date": finish_date,
401
397
  "net": net,
@@ -405,6 +401,7 @@ def _process_CCD4(elements, headers):
405
401
  "reads": reads,
406
402
  "bill_type_code": headers["bill_type_code"],
407
403
  }
404
+ headers["bills"].append(bill)
408
405
 
409
406
 
410
407
  def _process_CDT(elements, headers):
@@ -412,21 +409,39 @@ def _process_CDT(elements, headers):
412
409
  headers["customer_number"] = customer_id
413
410
 
414
411
 
412
+ def _process_CLO(elements, headers):
413
+ cloc = elements["CLOC"]
414
+ headers["account"] = cloc[1]
415
+
416
+
415
417
  def _process_END(elements, headers):
416
418
  pass
417
419
 
418
420
 
421
+ def _process_MAN(elements, headers):
422
+ madn = elements["MADN"]
423
+
424
+ headers["mpan_core"] = parse_mpan_core("".join(madn[0:3]))
425
+
426
+
419
427
  def _process_MHD(elements, headers):
420
428
  message_type = elements["TYPE"][0]
421
429
  if message_type == "UTLBIL":
430
+ last_line = headers["lines"][-1]
422
431
  keep_keys = {"customer_number"}
423
432
  keep = {k: headers[k] for k in keep_keys}
424
433
  headers.clear()
425
434
  headers.update(keep)
435
+ headers["lines"] = [last_line]
436
+ headers["breakdown"] = defaultdict(
437
+ int, {"vat": {}, "raw-lines": headers["lines"]}
438
+ )
439
+ headers["kwh"] = Decimal(0)
440
+ headers["reads"] = []
426
441
 
427
442
 
428
443
  def _process_MTR(elements, headers):
429
- pass
444
+ headers["lines"] = []
430
445
 
431
446
 
432
447
  def _process_VAT(elements, headers):
@@ -434,21 +449,7 @@ def _process_VAT(elements, headers):
434
449
  vat_percentage = to_decimal(elements["VATP"]) / Decimal("1000")
435
450
  vat_net = Decimal("0.00") + to_decimal(elements["UVLA"]) / Decimal("100")
436
451
 
437
- return {
438
- "bill_type_code": headers["bill_type_code"],
439
- "account": headers["mpan_core"],
440
- "mpan_core": headers["mpan_core"],
441
- "reference": headers["reference"] + "_vat",
442
- "issue_date": headers["issue_date"],
443
- "start_date": headers["bill_start_date"],
444
- "finish_date": headers["bill_finish_date"],
445
- "kwh": Decimal("0.00"),
446
- "net": Decimal("0.00"),
447
- "vat": vat,
448
- "gross": vat,
449
- "breakdown": {"vat": {vat_percentage: {"vat": vat, "net": vat_net}}},
450
- "reads": [],
451
- }
452
+ headers["breakdown"]["vat"][vat_percentage] = {"vat": vat, "net": vat_net}
452
453
 
453
454
 
454
455
  def _process_NOOP(elements, headers):
@@ -457,17 +458,17 @@ def _process_NOOP(elements, headers):
457
458
 
458
459
  CODE_FUNCS = {
459
460
  "BCD": _process_BCD,
460
- "BTL": _process_NOOP,
461
+ "BTL": _process_BTL,
461
462
  "CCD1": _process_CCD1,
462
463
  "CCD2": _process_CCD2,
463
464
  "CCD3": _process_CCD3,
464
465
  "CCD4": _process_CCD4,
465
466
  "CDT": _process_CDT,
466
- "CLO": _process_NOOP,
467
+ "CLO": _process_CLO,
467
468
  "DNA": _process_NOOP,
468
469
  "END": _process_END,
469
470
  "FIL": _process_NOOP,
470
- "MAN": _process_NOOP,
471
+ "MAN": _process_MAN,
471
472
  "MHD": _process_MHD,
472
473
  "MTR": _process_MTR,
473
474
  "SDT": _process_NOOP,
@@ -479,21 +480,6 @@ CODE_FUNCS = {
479
480
  }
480
481
 
481
482
 
482
- def _customer_mods(headers, bill):
483
- if headers["customer_number"] == "WESSEXWAT":
484
- if (
485
- headers["element_code"] == "307660"
486
- and "ro-gbp" in bill["breakdown"]
487
- and bill["issue_date"] == to_utc(ct_datetime(2023, 4, 14))
488
- and bill["start_date"] == to_utc(ct_datetime(2023, 3, 1))
489
- and bill["finish_date"] == to_utc(ct_datetime(2023, 3, 31, 23, 30))
490
- ):
491
- bill["start_date"] = to_utc(ct_datetime(2021, 4, 1))
492
- bill["finish_date"] = to_utc(ct_datetime(2022, 3, 31, 23, 30))
493
-
494
- return bill
495
-
496
-
497
483
  class Parser:
498
484
  def __init__(self, f):
499
485
  self.edi_str = str(f.read(), "utf-8", errors="ignore")
@@ -501,9 +487,9 @@ class Parser:
501
487
 
502
488
  def make_raw_bills(self):
503
489
  bills = []
504
- headers = {}
505
- bill = None
490
+ headers = {"lines": []}
506
491
  for self.line_number, line, seg_name, elements in parse_edi(self.edi_str):
492
+ headers["lines"].append(line)
507
493
  try:
508
494
  func = CODE_FUNCS[seg_name]
509
495
  except KeyError:
@@ -523,7 +509,6 @@ class Parser:
523
509
  ) from e
524
510
 
525
511
  if bill is not None:
526
- bill["breakdown"]["raw-lines"] = [line]
527
- bills.append(_customer_mods(headers, bill))
512
+ bills.append(bill)
528
513
 
529
514
  return bills