chellow 1744720514.0.0__py3-none-any.whl → 1745911639.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_parsers/mm.py +1 -1
- chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py +24 -14
- chellow/e/dno_rate_parser.py +10 -10
- chellow/gas/bill_parser_bgs_xlsx.py +271 -0
- chellow/reports/report_missing_bills.py +158 -0
- chellow/templates/e/dc_contract.html +12 -1
- chellow/templates/e/mop_contract.html +11 -0
- chellow/templates/e/supplier_contract.html +11 -0
- chellow/templates/report_run_missing_e_bills.html +97 -0
- chellow/utils.py +5 -2
- chellow/views.py +16 -3
- {chellow-1744720514.0.0.dist-info → chellow-1745911639.0.0.dist-info}/METADATA +1 -1
- {chellow-1744720514.0.0.dist-info → chellow-1745911639.0.0.dist-info}/RECORD +14 -11
- {chellow-1744720514.0.0.dist-info → chellow-1745911639.0.0.dist-info}/WHEEL +0 -0
chellow/e/bill_parsers/mm.py
CHANGED
|
@@ -59,7 +59,7 @@ def _handle_0100(headers, pre_record, record):
|
|
|
59
59
|
headers["issue_date"] = issue_date
|
|
60
60
|
if late_payment is not None:
|
|
61
61
|
headers["late_payment"] = late_payment
|
|
62
|
-
headers["account"] = pre_record[
|
|
62
|
+
headers["account"] = pre_record[43:52]
|
|
63
63
|
headers["reference"] = pre_record[41:46]
|
|
64
64
|
headers["kwh"] = Decimal("0")
|
|
65
65
|
headers["breakdown"] = defaultdict(int, {"vat": {}})
|
|
@@ -4,6 +4,7 @@ from openpyxl import load_workbook
|
|
|
4
4
|
|
|
5
5
|
from werkzeug.exceptions import BadRequest
|
|
6
6
|
|
|
7
|
+
from chellow.e.computer import hh_rate
|
|
7
8
|
from chellow.models import Session
|
|
8
9
|
from chellow.utils import ct_datetime, parse_mpan_core, to_utc
|
|
9
10
|
|
|
@@ -16,12 +17,6 @@ def get_start_date(title_row, row, name):
|
|
|
16
17
|
return to_utc(get_ct_date(title_row, row, name))
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def get_finish_date(title_row, row, name):
|
|
20
|
-
d = get_ct_date(title_row, row, name)
|
|
21
|
-
|
|
22
|
-
return to_utc(ct_datetime(d.year, d.month, d.day, 23, 30))
|
|
23
|
-
|
|
24
|
-
|
|
25
20
|
def get_value(title_row, row, name):
|
|
26
21
|
idx = None
|
|
27
22
|
name = name.strip().lower()
|
|
@@ -61,9 +56,6 @@ def get_int(title_row, row, name):
|
|
|
61
56
|
return int(get_value(title_row, row, name))
|
|
62
57
|
|
|
63
58
|
|
|
64
|
-
METER_RATE = Decimal("60.00")
|
|
65
|
-
|
|
66
|
-
|
|
67
59
|
class Parser:
|
|
68
60
|
def __init__(self, f):
|
|
69
61
|
self.book = load_workbook(f)
|
|
@@ -86,6 +78,7 @@ class Parser:
|
|
|
86
78
|
|
|
87
79
|
def make_raw_bills(self):
|
|
88
80
|
row_index = None
|
|
81
|
+
caches = {}
|
|
89
82
|
try:
|
|
90
83
|
with Session() as sess:
|
|
91
84
|
bills = []
|
|
@@ -101,14 +94,30 @@ class Parser:
|
|
|
101
94
|
mpan_core = parse_mpan_core(
|
|
102
95
|
str(get_int(title_row, row, "mpan ref"))
|
|
103
96
|
)
|
|
104
|
-
|
|
97
|
+
start_date_ct = get_ct_date(title_row, row, "start")
|
|
98
|
+
start_date = to_utc(start_date_ct)
|
|
105
99
|
issue_date = start_date
|
|
106
|
-
|
|
100
|
+
finish_date_ct = get_ct_date(title_row, row, "end")
|
|
101
|
+
finish_date = to_utc(
|
|
102
|
+
ct_datetime(
|
|
103
|
+
finish_date_ct.year,
|
|
104
|
+
finish_date_ct.month,
|
|
105
|
+
finish_date_ct.day,
|
|
106
|
+
23,
|
|
107
|
+
30,
|
|
108
|
+
)
|
|
109
|
+
)
|
|
107
110
|
check = get_str(title_row, row, "check")
|
|
108
111
|
if check != "Billed":
|
|
109
112
|
continue
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
rates = hh_rate(sess, caches, 0, start_date)
|
|
114
|
+
meter_rate = rates["annual_rates"]["non_settlement"]["*"]["IP"][
|
|
115
|
+
"*"
|
|
116
|
+
]["gbp_per_meter"]
|
|
117
|
+
months = (finish_date_ct.year - start_date_ct.year) * 12 + (
|
|
118
|
+
finish_date_ct.month - start_date_ct.month + 1
|
|
119
|
+
)
|
|
120
|
+
net = round(Decimal(float(meter_rate) / 12 * months), 2)
|
|
112
121
|
vat = round(net * Decimal("0.2"), 2)
|
|
113
122
|
|
|
114
123
|
breakdown = {
|
|
@@ -116,7 +125,8 @@ class Parser:
|
|
|
116
125
|
"cop": ["5"],
|
|
117
126
|
"settlement-status": ["non_settlement"],
|
|
118
127
|
"msn": [msn],
|
|
119
|
-
"meter-rate": [
|
|
128
|
+
"meter-rate": [meter_rate],
|
|
129
|
+
"months": months,
|
|
120
130
|
"meter-gbp": net,
|
|
121
131
|
}
|
|
122
132
|
|
chellow/e/dno_rate_parser.py
CHANGED
|
@@ -650,15 +650,15 @@ def update_vls(sess, logger, vls, dno_code, fy_start, rs_finish):
|
|
|
650
650
|
llfc = sess.execute(q).scalar_one_or_none()
|
|
651
651
|
|
|
652
652
|
if llfc is None:
|
|
653
|
-
|
|
653
|
+
logger(
|
|
654
654
|
f"There is no LLFC with the code '{vl_code}' associated with the DNO "
|
|
655
|
-
f"{dno.
|
|
655
|
+
f"{dno.dno_code} from {hh_format(fy_start)} to {hh_format(rs_finish)}."
|
|
656
656
|
)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
657
|
+
else:
|
|
658
|
+
vl_voltage_level = VoltageLevel.get_by_code(sess, vl["voltage_level"])
|
|
659
|
+
llfc.voltage_level = vl_voltage_level
|
|
660
|
+
|
|
661
|
+
llfc.is_substation = vl["is_substation"]
|
|
662
|
+
if sess.is_modified(llfc):
|
|
663
|
+
logger(f"Updated LLFC {llfc.code} of DNO {dno_code}")
|
|
664
|
+
sess.flush()
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
from datetime import datetime as Datetime
|
|
3
|
+
from decimal import Decimal, InvalidOperation
|
|
4
|
+
from enum import Enum, auto
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
|
|
7
|
+
from dateutil.relativedelta import relativedelta
|
|
8
|
+
|
|
9
|
+
from openpyxl import load_workbook
|
|
10
|
+
|
|
11
|
+
from werkzeug.exceptions import BadRequest
|
|
12
|
+
|
|
13
|
+
from chellow.utils import to_ct, to_utc
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Title(Enum):
|
|
17
|
+
CUSTOMER = auto()
|
|
18
|
+
PRODUCT = auto()
|
|
19
|
+
BROKER_NAME = auto()
|
|
20
|
+
ACCOUNT = auto()
|
|
21
|
+
MPRN = auto()
|
|
22
|
+
BILL = auto()
|
|
23
|
+
SUPPLY_ADDRESS = auto()
|
|
24
|
+
BILL_DATE = auto()
|
|
25
|
+
BILLING_PERIOD = auto()
|
|
26
|
+
CHARGE_TYPE = auto()
|
|
27
|
+
CHARGE_PERIOD_FROM = auto()
|
|
28
|
+
CHARGE_PERIOD_END = auto()
|
|
29
|
+
QUANTITY = auto()
|
|
30
|
+
QUNIT = auto()
|
|
31
|
+
CHARGE = auto()
|
|
32
|
+
CUNIT = auto()
|
|
33
|
+
TOTAL = auto()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
COLUMNS = {
|
|
37
|
+
Title.CUSTOMER: ["Customer"],
|
|
38
|
+
Title.PRODUCT: ["Product"],
|
|
39
|
+
Title.BROKER_NAME: ["Broker Name"],
|
|
40
|
+
Title.ACCOUNT: ["Account"],
|
|
41
|
+
Title.MPRN: ["MPRN"],
|
|
42
|
+
Title.BILL: ["Bill"],
|
|
43
|
+
Title.SUPPLY_ADDRESS: ["Supply Address"],
|
|
44
|
+
Title.BILL_DATE: ["Bill Date"],
|
|
45
|
+
Title.BILLING_PERIOD: ["Billing Period"],
|
|
46
|
+
Title.CHARGE_TYPE: ["Charge Type"],
|
|
47
|
+
Title.CHARGE_PERIOD_FROM: ["Charge Period From"],
|
|
48
|
+
Title.CHARGE_PERIOD_END: ["Charge Period End"],
|
|
49
|
+
Title.QUANTITY: ["Quantity"],
|
|
50
|
+
Title.QUNIT: ["QUnit"],
|
|
51
|
+
Title.CHARGE: ["Charge"],
|
|
52
|
+
Title.CUNIT: ["CUnit"],
|
|
53
|
+
Title.TOTAL: ["Total"],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def make_column_map(title_row):
|
|
58
|
+
titles = [cell.value for cell in title_row]
|
|
59
|
+
column_map = {}
|
|
60
|
+
for title, title_names in COLUMNS.items():
|
|
61
|
+
idx = None
|
|
62
|
+
for title_name in title_names:
|
|
63
|
+
try:
|
|
64
|
+
idx = titles.index(title_name)
|
|
65
|
+
except ValueError:
|
|
66
|
+
pass
|
|
67
|
+
if idx is None:
|
|
68
|
+
raise BadRequest(f"For the title {title} a column can't be found")
|
|
69
|
+
|
|
70
|
+
column_map[title] = idx + 1
|
|
71
|
+
return column_map
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_date_naive(sheet, row, col):
|
|
75
|
+
value = get_value(sheet, row, col)
|
|
76
|
+
if value in ("", None):
|
|
77
|
+
return None
|
|
78
|
+
elif not isinstance(value, Datetime):
|
|
79
|
+
raise BadRequest(
|
|
80
|
+
f"Problem reading {value} as a timestamp at row {row} col {col}."
|
|
81
|
+
)
|
|
82
|
+
return value
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_date(sheet, row, col_name):
|
|
86
|
+
dt = get_date_naive(sheet, row, col_name)
|
|
87
|
+
return None if dt is None else to_utc(to_ct(dt))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_value(sheet, row, col):
|
|
91
|
+
try:
|
|
92
|
+
return sheet.cell(row=row, column=col).value
|
|
93
|
+
except IndexError:
|
|
94
|
+
raise BadRequest(
|
|
95
|
+
f"Can't find the cell at row {row} and col {col} on sheet {sheet}."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_str(sheet, row, col):
|
|
100
|
+
value = get_value(sheet, row, col)
|
|
101
|
+
if value is None:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
return value.strip()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_dec(sheet, row, col):
|
|
108
|
+
value = get_value(sheet, row, col)
|
|
109
|
+
if value in ("", None):
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
return Decimal(str(value))
|
|
114
|
+
except InvalidOperation as e:
|
|
115
|
+
raise BadRequest(
|
|
116
|
+
f"Problem parsing the number '{value}' at row {row} col {col}. {e}"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_int(sheet, row, col):
|
|
121
|
+
value = get_value(sheet, row, col)
|
|
122
|
+
try:
|
|
123
|
+
return int(value)
|
|
124
|
+
except ValueError as e:
|
|
125
|
+
raise BadRequest(
|
|
126
|
+
f"Problem parsing the integer '{value}' at row {row} col {col}. {e}"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
def _bd_add(bd, el_name, val):
|
|
132
|
+
if el_name.split("-")[-1] in ("rate", "kva"):
|
|
133
|
+
if el_name not in bd:
|
|
134
|
+
bd[el_name] = set()
|
|
135
|
+
bd[el_name].add(val)
|
|
136
|
+
else:
|
|
137
|
+
if el_name not in bd:
|
|
138
|
+
bd[el_name] = 0
|
|
139
|
+
try:
|
|
140
|
+
bd[el_name] += val
|
|
141
|
+
except TypeError as e:
|
|
142
|
+
raise BadRequest(
|
|
143
|
+
f"Problem with element name {el_name} and value '{val}': {e}"
|
|
144
|
+
)
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
ELEMENT_LOOKUP = {
|
|
148
|
+
"Management Fee": "admin_variable",
|
|
149
|
+
"LDZ Customer Capacity": "dn_customer_capacity_fixed",
|
|
150
|
+
"LDZ System Capacity": "dn_system_capacity_fixed",
|
|
151
|
+
"LDZ System Commodity": "dn_system_capacity",
|
|
152
|
+
"Metering Charges": "metering",
|
|
153
|
+
"NTS Exit Capacity (ECN)": "dn_ecn_fixed",
|
|
154
|
+
"NTS SO Exit": "so_exit_commodity",
|
|
155
|
+
"NTS TO Exit": "to_exit_commodity",
|
|
156
|
+
"Unidentified Gas": "ug",
|
|
157
|
+
"WAP": "wap",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
QUNIT_LOOKUP = {
|
|
161
|
+
"kWh": "kwh",
|
|
162
|
+
"days": "days",
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _parse_row(bills, sheet, row, title_row):
|
|
167
|
+
column_map = make_column_map(title_row)
|
|
168
|
+
mprn = get_value(sheet, row, column_map[Title.MPRN])
|
|
169
|
+
reference = get_value(sheet, row, column_map[Title.BILL])
|
|
170
|
+
account = get_value(sheet, row, column_map[Title.ACCOUNT])
|
|
171
|
+
issue_date = get_date(sheet, row, column_map[Title.BILL_DATE])
|
|
172
|
+
start_date = get_date(sheet, row, column_map[Title.CHARGE_PERIOD_FROM])
|
|
173
|
+
finish_date = to_utc(
|
|
174
|
+
to_ct(get_date_naive(sheet, row, column_map[Title.CHARGE_PERIOD_END]))
|
|
175
|
+
+ relativedelta(hours=23, minutes=30)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
mprn_values = bills[mprn]
|
|
180
|
+
except KeyError:
|
|
181
|
+
mprn_values = bills[mprn] = {}
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
start_date_values = mprn_values[start_date]
|
|
185
|
+
except KeyError:
|
|
186
|
+
start_date_values = mprn_values[start_date] = {}
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
bill = start_date_values[finish_date]
|
|
190
|
+
except KeyError:
|
|
191
|
+
bill = start_date_values[finish_date] = {
|
|
192
|
+
"bill_type_code": "N",
|
|
193
|
+
"mprn": mprn,
|
|
194
|
+
"reference": reference,
|
|
195
|
+
"account": account,
|
|
196
|
+
"issue_date": issue_date,
|
|
197
|
+
"start_date": start_date,
|
|
198
|
+
"finish_date": finish_date,
|
|
199
|
+
"kwh": Decimal("0"),
|
|
200
|
+
"net_gbp": Decimal("0.00"),
|
|
201
|
+
"vat_gbp": Decimal("0.00"),
|
|
202
|
+
"gross_gbp": Decimal("0.00"),
|
|
203
|
+
"breakdown": {},
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
bd = bill["breakdown"]
|
|
207
|
+
element_desc = get_value(sheet, row, column_map[Title.CHARGE_TYPE])
|
|
208
|
+
quantity = get_dec(sheet, row, column_map[Title.QUANTITY])
|
|
209
|
+
qunit = get_value(sheet, row, column_map[Title.QUNIT])
|
|
210
|
+
charge = get_dec(sheet, row, column_map[Title.CHARGE]) / Decimal("100")
|
|
211
|
+
total = get_dec(sheet, row, column_map[Title.TOTAL])
|
|
212
|
+
if element_desc.startswith("20% VAT on "):
|
|
213
|
+
bill["net_gbp"] += quantity
|
|
214
|
+
bill["vat_gbp"] += total
|
|
215
|
+
bill["gross_gbp"] += quantity + total
|
|
216
|
+
else:
|
|
217
|
+
element_name = ELEMENT_LOOKUP[element_desc]
|
|
218
|
+
if element_name == "admin_variable":
|
|
219
|
+
bill["kwh"] += quantity
|
|
220
|
+
bd[f"{element_name}_gbp"] = total
|
|
221
|
+
element_qunit = QUNIT_LOOKUP[qunit]
|
|
222
|
+
bd[f"{element_name}_{element_qunit}"] = quantity
|
|
223
|
+
bd[f"{element_name}_rate"] = charge
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _make_raw_bills(sheet):
|
|
227
|
+
bills = {}
|
|
228
|
+
rows = tuple(sheet.rows)
|
|
229
|
+
title_row = rows[1]
|
|
230
|
+
for row_index, row in enumerate(rows[2:], start=3):
|
|
231
|
+
val = row[0].value
|
|
232
|
+
if val not in (None, ""):
|
|
233
|
+
try:
|
|
234
|
+
_parse_row(bills, sheet, row_index, title_row)
|
|
235
|
+
except BadRequest as e:
|
|
236
|
+
raise BadRequest(f"On row {row_index + 1}: {e.description}")
|
|
237
|
+
print("bills", bills)
|
|
238
|
+
|
|
239
|
+
raw_bills = []
|
|
240
|
+
for mprn, mprn_values in bills.items():
|
|
241
|
+
for period_stat, period_start_values in mprn_values.items():
|
|
242
|
+
for period_finish, bill in period_start_values.items():
|
|
243
|
+
raw_bills.append(bill)
|
|
244
|
+
|
|
245
|
+
return raw_bills
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class Parser:
|
|
249
|
+
def __init__(self, f):
|
|
250
|
+
self.book = load_workbook(BytesIO(f), data_only=True)
|
|
251
|
+
self.sheet = self.book.worksheets[0]
|
|
252
|
+
|
|
253
|
+
self.last_line = None
|
|
254
|
+
lines = (self._set_last_line(i, l) for i, l in enumerate(f))
|
|
255
|
+
self.reader = csv.reader(lines, skipinitialspace=True)
|
|
256
|
+
self._line_number = None
|
|
257
|
+
self._title_line = None
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def line_number(self):
|
|
261
|
+
return None if self._line_number is None else self._line_number + 1
|
|
262
|
+
|
|
263
|
+
def _set_last_line(self, i, line):
|
|
264
|
+
self._line_numer = i
|
|
265
|
+
self.last_line = line
|
|
266
|
+
if i == 0:
|
|
267
|
+
self._title_line = line
|
|
268
|
+
return line
|
|
269
|
+
|
|
270
|
+
def make_raw_bills(self):
|
|
271
|
+
return _make_raw_bills(self.sheet)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import threading
|
|
3
|
+
import traceback
|
|
4
|
+
|
|
5
|
+
from flask import g, redirect
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import null, or_, select
|
|
9
|
+
from sqlalchemy.orm import joinedload
|
|
10
|
+
|
|
11
|
+
from chellow.dloads import open_file
|
|
12
|
+
from chellow.models import Batch, Bill, Contract, Era, RSession, ReportRun, User
|
|
13
|
+
from chellow.utils import (
|
|
14
|
+
c_months_u,
|
|
15
|
+
csv_make_val,
|
|
16
|
+
hh_max,
|
|
17
|
+
hh_min,
|
|
18
|
+
hh_range,
|
|
19
|
+
req_date,
|
|
20
|
+
req_int,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def content(user_id, report_run_id, contract_id, months_length, finish_date):
|
|
25
|
+
f = writer = None
|
|
26
|
+
try:
|
|
27
|
+
with RSession() as sess:
|
|
28
|
+
caches = {}
|
|
29
|
+
contract = Contract.get_by_id(sess, contract_id)
|
|
30
|
+
user = User.get_by_id(sess, user_id)
|
|
31
|
+
f = open_file(
|
|
32
|
+
f"missing_bills_{contract.id}.csv", user, mode="w", newline=""
|
|
33
|
+
)
|
|
34
|
+
writer = csv.writer(f, lineterminator="\n")
|
|
35
|
+
titles = (
|
|
36
|
+
"contract_name",
|
|
37
|
+
"month_start",
|
|
38
|
+
"month_finish",
|
|
39
|
+
"site_code",
|
|
40
|
+
"site_name",
|
|
41
|
+
"imp_mpan_core",
|
|
42
|
+
"exp_mpan_core",
|
|
43
|
+
"account",
|
|
44
|
+
)
|
|
45
|
+
writer.writerow(titles)
|
|
46
|
+
|
|
47
|
+
months = list(
|
|
48
|
+
c_months_u(
|
|
49
|
+
finish_year=finish_date.year,
|
|
50
|
+
finish_month=finish_date.month,
|
|
51
|
+
months=months_length,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
for month_start, month_finish in months:
|
|
56
|
+
missing_bills = {}
|
|
57
|
+
missing_account = {}
|
|
58
|
+
account_missing_tuple = hh_range(caches, month_start, month_finish)
|
|
59
|
+
for era in sess.scalars(
|
|
60
|
+
select(Era)
|
|
61
|
+
.where(
|
|
62
|
+
Era.start_date <= month_finish,
|
|
63
|
+
or_(Era.finish_date == null(), Era.finish_date >= month_start),
|
|
64
|
+
or_(
|
|
65
|
+
Era.mop_contract == contract,
|
|
66
|
+
Era.dc_contract == contract,
|
|
67
|
+
Era.imp_supplier_contract == contract,
|
|
68
|
+
Era.exp_supplier_contract == contract,
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
.options(joinedload(Era.supply))
|
|
72
|
+
):
|
|
73
|
+
chunk_start = hh_max(era.start_date, month_start)
|
|
74
|
+
chunk_finish = hh_min(era.finish_date, month_finish)
|
|
75
|
+
missing_set = set(hh_range(caches, chunk_start, chunk_finish))
|
|
76
|
+
if era.mop_contract == contract:
|
|
77
|
+
account = era.mop_account
|
|
78
|
+
elif era.dc_contract == contract:
|
|
79
|
+
account = era.dc_account
|
|
80
|
+
elif era.imp_supplier_contract == contract:
|
|
81
|
+
account = era.imp_supplier_account
|
|
82
|
+
elif era.imp_supplier_contract == contract:
|
|
83
|
+
account = era.exp_supplier_account
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
account_missing_set = missing_account[account]
|
|
87
|
+
except KeyError:
|
|
88
|
+
account_missing_set = missing_account[account] = set(
|
|
89
|
+
account_missing_tuple
|
|
90
|
+
)
|
|
91
|
+
supply = era.supply
|
|
92
|
+
|
|
93
|
+
for bill in sess.scalars(
|
|
94
|
+
select(Bill)
|
|
95
|
+
.join(Batch)
|
|
96
|
+
.where(
|
|
97
|
+
Batch.contract == contract,
|
|
98
|
+
Bill.supply == supply,
|
|
99
|
+
Bill.start_date <= chunk_finish,
|
|
100
|
+
Bill.finish_date >= chunk_start,
|
|
101
|
+
)
|
|
102
|
+
):
|
|
103
|
+
found_set = set(
|
|
104
|
+
hh_range(caches, bill.start_date, bill.finish_date)
|
|
105
|
+
)
|
|
106
|
+
missing_set.difference_update(found_set)
|
|
107
|
+
account_missing_set.difference_update(found_set)
|
|
108
|
+
|
|
109
|
+
if len(missing_set) > 0:
|
|
110
|
+
site = era.get_physical_site(sess)
|
|
111
|
+
|
|
112
|
+
values = {
|
|
113
|
+
"contract_id": contract.id,
|
|
114
|
+
"contract_name": contract.name,
|
|
115
|
+
"month_start": month_start,
|
|
116
|
+
"month_finish": month_finish,
|
|
117
|
+
"era_id": era.id,
|
|
118
|
+
"supply_id": supply.id,
|
|
119
|
+
"imp_mpan_core": era.imp_mpan_core,
|
|
120
|
+
"exp_mpan_core": era.exp_mpan_core,
|
|
121
|
+
"site_id": site.id,
|
|
122
|
+
"site_code": site.code,
|
|
123
|
+
"site_name": site.name,
|
|
124
|
+
"account": account,
|
|
125
|
+
"market_role_code": contract.market_role.code,
|
|
126
|
+
}
|
|
127
|
+
missing_bills[era.id] = values
|
|
128
|
+
for era_id, values in missing_bills.items():
|
|
129
|
+
if len(missing_account[values["account"]]) > 0:
|
|
130
|
+
writer.writerow(csv_make_val(values[t]) for t in titles)
|
|
131
|
+
ReportRun.w_insert_row(report_run_id, "", titles, values, {})
|
|
132
|
+
|
|
133
|
+
except BaseException:
|
|
134
|
+
msg = traceback.format_exc()
|
|
135
|
+
print(msg)
|
|
136
|
+
if writer is not None:
|
|
137
|
+
writer.writerow([msg])
|
|
138
|
+
finally:
|
|
139
|
+
ReportRun.w_update(report_run_id, "finished")
|
|
140
|
+
if f is not None:
|
|
141
|
+
f.close()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def do_get(sess):
|
|
145
|
+
contract_id = req_int("contract_id")
|
|
146
|
+
months = req_int("months")
|
|
147
|
+
finish_date = req_date("finish", resolution="month")
|
|
148
|
+
report_run = ReportRun.insert(
|
|
149
|
+
sess,
|
|
150
|
+
"missing_e_bills",
|
|
151
|
+
g.user,
|
|
152
|
+
"missing_e_bills",
|
|
153
|
+
{},
|
|
154
|
+
)
|
|
155
|
+
sess.commit()
|
|
156
|
+
args = g.user.id, report_run.id, contract_id, months, finish_date
|
|
157
|
+
threading.Thread(target=content, args=args).start()
|
|
158
|
+
return redirect(f"/report_runs/{report_run.id}", 303)
|
|
@@ -97,7 +97,18 @@
|
|
|
97
97
|
<input type="submit" value="Download">
|
|
98
98
|
</fieldset>
|
|
99
99
|
</form>
|
|
100
|
-
|
|
100
|
+
|
|
101
|
+
<form action="/reports/missing_bills">
|
|
102
|
+
<fieldset>
|
|
103
|
+
<legend>Download Missing Bills</legend>
|
|
104
|
+
<input type="hidden" name="contract_id" value="{{dc_contract.id}}">
|
|
105
|
+
|
|
106
|
+
<label>Months</label> <input name="months" value="1" maxlength="2" size="2">
|
|
107
|
+
<label>Last Month</label> {{input_date('finish', last_month_finish, 'month')}}
|
|
108
|
+
<input type="submit" value="Download">
|
|
109
|
+
</fieldset>
|
|
110
|
+
</form>
|
|
111
|
+
|
|
101
112
|
<h3>Script</h3>
|
|
102
113
|
|
|
103
114
|
<pre>{{dc_contract.charge_script}}</pre>
|
|
@@ -76,6 +76,17 @@
|
|
|
76
76
|
<input type="submit" value="Download"/>
|
|
77
77
|
</fieldset>
|
|
78
78
|
</form>
|
|
79
|
+
|
|
80
|
+
<form action="/reports/missing_bills">
|
|
81
|
+
<fieldset>
|
|
82
|
+
<legend>Download Missing Bills</legend>
|
|
83
|
+
<input type="hidden" name="contract_id" value="{{contract.id}}">
|
|
84
|
+
|
|
85
|
+
<label>Months</label> <input name="months" value="1" maxlength="2" size="2">
|
|
86
|
+
<label>Last Month</label> {{input_date('finish', last_month_finish, 'month')}}
|
|
87
|
+
<input type="submit" value="Download">
|
|
88
|
+
</fieldset>
|
|
89
|
+
</form>
|
|
79
90
|
<br>
|
|
80
91
|
<h2>Script</h2>
|
|
81
92
|
<pre>{{contract.charge_script}}</pre>
|
|
@@ -97,6 +97,17 @@
|
|
|
97
97
|
</fieldset>
|
|
98
98
|
</form>
|
|
99
99
|
|
|
100
|
+
<form action="/reports/missing_bills">
|
|
101
|
+
<fieldset>
|
|
102
|
+
<legend>Download Missing Bills</legend>
|
|
103
|
+
<input type="hidden" name="contract_id" value="{{contract.id}}">
|
|
104
|
+
|
|
105
|
+
<label>Months</label> <input name="months" value="1" maxlength="2" size="2">
|
|
106
|
+
<label>Last Month</label> {{input_date('finish', month_finish, 'month')}}
|
|
107
|
+
<input type="submit" value="Download">
|
|
108
|
+
</fieldset>
|
|
109
|
+
</form>
|
|
110
|
+
|
|
100
111
|
<h2>Script</h2>
|
|
101
112
|
|
|
102
113
|
<pre>{{contract.charge_script}}</pre>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}
|
|
4
|
+
» Report Runs » {{run.id}}
|
|
5
|
+
{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block nav %}
|
|
8
|
+
<a href="/report_runs">Report Runs</a> » {{run.id}}
|
|
9
|
+
{% endblock %}
|
|
10
|
+
|
|
11
|
+
{% block content %}
|
|
12
|
+
<table class="sticky">
|
|
13
|
+
<caption>Missing Electricity Bills Summary</caption>
|
|
14
|
+
<thead>
|
|
15
|
+
<tr>
|
|
16
|
+
<th>Date Created</th>
|
|
17
|
+
<th>Created By</th>
|
|
18
|
+
<th>State</th>
|
|
19
|
+
<th>Number Of Rows</th>
|
|
20
|
+
<th>Delete</th>
|
|
21
|
+
<th>Download Spreadsheet</th>
|
|
22
|
+
</tr>
|
|
23
|
+
</thead>
|
|
24
|
+
<tbody>
|
|
25
|
+
<tr>
|
|
26
|
+
<td>{{run.date_created|hh_format}}</td>
|
|
27
|
+
<td>{{run.creator}}</td>
|
|
28
|
+
<td>{{run.state}}</td>
|
|
29
|
+
<td>{{rows|length}}</td>
|
|
30
|
+
<td>
|
|
31
|
+
<form hx-delete="/report_runs/{{run.id}}/edit"
|
|
32
|
+
hx-confirm="Are you sure you want to delete this report run?">
|
|
33
|
+
<fieldset style="border: none;">
|
|
34
|
+
<input type="submit" name="delete" value="Delete">
|
|
35
|
+
</fieldset>
|
|
36
|
+
</form>
|
|
37
|
+
</td>
|
|
38
|
+
<td>
|
|
39
|
+
<a href="/report_runs/{{run.id}}/spreadsheet">Download</a>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
42
|
+
</tbody>
|
|
43
|
+
</table>
|
|
44
|
+
|
|
45
|
+
<table class="sticky">
|
|
46
|
+
<caption>
|
|
47
|
+
Missing Electricity Bills
|
|
48
|
+
</caption>
|
|
49
|
+
<thead>
|
|
50
|
+
<tr>
|
|
51
|
+
<th>Contract</th>
|
|
52
|
+
<th>Month</th>
|
|
53
|
+
<th>Supply</th>
|
|
54
|
+
<th>Import MPAN</th>
|
|
55
|
+
<th>Export MPAN</th>
|
|
56
|
+
<th>Account</th>
|
|
57
|
+
<th>Site</th>
|
|
58
|
+
</tr>
|
|
59
|
+
</thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
{% for row in rows %}
|
|
62
|
+
{% set values = row.data['values'] %}
|
|
63
|
+
<tr>
|
|
64
|
+
<td>
|
|
65
|
+
{% if values['market_role_code'] == 'M' %}
|
|
66
|
+
<a href="/e/mop_contracts/{{values['contract_id']}}">{{values['contract_name']}}</a>
|
|
67
|
+
{% elif values['market_role_code'] == 'C' %}
|
|
68
|
+
<a href="/e/dc_contracts/{{values['contract_id']}}">{{values['contract_name']}}</a>
|
|
69
|
+
{% elif values['market_role_code'] == 'X' %}
|
|
70
|
+
<a href="/e/supplier_contracts/{{values['contract_id']}}">{{values['contract_name']}}</a>
|
|
71
|
+
{% endif %}
|
|
72
|
+
</td>
|
|
73
|
+
<td>{{values['month_start'][:7]}}</td>
|
|
74
|
+
<td>
|
|
75
|
+
<a href="/e/supplies/{{values['supply_id']}}">View</a>
|
|
76
|
+
</td>
|
|
77
|
+
<td>
|
|
78
|
+
{% if values['imp_mpan_core'] is not none %}
|
|
79
|
+
{{values['imp_mpan_core']}}
|
|
80
|
+
{% endif %}
|
|
81
|
+
</td>
|
|
82
|
+
<td>
|
|
83
|
+
{% if values['exp_mpan_core'] is not none %}
|
|
84
|
+
{{values['exp_mpan_core']}}
|
|
85
|
+
{% endif %}
|
|
86
|
+
</td>
|
|
87
|
+
<td>{{values['account']}}</td>
|
|
88
|
+
<td>
|
|
89
|
+
<a href="/sites/{{values['site_id']}}" title="{{values['site_name']}}">
|
|
90
|
+
{{values['site_code']}}
|
|
91
|
+
</a>
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
{% endfor %}
|
|
95
|
+
</tbody>
|
|
96
|
+
</table>
|
|
97
|
+
{% endblock %}
|
chellow/utils.py
CHANGED
|
@@ -67,12 +67,15 @@ def req_zish(name):
|
|
|
67
67
|
def req_date(prefix, resolution="minute"):
|
|
68
68
|
year = req_int(f"{prefix}_year")
|
|
69
69
|
month = req_int(f"{prefix}_month")
|
|
70
|
-
day = req_int(f"{prefix}_day")
|
|
71
70
|
|
|
72
71
|
try:
|
|
73
|
-
if resolution == "
|
|
72
|
+
if resolution == "month":
|
|
73
|
+
d = ct_datetime(year, month)
|
|
74
|
+
elif resolution == "day":
|
|
75
|
+
day = req_int(f"{prefix}_day")
|
|
74
76
|
d = ct_datetime(year, month, day)
|
|
75
77
|
elif resolution == "minute":
|
|
78
|
+
day = req_int(f"{prefix}_day")
|
|
76
79
|
hour = req_int(f"{prefix}_hour")
|
|
77
80
|
minute = req_int(f"{prefix}_minute")
|
|
78
81
|
d = ct_datetime(year, month, day, hour, minute)
|
chellow/views.py
CHANGED
|
@@ -1538,6 +1538,19 @@ def report_run_get(run_id):
|
|
|
1538
1538
|
run=run,
|
|
1539
1539
|
rows=rows,
|
|
1540
1540
|
)
|
|
1541
|
+
elif run.name == "missing_e_bills":
|
|
1542
|
+
rows = g.sess.scalars(
|
|
1543
|
+
select(ReportRunRow)
|
|
1544
|
+
.filter(ReportRunRow.report_run == run)
|
|
1545
|
+
.order_by(
|
|
1546
|
+
ReportRunRow.data["values"]["month_start"],
|
|
1547
|
+
)
|
|
1548
|
+
).all()
|
|
1549
|
+
return render_template(
|
|
1550
|
+
"report_run_missing_e_bills.html",
|
|
1551
|
+
run=run,
|
|
1552
|
+
rows=rows,
|
|
1553
|
+
)
|
|
1541
1554
|
|
|
1542
1555
|
else:
|
|
1543
1556
|
order_by = "row.id"
|
|
@@ -1561,12 +1574,12 @@ def report_run_get(run_id):
|
|
|
1561
1574
|
)
|
|
1562
1575
|
|
|
1563
1576
|
|
|
1564
|
-
@home.route("/report_runs/<int:run_id>", methods=["
|
|
1565
|
-
def
|
|
1577
|
+
@home.route("/report_runs/<int:run_id>", methods=["DELETE"])
|
|
1578
|
+
def report_run_delete(run_id):
|
|
1566
1579
|
run = g.sess.query(ReportRun).filter(ReportRun.id == run_id).one()
|
|
1567
1580
|
run.delete(g.sess)
|
|
1568
1581
|
g.sess.commit()
|
|
1569
|
-
return
|
|
1582
|
+
return hx_redirect("/report_runs", 303)
|
|
1570
1583
|
|
|
1571
1584
|
|
|
1572
1585
|
@home.route("/report_runs/<int:run_id>/spreadsheet")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chellow
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1745911639.0.0
|
|
4
4
|
Summary: Web Application for checking UK energy bills.
|
|
5
5
|
Project-URL: Homepage, https://github.com/WessexWater/chellow
|
|
6
6
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -12,8 +12,8 @@ chellow/proxy.py,sha256=cVXIktPlX3tQ1BYcwxq0nJXKE6r3DtFTtfFHPq55HaM,1351
|
|
|
12
12
|
chellow/rate_server.py,sha256=fg-Pf_9Hk3bXmC9riPQNGQxBvLvBa_WtNYdwDCjnCSg,5678
|
|
13
13
|
chellow/rrun.py,sha256=1Kt2q_K9UoDG_nsZz-Q6XJiMNKroWqlqFdxn2M6Q8CA,2088
|
|
14
14
|
chellow/testing.py,sha256=Dj2c1NX8lVlygueOrh2eyYawLW6qKEHxNhXVVUaNRO0,3637
|
|
15
|
-
chellow/utils.py,sha256=
|
|
16
|
-
chellow/views.py,sha256=
|
|
15
|
+
chellow/utils.py,sha256=i3GQK9MIcweosZk2gi-nX_IFq2DxURAJDyNoLBg6YwM,19421
|
|
16
|
+
chellow/views.py,sha256=GVmmChaMaFmZmcsgvR9Z5xfSh4Y1mPEBDR90kiZH5Xk,85292
|
|
17
17
|
chellow/e/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
chellow/e/aahedc.py,sha256=d2usudp7KYWpU6Pk3fal5EQ47EbvkvKeaFGylnb3NWw,606
|
|
19
19
|
chellow/e/bill_importer.py,sha256=7UcnqNlKbJc2GhW9gy8sDp9GuqambJVpZLvbafOZztA,7411
|
|
@@ -22,7 +22,7 @@ chellow/e/bsuos.py,sha256=hdP9vnOJSuZl46OAkJeUg1XJYvYIBj4J6Sqce1Hy9Vs,15542
|
|
|
22
22
|
chellow/e/ccl.py,sha256=30dh_SvlgzsTQPPAJNZWILaMvbeDsv9-P-S1JxS5_SQ,3184
|
|
23
23
|
chellow/e/cfd.py,sha256=CWLdYeNjgqT6Ro8YRf4vhwXIAJ2aV4Wi6HLNClVSeaQ,14260
|
|
24
24
|
chellow/e/computer.py,sha256=aGti5aXRTdC85M-WRiAoZkC_76sqfAiP-79ggWZF09Q,68020
|
|
25
|
-
chellow/e/dno_rate_parser.py,sha256=
|
|
25
|
+
chellow/e/dno_rate_parser.py,sha256=NOVfS9HRDsc0rO282hU-IdrcuvMlC7VE8RySqd_5eT0,21762
|
|
26
26
|
chellow/e/duos.py,sha256=RHrn93I1ASO2uYkuF18qlhG4p-jpuJhd_g3o69wtP4U,31004
|
|
27
27
|
chellow/e/elexon.py,sha256=ALhXS9Es7PV0z9ukPbIramn3cf3iLyFi-PMWPSm5iOs,5487
|
|
28
28
|
chellow/e/energy_management.py,sha256=aXC2qlGt3FAODlNl_frWzVYAQrJLP8FFOiNX3m-QE_Y,12388
|
|
@@ -59,12 +59,13 @@ chellow/e/bill_parsers/gdf_csv.py,sha256=ZfK3Oc6oP28p_P9DIevLNB_zW2WLcEJ3Lvb1gL3
|
|
|
59
59
|
chellow/e/bill_parsers/haven_csv.py,sha256=0uENq8IgVNqdxfBQMBxLTSZWCOuDHXZC0xzk52SbfyE,13652
|
|
60
60
|
chellow/e/bill_parsers/haven_edi.py,sha256=YGPHRxPOhje9s32jqPHHELni2tooOYj3cMC_qaZVPq4,16107
|
|
61
61
|
chellow/e/bill_parsers/haven_edi_tprs.py,sha256=ZVX9CCqUybsot_Z0BEOJPvl9x5kSr7fEWyuJXvZDcz4,11841
|
|
62
|
-
chellow/e/bill_parsers/mm.py,sha256=
|
|
63
|
-
chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py,sha256=
|
|
62
|
+
chellow/e/bill_parsers/mm.py,sha256=tv7SAIbwFJYCiHz1ZwTV9QJeGkSo3-KuOp--DUimzLI,11227
|
|
63
|
+
chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py,sha256=YvQ0Q6HlZ4XSk6Phx1UWaTSj8FREVjwQe8nrpiLVzbU,5458
|
|
64
64
|
chellow/e/bill_parsers/settlement_dc_stark_xlsx.py,sha256=osCpUYUdLcPtlo7ngXWGw0ImnxssLa1fOSejMwe51-k,6381
|
|
65
65
|
chellow/e/bill_parsers/sse_edi.py,sha256=L85DOfNkqexeEIEr8pCBn_2sHJI-zEaw6cogpE3YyYM,15204
|
|
66
66
|
chellow/e/bill_parsers/sww_xls.py,sha256=QEjiuvwvr5FuWCfqqVw8LaA_vZyAKsvRAS5fw3xtFhM,7533
|
|
67
67
|
chellow/gas/bill_import.py,sha256=w0lPgK_Drzh8rtnEBQe3qFuxrgzZ6qQSgpaGrrGznMU,6549
|
|
68
|
+
chellow/gas/bill_parser_bgs_xlsx.py,sha256=PxjFMEB91QcGvyarCc9qHfO3-Rs_Twdo9iJU9uBYfa4,7834
|
|
68
69
|
chellow/gas/bill_parser_csv.py,sha256=Ecdy-apFT-mWAxddAsM4k1s-9-FpIaOfjP0oFc0rdQg,5557
|
|
69
70
|
chellow/gas/bill_parser_engie_edi.py,sha256=Ko0vZP-QdVQ1uuhS_5cdrii60_cM4b_LFJMoY0pZqnk,8950
|
|
70
71
|
chellow/gas/bill_parser_total_edi.py,sha256=bMAeIkzHwNhv0qdKYXtRl-tzUUYtjNkbM3PMl3NurFc,11225
|
|
@@ -107,6 +108,7 @@ chellow/reports/report_g_supplies_snapshot.py,sha256=9xB6RDrnbgxuomMcP1b1yEP4kOn
|
|
|
107
108
|
chellow/reports/report_g_supply_virtual_bill.py,sha256=EaYrB8PHJIXrUuhiZ7dwUlbNBkuyJebQHrdc308_z1o,3653
|
|
108
109
|
chellow/reports/report_g_virtual_bills.py,sha256=20vHa5LGQwOAlJlaGJaGszZrrbT0PMOZJf6hSxU2hIQ,4528
|
|
109
110
|
chellow/reports/report_g_virtual_bills_hh.py,sha256=gaiLEmKTpq6JsfZ1p0SdCDuPvzvigXp6z88gHRCA63w,3416
|
|
111
|
+
chellow/reports/report_missing_bills.py,sha256=3uWA6Wyskn30tUDOV_W6u_009flfKzua7vDh_jsBDaM,5956
|
|
110
112
|
chellow/reports/report_sscs.py,sha256=fQWyVG-gdg37DyNHgpNARpSxIwTl7mCn20fDLwx9oHg,3214
|
|
111
113
|
chellow/reports/report_supply_contacts.py,sha256=pvwlInaPYV_pa9MMK6vh854plHFwv3m5zo5xulR1g5I,3599
|
|
112
114
|
chellow/static/css/chellow.css,sha256=dnkuj9Z1BCOV_L2Y26lDd2QlTmFFhATa1YvwPVch1Oc,5375
|
|
@@ -145,6 +147,7 @@ chellow/templates/report_run_asset_comparison.html,sha256=VYCCUmIC7Mfe7uuaAHb6ih
|
|
|
145
147
|
chellow/templates/report_run_bill_check.html,sha256=H2ayoBL7EgKMq2Nwq5VjE_TNvcIKcqeCm0alQWLIw78,5084
|
|
146
148
|
chellow/templates/report_run_ecoes_comparison.html,sha256=VmkT5ypWLP8qZS6NbDTC4yDaG7mnUlxZz7EV8xkHGZw,4086
|
|
147
149
|
chellow/templates/report_run_g_bill_check.html,sha256=tOXl_mjR__foYKiOYflJbK-459actAtjzv8rfuL3TwM,4851
|
|
150
|
+
chellow/templates/report_run_missing_e_bills.html,sha256=l5idQhfaNhMvvzIRv-iqCpeDnYl_wgs6-mZMBOmuyR8,2447
|
|
148
151
|
chellow/templates/report_run_monthly_duration_org.html,sha256=gGNGJ4Q50q4BtIMi98rhO-7NqRHcsFUmbj2qzeOLejw,1713
|
|
149
152
|
chellow/templates/report_run_row.html,sha256=bmtcdqJaS1CXpL0i8PuqvmeF98jKNYX5-mnQu-OuDKQ,3791
|
|
150
153
|
chellow/templates/report_run_row_bill_check.html,sha256=aC2LMu_6NvmTN3ZdxHJPPPczyxPN6hg0F-PPcqIWUws,4683
|
|
@@ -200,7 +203,7 @@ chellow/templates/e/dc_bill_add.html,sha256=73Sn_MKBsUuYYnDfUMCdX1Dul6GimMC9YXk6
|
|
|
200
203
|
chellow/templates/e/dc_bill_edit.html,sha256=0GsN-ZIc4q-z_xs8igC2ZS6t4soo2SvB3qRA6iC-AuM,2707
|
|
201
204
|
chellow/templates/e/dc_bill_import.html,sha256=NHjMSoFizvFQIaPWuVE3nTCtMDTzJB0XmH8jXfV1tiA,2188
|
|
202
205
|
chellow/templates/e/dc_bill_imports.html,sha256=lCaUR47r9KPr0VrQhEvVEaKexM5R_nmkxtzgpWZ0e9s,2135
|
|
203
|
-
chellow/templates/e/dc_contract.html,sha256=
|
|
206
|
+
chellow/templates/e/dc_contract.html,sha256=MqkRck0qCgxXrHSbYTfyqjf_bAbIfMy93b9kdqH0pCQ,2746
|
|
204
207
|
chellow/templates/e/dc_contract_edit.html,sha256=IObmbHQmeZ_LSpnYgabmhoSNUR3aPmm-Jk5nJLM_u74,1706
|
|
205
208
|
chellow/templates/e/dc_contract_hh_import.html,sha256=JncR3L6cOK4jghsGyr-itEqlIemXBXt3kL09wpqnQSE,856
|
|
206
209
|
chellow/templates/e/dc_contract_hh_imports.html,sha256=eXFDGyzSgag4JRism81_p5yTzQOjCIXaVkQ8tl3dDcM,8172
|
|
@@ -257,7 +260,7 @@ chellow/templates/e/mop_bill_add.html,sha256=fjoJj6QvcCk28rPD0Z6tIXrFYZ7ygN6lIXu
|
|
|
257
260
|
chellow/templates/e/mop_bill_edit.html,sha256=Hld9b-ckLCd9N7hleugg0BuwsZrmkvDeiJ5yLW8z-Js,2748
|
|
258
261
|
chellow/templates/e/mop_bill_import.html,sha256=sSVCZnarru0GKYFW1uuP0f1Ix9iNFDtEypQ93hqeUsk,3201
|
|
259
262
|
chellow/templates/e/mop_bill_imports.html,sha256=jIhWaOuk4hYj9jhXBUxQ7C8v9HJljWfOGGeu1rZa1PI,1610
|
|
260
|
-
chellow/templates/e/mop_contract.html,sha256=
|
|
263
|
+
chellow/templates/e/mop_contract.html,sha256=M6_pZuNpjdh9EaSGA5UsqSFIAvB0ABOehJ7s5oQ3pvk,2144
|
|
261
264
|
chellow/templates/e/mop_contract_add.html,sha256=qyjDNz_pOVgfrsKaju0KwxBKJnWF9RxUa-8gUtaOc60,738
|
|
262
265
|
chellow/templates/e/mop_contract_edit.html,sha256=4DbyiKbWRdliQGPh0wlh4dEuhD-R4-3XRzXoNMLfFD4,1719
|
|
263
266
|
chellow/templates/e/mop_contracts.html,sha256=2TPVkeUefsYOVZIiiLMPJgwcrxhvjk8Io4UKvgdQ2AY,768
|
|
@@ -317,7 +320,7 @@ chellow/templates/e/supplier_bill_add.html,sha256=BsD-Zh7d9auiqJ61VPHiQrP8u8rTcw
|
|
|
317
320
|
chellow/templates/e/supplier_bill_edit.html,sha256=oxZrMcMwrvluJSPxD4yfM9mWNeugoguAwT_ai9Ynl88,2732
|
|
318
321
|
chellow/templates/e/supplier_bill_import.html,sha256=2_VvBoNE838UwuN0AiMwIrzqxQmWBo5DGY0lY833Bpk,5265
|
|
319
322
|
chellow/templates/e/supplier_bill_imports.html,sha256=9iTNGWKn9XjQTBP1Sepbo0QVNlgKh7EfizXapam7I6s,9356
|
|
320
|
-
chellow/templates/e/supplier_contract.html,sha256=
|
|
323
|
+
chellow/templates/e/supplier_contract.html,sha256=TOm5ey5b1tm0A-8g2EmZhY7WddbGDih1B4ieJknA8Ns,3254
|
|
321
324
|
chellow/templates/e/supplier_contract_add.html,sha256=gsozEtF24lzYi_Bb4LTenvh62tCt7dQ4CwaIz7rFck4,899
|
|
322
325
|
chellow/templates/e/supplier_contract_edit.html,sha256=Afwtn0l8XRbt86bMOru2AjjvctoUkieD052aY0B2mDc,1463
|
|
323
326
|
chellow/templates/e/supplier_contracts.html,sha256=VwWD4q88Fynz7vioFSAsyH6RR_1SyQQl6bQwzL-W1m0,1508
|
|
@@ -380,6 +383,6 @@ chellow/templates/g/supply_note_edit.html,sha256=b8mB6_ucBwoljp03iy6AgVaZUhGw3-1
|
|
|
380
383
|
chellow/templates/g/supply_notes.html,sha256=6epNmZ3NKdXZz27fvmRUGeffg_oc1kmwuBeyRzQe3Rg,854
|
|
381
384
|
chellow/templates/g/unit.html,sha256=KouNVU0-i84afANkLQ_heJ0uDfJ9H5A05PuLqb8iCN8,438
|
|
382
385
|
chellow/templates/g/units.html,sha256=p5Nd-lAIboKPEOO6N451hx1bcKxMg4BDODnZ-43MmJc,441
|
|
383
|
-
chellow-
|
|
384
|
-
chellow-
|
|
385
|
-
chellow-
|
|
386
|
+
chellow-1745911639.0.0.dist-info/METADATA,sha256=ayBjqwEd2px2L8nUYB1t63nDxqnf-NrSJIhWkln42HA,12238
|
|
387
|
+
chellow-1745911639.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
388
|
+
chellow-1745911639.0.0.dist-info/RECORD,,
|
|
File without changes
|