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
@@ -28,124 +28,162 @@ def parse_date(row, idx, is_finish=False):
28
28
  else:
29
29
  dt = to_utc(ct_datetime_parse(date_str, "%Y-%m-%d %H:%M"))
30
30
  return validate_hh_start(dt)
31
- except ValueError as e:
31
+ except BaseException as e:
32
32
  raise BadRequest(
33
33
  f"Can't parse the date at column {idx}. The required spreadsheet format is "
34
34
  f"'YYYY-MM-DD HH:MM'. {e}"
35
35
  )
36
36
 
37
37
 
38
+ def to_decimal(vals, dec_index, dec_name, is_money=False):
39
+ try:
40
+ dec_str = vals[dec_index]
41
+ dec_str = dec_str.replace(",", "")
42
+ dec = Decimal(dec_str)
43
+ if is_money:
44
+ dec += Decimal("0.00")
45
+ dec = round(dec, 2)
46
+ return dec
47
+ except InvalidOperation as e:
48
+ raise BadRequest(
49
+ f"The value '{dec_str}' can't be parsed as a decimal: {e}. It's in "
50
+ f"the '{dec_name}' column at position {dec_index} in {vals}."
51
+ )
52
+ except IndexError:
53
+ raise BadRequest(
54
+ f"The field '{dec_name}' can't be found. It's expected at "
55
+ f"position {dec_index} in the list of fields."
56
+ )
57
+ except ValueError:
58
+ raise BadRequest(
59
+ f"The {dec_name} field '{dec_str}' cannot be parsed as a number. The "
60
+ f"{dec_name} field is the {dec_index} field of {vals}."
61
+ )
62
+
63
+
64
+ def _process_row(vals):
65
+ bill_type_code = vals[0]
66
+ account = vals[1]
67
+ mpan_core = parse_mpan_core(vals[2])
68
+ reference = vals[3]
69
+ issue_date = parse_date(vals, 4)
70
+ start_date = parse_date(vals, 5)
71
+ finish_date = parse_date(vals, 6, True)
72
+
73
+ kwh = to_decimal(vals, 7, "kwh")
74
+ net = to_decimal(vals, 8, "net", is_money=True)
75
+ vat = to_decimal(vals, 9, "vat", is_money=True)
76
+ gross = to_decimal(vals, 10, "gross", is_money=True)
77
+
78
+ if len(vals) > 11:
79
+ breakdown_str = vals[11].strip()
80
+ if len(breakdown_str) == 0:
81
+ breakdown = {}
82
+ else:
83
+ try:
84
+ breakdown = loads(breakdown_str)
85
+ except ZishLocationException as e:
86
+ raise BadRequest(str(e))
87
+ else:
88
+ raise BadRequest("There isn't a 'breakdown' field on the end.")
89
+
90
+ while vals[-1] == "" and len(vals) > 12:
91
+ del vals[-1]
92
+
93
+ reads = []
94
+ elements = []
95
+ i = 12
96
+ while i < len(vals):
97
+ typ = vals[i].strip().lower()
98
+ if typ == "read":
99
+ tpr_str = vals[i + 5].strip()
100
+ tpr_code = None if len(tpr_str) == 0 else tpr_str.zfill(5)
101
+ reads.append(
102
+ {
103
+ "msn": vals[i + 1],
104
+ "mpan": vals[i + 2],
105
+ "coefficient": to_decimal(vals, i + 3, "coefficient"),
106
+ "units": vals[i + 4],
107
+ "tpr_code": tpr_code,
108
+ "prev_date": parse_date(vals, i + 6),
109
+ "prev_value": Decimal(vals[i + 7]),
110
+ "prev_type_code": vals[i + 8],
111
+ "pres_date": parse_date(vals, i + 9),
112
+ "pres_value": Decimal(vals[i + 10]),
113
+ "pres_type_code": vals[i + 11],
114
+ }
115
+ )
116
+ i += 12
117
+ elif typ == "element":
118
+ breakdown_str = vals[i + 5].strip()
119
+ if len(breakdown_str) == 0:
120
+ breakdown = {}
121
+ else:
122
+ try:
123
+ breakdown = loads(breakdown_str)
124
+ except ZishLocationException as e:
125
+ raise BadRequest(str(e))
126
+ elements.append(
127
+ {
128
+ "name": vals[i + 1],
129
+ "start_date": parse_date(vals, i + 2),
130
+ "finish_date": parse_date(vals, i + 3),
131
+ "net": to_decimal(vals, i + 4, "net", is_money=True),
132
+ "breakdown": breakdown,
133
+ }
134
+ )
135
+ i += 6
136
+ else:
137
+ raise BadRequest("Record type {type} not recognized.")
138
+
139
+ if len(elements) == 0:
140
+ elements.append(
141
+ {
142
+ "name": "net",
143
+ "start_date": start_date,
144
+ "finish_date": finish_date,
145
+ "net": net,
146
+ "breakdown": {},
147
+ }
148
+ )
149
+
150
+ return {
151
+ "bill_type_code": bill_type_code,
152
+ "account": account,
153
+ "mpan_core": mpan_core,
154
+ "reference": reference,
155
+ "issue_date": issue_date,
156
+ "start_date": start_date,
157
+ "finish_date": finish_date,
158
+ "kwh": kwh,
159
+ "net": net,
160
+ "vat": vat,
161
+ "gross": gross,
162
+ "breakdown": breakdown,
163
+ "reads": reads,
164
+ "elements": elements,
165
+ }
166
+
167
+
38
168
  class Parser:
39
169
  def __init__(self, f):
40
170
  self.reader = csv.reader(
41
- StringIO(str(f.read(), "utf-8", errors="ignore")), skipinitialspace=True
171
+ StringIO(str(f.read(), "utf-8-sig", errors="ignore")), skipinitialspace=True
42
172
  )
43
173
  self.line_number = None
44
174
 
45
175
  def make_raw_bills(self):
46
- raw_bills = []
47
- next(iter(self.reader)) # skip title row
48
- blank_set = set(("",))
49
176
  for self.line_number, self.vals in enumerate(self.reader, start=2):
50
177
  try:
51
- # skip blank lines
52
- if len(self.vals) == 0 or set(self.vals) == blank_set:
178
+ # skip blank lines and comment lines
179
+ if (
180
+ len(self.vals) == 0
181
+ or set(self.vals) == {""}
182
+ or self.vals[0].strip().startswith("#")
183
+ ):
53
184
  continue
54
-
55
- bill_type_code = self.vals[0]
56
- if bill_type_code.startswith("#"):
57
- continue # skip comment lines
58
- account = self.vals[1]
59
- mpan_core = parse_mpan_core(self.vals[2])
60
- reference = self.vals[3]
61
- issue_date = parse_date(self.vals, 4)
62
- start_date = parse_date(self.vals, 5)
63
- finish_date = parse_date(self.vals, 6, True)
64
-
65
- kwh = self.to_decimal(7, "kwh")
66
- net = self.to_decimal(8, "net", True)
67
- vat = self.to_decimal(9, "vat", True)
68
- gross = self.to_decimal(10, "gross", True)
69
-
70
- if len(self.vals) > 11:
71
- breakdown_str = self.vals[11].strip()
72
- if len(breakdown_str) == 0:
73
- breakdown = {}
74
- else:
75
- try:
76
- breakdown = loads(breakdown_str)
77
- except ZishLocationException as e:
78
- raise BadRequest(str(e))
79
- else:
80
- raise BadRequest("There isn't a 'breakdown' field on the end.")
81
-
82
- while self.vals[-1] == "" and len(self.vals) > 12:
83
- del self.vals[-1]
84
-
85
- reads = []
86
- for i in range(12, len(self.vals), 11):
87
- tpr_str = self.vals[i + 4].strip()
88
- tpr_code = None if len(tpr_str) == 0 else tpr_str.zfill(5)
89
- reads.append(
90
- {
91
- "msn": self.vals[i],
92
- "mpan": self.vals[i + 1],
93
- "coefficient": self.to_decimal(i + 2, "coefficient"),
94
- "units": self.vals[i + 3],
95
- "tpr_code": tpr_code,
96
- "prev_date": parse_date(self.vals, i + 5),
97
- "prev_value": Decimal(self.vals[i + 6]),
98
- "prev_type_code": self.vals[i + 7],
99
- "pres_date": parse_date(self.vals, i + 8),
100
- "pres_value": Decimal(self.vals[i + 9]),
101
- "pres_type_code": self.vals[i + 10],
102
- }
103
- )
104
-
105
- raw_bill = {
106
- "bill_type_code": bill_type_code,
107
- "account": account,
108
- "mpan_core": mpan_core,
109
- "reference": reference,
110
- "issue_date": issue_date,
111
- "start_date": start_date,
112
- "finish_date": finish_date,
113
- "kwh": kwh,
114
- "net": net,
115
- "vat": vat,
116
- "gross": gross,
117
- "breakdown": breakdown,
118
- "reads": reads,
119
- }
120
- raw_bills.append(raw_bill)
185
+ yield _process_row(self.vals)
121
186
  except BadRequest as e:
122
187
  raise BadRequest(
123
188
  f"Problem at line {self.line_number} {self.vals}: {e.description}"
124
189
  )
125
-
126
- return raw_bills
127
-
128
- def to_decimal(self, dec_index, dec_name, is_money=False):
129
- try:
130
- dec_str = self.vals[dec_index]
131
- dec_str = dec_str.replace(",", "")
132
- dec = Decimal(dec_str)
133
- if is_money:
134
- dec += Decimal("0.00")
135
- return dec
136
- except InvalidOperation as e:
137
- raise BadRequest(
138
- f"The value '{dec_str}' can't be parsed as a decimal: {e}. It's in "
139
- f"the '{dec_name}' column at position {dec_index} in {self.vals}."
140
- )
141
- except IndexError:
142
- raise BadRequest(
143
- f"The field '{dec_name}' can't be found. It's expected at "
144
- f"position {dec_index} in the list of fields."
145
- )
146
- except ValueError:
147
- raise BadRequest(
148
- f"The {dec_name} field '{dec_str}' cannot be parsed as a "
149
- f"number. The {dec_name} field is the {dec_index} field of "
150
- f"{self.vals}."
151
- )
@@ -24,131 +24,100 @@ read_type_map = {
24
24
  ELEMENT_MAP = {
25
25
  "AAH": {
26
26
  "AAHEDC": {
27
- "139039": ("aahedc-gbp", "aahedc-rate", "aahedc-kwh"),
27
+ "139039": ("aahedc", "rate", "kwh"),
28
28
  },
29
29
  },
30
30
  "ADH": {
31
31
  "ADHOC": {
32
- "020330": ("eii-gbp", None, None),
33
- "064305": ("fit-gbp", None, None),
34
- "590346": ("cfd-operational-gbp", None, None),
35
- "637050": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
32
+ "020330": ("eii", None, None),
33
+ "064305": ("fit", None, None),
34
+ "590346": ("cfd-operational", None, None),
35
+ "493988": ("reconciliation", None, None),
36
+ "637050": ("meter-rental", "rate", "days"),
36
37
  },
37
38
  },
38
39
  "BUS": {
39
40
  "BSUOS": {
40
- "269100": ("bsuos-gbp", "bsuos-rate", "bsuos-kwh"),
41
+ "269100": ("bsuos", "rate", "kwh"),
41
42
  },
42
43
  },
43
44
  "CCL": {
44
45
  "CCL": {
45
- "422733": ("ccl-gbp", "ccl-rate", "ccl-kwh"),
46
+ "422733": ("ccl", "rate", "kwh"),
46
47
  },
47
48
  },
48
49
  "CFD": {
49
50
  "CFD001": {
50
- "273237": (
51
- "cfd-operational-gbp",
52
- "cfd-operational-rate",
53
- "cfd-operational-kwh",
54
- ),
55
- "954379": ("cfd-interim-gbp", "cfd-interim-rate", "cfd-interim-kwh"),
56
- "538249": (
57
- "cm-settlement-levy-gbp",
58
- "cm-settlement-levy-rate",
59
- "cm-settlement-levy-kwh",
60
- ),
61
- "568307": ("capacity-gbp", "capacity-rate", "capacity-kwh"),
51
+ "273237": ("cfd-operational", "rate", "kwh"),
52
+ "954379": ("cfd-interim", "rate", "kwh"),
53
+ "538249": ("cm-settlement-levy", "rate", "kwh"),
54
+ "568307": ("capacity", "rate", "kwh"),
62
55
  },
63
56
  },
64
57
  "DCA": {
65
58
  "DCDA": {
66
- "095469": ("meter-rental-gbp", "meter-rental-rate", "meter-rental-days"),
59
+ "095469": ("meter-rental", "rate", "days"),
67
60
  },
68
61
  },
69
62
  "DUS": {
70
63
  "DUS001": {
71
- "794486": (
72
- "duos-availability-gbp",
73
- "duos-availability-rate",
74
- "duos-availability-kva",
75
- ),
76
- "644819": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
77
- "797790": (
78
- "duos-reactive-gbp",
79
- "duos-reactive-rate",
80
- "duos-reactive-kvarh",
81
- ),
82
- "806318": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
83
- "716514": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
84
- "769979": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
85
- "709522": (
86
- "duos-excess-availability-gbp",
87
- "duos-excess-availability-rate",
88
- "duos-excess-availability-kva",
89
- ),
90
- "209269": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
64
+ "794486": ("duos-availability", "rate", "kva"),
65
+ "644819": ("duos-fixed", "rate", "days"),
66
+ "797790": ("duos-reactive", "rate", "kvarh"),
67
+ "806318": ("duos-green", "rate", "kwh"),
68
+ "716514": ("duos-amber", "rate", "kwh"),
69
+ "769979": ("duos-red", "rate", "kwh"),
70
+ "709522": ("duos-excess-availability", "rate", "kva"),
71
+ "209269": ("tnuos", "rate", "days"),
91
72
  },
92
73
  "DUS002": {
93
- "185913": ("duos-yellow-gbp", "duos-yellow-rate", "duos-yellow-kwh"),
94
- "517270": ("duos-black-gbp", "duos-black-rate", "duos-black-kwh"),
95
- "797790": (
96
- "duos-reactive-gbp",
97
- "duos-reactive-rate",
98
- "duos-reactive-kvarh",
99
- ),
100
- "806318": ("duos-green-gbp", "duos-green-rate", "duos-green-kwh"),
101
- "716514": ("duos-amber-gbp", "duos-amber-rate", "duos-amber-kwh"),
102
- "709522": (
103
- "duos-excess-availability-gbp",
104
- "duos-excess-availability-rate",
105
- "duos-excess-availability-kva",
106
- ),
107
- "769979": ("duos-red-gbp", "duos-red-rate", "duos-red-kwh"),
108
- "644819": ("duos-fixed-gbp", "duos-fixed-rate", "duos-fixed-days"),
109
- "794486": (
110
- "duos-availability-gbp",
111
- "duos-availability-rate",
112
- "duos-availability-kva",
113
- ),
114
- "209269": ("tnuos-gbp", "tnuos-rate", "tnuos-days"),
115
- "065950": ("eii-gbp", "eii-rate", "eii-kwh"),
74
+ "185913": ("duos-yellow", "rate", "kwh"),
75
+ "517270": ("duos-black", "rate", "kwh"),
76
+ "797790": ("duos-reactive", "rate", "kvarh"),
77
+ "806318": ("duos-green", "rate", "kwh"),
78
+ "716514": ("duos-amber", "rate", "kwh"),
79
+ "709522": ("duos-excess-availability", "rate", "kva"),
80
+ "769979": ("duos-red", "rate", "kwh"),
81
+ "644819": ("duos-fixed", "rate", "days"),
82
+ "794486": ("duos-availability", "rate", "kva"),
83
+ "209269": ("tnuos", "rate", "days"),
84
+ "065950": ("eii", "rate", "kwh"),
116
85
  },
117
86
  "DUSDIS": {
118
- "122568": ("nrg-gsp-losses-gbp", "nrg-rate", "nrg-gsp-losses-kwh"),
87
+ "122568": ("nrg-gsp-losses", "rate", "kwh"),
119
88
  },
120
89
  "DUSTRN": {
121
- "122568": ("nrg-nbp-losses-gbp", "nrg-rate", "nrg-nbp-losses-kwh"),
90
+ "122568": ("nrg-nbp-losses", "rate", "kwh"),
122
91
  },
123
92
  },
124
93
  "ELX": {
125
94
  "ELEXON": {
126
- "489920": ("elexon-gbp", "elexon-rate", "elexon-nbp-kwh"),
95
+ "489920": ("elexon", "rate", "kwh"),
127
96
  },
128
97
  },
129
98
  "FIT": {
130
99
  "FIT_LV": {
131
- "704107": ("fit-gbp", "fit-rate", "fit-kwh"),
100
+ "704107": ("fit", "rate", "kwh"),
132
101
  },
133
102
  },
134
103
  "NRG": {
135
104
  "HH0002": {
136
- "033667": ("management-gbp", "management-rate", "management-kwh"),
137
- "091890": ("shape-gbp", "shape-rate", "shape-kwh"),
138
- "122568": ("nrg-msp-gbp", "nrg-rate", "nrg-msp-kwh"),
105
+ "033667": ("management", "rate", "kwh"),
106
+ "091890": ("shape", "rate", "kwh"),
107
+ "122568": ("nrg-msp", "rate", "kwh"),
139
108
  },
140
109
  },
141
110
  "REN": {
142
111
  "REN001": {
143
- "229128": ("ro-gbp", "ro-rate", "ro-kwh"),
112
+ "229128": ("ro", "rate", "kwh"),
144
113
  },
145
114
  "REN002": {
146
- "019090": ("rego-gbp", "rego-rate", "rego-kwh"),
115
+ "019090": ("rego", "rate", "kwh"),
147
116
  },
148
117
  },
149
118
  "TUS": {
150
119
  "TNUOS": {
151
- "012069": ("triad-gbp", "triad-rate", "triad-kw"),
120
+ "012069": ("triad", "rate", "kw"),
152
121
  },
153
122
  },
154
123
  }
@@ -173,6 +142,7 @@ def _process_BCD(elements, headers):
173
142
  headers["issue_date"] = issue_date
174
143
  headers["bill_type_code"] = bill_type_code
175
144
  headers["reference"] = reference
145
+ headers["elements"] = []
176
146
 
177
147
 
178
148
  def _process_BTL(elements, headers):
@@ -194,6 +164,7 @@ def _process_BTL(elements, headers):
194
164
  "gross": Decimal("0.00") + to_decimal(tbtl) / Decimal("100"),
195
165
  "breakdown": headers["breakdown"],
196
166
  "reads": headers["reads"],
167
+ "elements": headers["elements"],
197
168
  }
198
169
 
199
170
 
@@ -311,13 +282,13 @@ def _process_CCD2(elements, headers):
311
282
 
312
283
 
313
284
  def _process_CCD3(elements, headers):
314
- breakdown = headers["breakdown"]
285
+ elem_bd = {}
315
286
 
316
287
  supplier_code = elements["CCDE"][2]
317
288
  tariff_code = elements["TCOD"][0]
318
289
  mod_code = elements["TMOD"][0]
319
290
  try:
320
- eln_gbp, eln_rate, eln_cons = ELEMENT_MAP[supplier_code][tariff_code][mod_code]
291
+ eln_name, eln_rate, eln_cons = ELEMENT_MAP[supplier_code][tariff_code][mod_code]
321
292
  except KeyError:
322
293
  raise BadRequest(
323
294
  f"Can't find the element key {supplier_code} -> {tariff_code} -> "
@@ -325,29 +296,35 @@ def _process_CCD3(elements, headers):
325
296
  )
326
297
 
327
298
  cons = elements["CONS"]
328
- kwh = Decimal("0")
329
299
  if len(cons[0]) > 0:
330
300
  el_cons = to_decimal(cons) / Decimal("1000")
331
- kwh = el_cons
332
- breakdown[eln_cons] += kwh
301
+ elem_bd[eln_cons] = el_cons
302
+ if eln_name == "nrg-msp":
303
+ headers["kwh"] += el_cons
333
304
 
334
305
  bpri = elements["BPRI"]
335
306
  if len(bpri[0]) > 0:
336
307
  rate = to_decimal(bpri) / Decimal("100000")
337
- if eln_rate in breakdown:
338
- breakdown[eln_rate].add(rate)
339
- else:
340
- breakdown[eln_rate] = {rate}
308
+ elem_bd[eln_rate] = {rate}
341
309
 
310
+ net = Decimal("0.00")
342
311
  if "CTOT" in elements:
343
- net = Decimal("0.00") + to_decimal(elements["CTOT"]) / Decimal("100")
344
- else:
345
- net = Decimal("0.00")
312
+ net += to_decimal(elements["CTOT"]) / Decimal("100")
346
313
 
347
- breakdown[eln_gbp] += net
314
+ csdt = elements["CSDT"]
315
+ start_date = to_date(csdt[0])
316
+ cedt = elements["CEDT"]
317
+ finish_date = to_finish_date(cedt[0])
348
318
 
349
- if eln_gbp == "nrg-msp-gbp":
350
- headers["kwh"] += kwh
319
+ headers["elements"].append(
320
+ {
321
+ "name": eln_name,
322
+ "start_date": start_date,
323
+ "finish_date": finish_date,
324
+ "net": net,
325
+ "breakdown": elem_bd,
326
+ }
327
+ )
351
328
 
352
329
 
353
330
  def _process_CCD4(elements, headers):