Browse Source

Budget template now includes 'Fond' column

main
Holger Frey 5 months ago
parent
commit
34318d94ec
  1. 12
      superx_budget/budget.py
  2. 16
      superx_budget/overview.py
  3. 6
      superx_budget/pyramid/templates.py
  4. 2
      superx_budget/superx.py
  5. BIN
      test_data/Budget-Vorlage-2024.xlsx
  6. BIN
      test_data/Verwendungsnachweis_und_Kassenstand_SAP.xlsx
  7. 2
      tests/conftest.py
  8. 27
      tests/test_budget_parser.py
  9. 1
      tests/test_helpers.py
  10. 10
      tests/test_overview.py
  11. 13
      tests/test_superx_parser.py

12
superx_budget/budget.py

@ -10,11 +10,13 @@ EXPECTED_TABLE_HEADERS = [
"Projekt", "Projekt",
"Laufzeit", "Laufzeit",
"BA", "BA",
"Fond",
"Budget", "Budget",
"Ausgaben", "Ausgaben",
"Rest", "Rest",
] ]
NUM_EXPECTED_HEADERS = len(EXPECTED_TABLE_HEADERS)
ExcelRow = namedtuple("ExcelRow", ["row", "data"]) ExcelRow = namedtuple("ExcelRow", ["row", "data"])
@ -26,6 +28,7 @@ BudgetData = namedtuple(
"project_name", "project_name",
"period", "period",
"project", "project",
"fond",
"budget", "budget",
"expenses", "expenses",
"rest", "rest",
@ -34,7 +37,9 @@ BudgetData = namedtuple(
def _check_table_header(xl_row): def _check_table_header(xl_row):
fields_ignore_none = (("" if c is None else c) for c in xl_row.data[:7]) fields_ignore_none = (
("" if c is None else c) for c in xl_row.data[:NUM_EXPECTED_HEADERS]
)
fields_str = (str(c) for c in fields_ignore_none) fields_str = (str(c) for c in fields_ignore_none)
fields = [c.strip() for c in fields_str] fields = [c.strip() for c in fields_str]
if fields != EXPECTED_TABLE_HEADERS: if fields != EXPECTED_TABLE_HEADERS:
@ -51,7 +56,10 @@ def _skip_empty_lines(rows):
def _parse_data_table(rows): def _parse_data_table(rows):
for xl_row in _skip_empty_lines(rows): for xl_row in _skip_empty_lines(rows):
data = [strip_excel_value(value) for value in xl_row.data[:7]] data = [
strip_excel_value(value)
for value in xl_row.data[:NUM_EXPECTED_HEADERS]
]
yield BudgetData(xl_row.row, *data) yield BudgetData(xl_row.row, *data)

16
superx_budget/overview.py

@ -34,6 +34,11 @@ class ProjectOverview:
"""returns the excel row number""" """returns the excel row number"""
return self.budget_data.row return self.budget_data.row
@property
def budget(self):
"""returns the budget"""
return self.budget_data.budget
@property @property
def expenses(self): def expenses(self):
"""returns the accumulated expenses""" """returns the accumulated expenses"""
@ -43,14 +48,21 @@ class ProjectOverview:
@property @property
def available(self): def available(self):
"""returns the still available budget""" """returns the still available rest budget"""
return self.budget_data.budget - self.expenses return (self.budget_data.budget or 0) - self.expenses
def add(self, description, kind, amount): def add(self, description, kind, amount):
"""adds an entry that modifies the available budget""" """adds an entry that modifies the available budget"""
entry = OverviewBudgetEntry(description, kind, amount) entry = OverviewBudgetEntry(description, kind, amount)
self.entries.append(entry) self.entries.append(entry)
def __repr__(self):
"""returns an informative string representation"""
return (
f"<ProjectOverview {self.project}:"
f"{self.budget} - {self.expenses} = {self.available}>"
)
def _create_overview_map(budget_list): def _create_overview_map(budget_list):
"""returns a dictonary with project as key and overview as value""" """returns a dictonary with project as key and overview as value"""

6
superx_budget/pyramid/templates.py

@ -32,9 +32,9 @@ def templates(context, request):
cache_max_age=0, cache_max_age=0,
content_type=XLSX_CONTENT_TYPE, content_type=XLSX_CONTENT_TYPE,
) )
response.headers[ response.headers["Content-Disposition"] = (
"Content-Disposition" f"attachment;filename={file_name}"
] = f"attachment;filename={file_name}" )
return response return response
tmp = list_budget_files(request.budgets_dir) tmp = list_budget_files(request.budgets_dir)

2
superx_budget/superx.py

@ -27,7 +27,7 @@ SuperXData = namedtuple(
"expenses", "expenses",
"revenue_actual", "revenue_actual",
"revenue_target", "revenue_target",
"acutal_value", "actual_value",
], ],
) )

BIN
test_data/Budget-Vorlage-2024.xlsx

Binary file not shown.

BIN
test_data/Verwendungsnachweis_und_Kassenstand_SAP.xlsx

Binary file not shown.

2
tests/conftest.py

@ -22,7 +22,7 @@ def example_root(request):
@pytest.fixture @pytest.fixture
def budget_example_file(example_root): def budget_example_file(example_root):
return example_root / "Budget-Vorlage-2020.xlsx" return example_root / "Budget-Vorlage-2024.xlsx"
@pytest.fixture @pytest.fixture

27
tests/test_budget_parser.py

@ -32,9 +32,9 @@ def test_parse_data_table():
from superx_budget.budget import ExcelRow, _parse_data_table from superx_budget.budget import ExcelRow, _parse_data_table
rows = [ rows = [
ExcelRow(2, list("ABCDEFG")), ExcelRow(2, list("ABCDEFGH")),
ExcelRow(3, [None for i in range(7)]), ExcelRow(3, [None for i in range(8)]),
ExcelRow(4, list("tuvwxyzX")), # one item more ExcelRow(4, list("stuvwxyzX")), # one item more
] ]
result = _parse_data_table(rows) result = _parse_data_table(rows)
@ -42,9 +42,9 @@ def test_parse_data_table():
assert first.row == 2 assert first.row == 2
assert first.number == "A" assert first.number == "A"
assert first.rest == "G" assert first.rest == "H"
assert last.row == 4 assert last.row == 4
assert last.number == "t" assert last.number == "s"
assert last.rest == "z" assert last.rest == "z"
@ -54,15 +54,12 @@ def test_parse_budget_data(budget_example_sheet):
result = parse_budget_data(budget_example_sheet) result = parse_budget_data(budget_example_sheet)
first, last = result[0], result[-1] first, last = result[0], result[-1]
assert len(result) == 18 assert len(result) == 20
assert first.row == 3 assert first.row == 3
assert first.number == 1 assert first.number == 1
assert first.project_name == "Safegurard I (neu)" assert first.project_name == "Safeguard IV"
assert first.rest == "=E3-F3" assert first.rest == "=F3-G3"
assert last.row == 54 assert last.row == 61
assert last.number == "=A51+1"
assert last.project_name == "ZIM Microcoat II"
assert last.rest == "=E54-F54"
def test_parse_budget_file(budget_example_file): def test_parse_budget_file(budget_example_file):
@ -71,8 +68,8 @@ def test_parse_budget_file(budget_example_file):
result = parse_budget_file(budget_example_file) result = parse_budget_file(budget_example_file)
first = result[0] first = result[0]
assert len(result) == 18 assert len(result) == 20
assert first.row == 3 assert first.row == 3
assert first.number == 1 assert first.number == 1
assert first.project_name == "Safegurard I (neu)" assert first.project_name == "Safeguard IV"
assert first.rest == "=E3-F3" assert first.rest == "=F3-G3"

1
tests/test_helpers.py

@ -109,6 +109,7 @@ def test_list_budget_files(example_root):
assert sorted(r.name for r in result) == [ assert sorted(r.name for r in result) == [
"Budget-Vorlage-2019.xlsx", "Budget-Vorlage-2019.xlsx",
"Budget-Vorlage-2020.xlsx", "Budget-Vorlage-2020.xlsx",
"Budget-Vorlage-2024.xlsx",
] ]

10
tests/test_overview.py

@ -11,6 +11,7 @@ def example_budget_data():
"Safegurard I", "Safegurard I",
"01.08.2019-31.07.2020", "01.08.2019-31.07.2020",
"1100000102", "1100000102",
"3210",
5000, 5000,
0, 0,
0, 0,
@ -85,8 +86,8 @@ def test_create_overview_map(budget_example_file):
budget_data = parse_budget_file(budget_example_file) budget_data = parse_budget_file(budget_example_file)
result = _create_overview_map(budget_data) result = _create_overview_map(budget_data)
assert len(result) == 18 assert len(result) == 20
assert "2100276501" in result assert "1083013701" in result
for key, value in result.items(): for key, value in result.items():
assert key == str(value.project) assert key == str(value.project)
@ -105,7 +106,8 @@ def test_create_entries_from_export(budget_example_file, superx_example_file):
_create_entries_from_superx(budget_map, superx_data.data) _create_entries_from_superx(budget_map, superx_data.data)
assert budget_map["1100000102"].available == 5000 - 0.01 - 1000 - 1 - 1001 expenses = 0.01 + 1000 + 1 + 1001
assert budget_map["1100068704"].available == 2000 - expenses
def test_filter_superx_material_expenses(superx_example_file): def test_filter_superx_material_expenses(superx_example_file):
@ -132,4 +134,4 @@ def test_create_overview(budget_example_file, superx_example_file):
budget_data = parse_budget_file(budget_example_file) budget_data = parse_budget_file(budget_example_file)
budget_map = create_overview(budget_data, superx_data) budget_map = create_overview(budget_data, superx_data)
assert budget_map["1100000102"].available == 5000 - 1 - 1001 assert budget_map["1100068704"].available == 2000 - 1 - 1001

13
tests/test_superx_parser.py

@ -1,6 +1,5 @@
""" Stub file for testing the project """ """ Stub file for testing the project """
import pytest import pytest
@ -83,9 +82,9 @@ def test_parse_data_table():
assert first_value.cost_center == "A" assert first_value.cost_center == "A"
assert first_value.fonds == "B" assert first_value.fonds == "B"
assert first_value.acutal_value == "J" assert first_value.actual_value == "J"
assert second_value.cost_center == "q" assert second_value.cost_center == "q"
assert second_value.acutal_value == "z" assert second_value.actual_value == "z"
def test_parse_export_data(superx_example_workbook): def test_parse_export_data(superx_example_workbook):
@ -102,14 +101,14 @@ def test_parse_export_data(superx_example_workbook):
first, last = result.data[0], result.data[-1] first, last = result.data[0], result.data[-1]
assert first.cost_center == "1110200121" assert first.cost_center == "1110200121"
assert first.fonds == "3310" assert first.fonds == "3310"
assert first.project == "1100000102" assert first.project == "1100068704"
assert first.kind == "1 - Personal" assert first.kind == "1 - Personal"
assert first.budget_year is None assert first.budget_year is None
assert first.obligo == 0.01 assert first.obligo == 0.01
assert first.expenses == 1000 assert first.expenses == 1000
assert first.revenue_actual == 2000 assert first.revenue_actual == 2000
assert first.revenue_target == 3000 assert first.revenue_target == 3000
assert first.acutal_value == 4000 assert first.actual_value == 4000
assert last.cost_center == "1110200121" assert last.cost_center == "1110200121"
assert last.fonds == "1123" assert last.fonds == "1123"
assert last.project == "8200062807" assert last.project == "8200062807"
@ -119,7 +118,7 @@ def test_parse_export_data(superx_example_workbook):
assert last.expenses == 1236 assert last.expenses == 1236
assert last.revenue_actual == 2236 assert last.revenue_actual == 2236
assert last.revenue_target == 3236 assert last.revenue_target == 3236
assert last.acutal_value == 4236 assert last.actual_value == 4236
def test_parse_exported_file(superx_example_file): def test_parse_exported_file(superx_example_file):
@ -131,5 +130,5 @@ def test_parse_exported_file(superx_example_file):
assert len(result.data) == 212 assert len(result.data) == 212
assert first.cost_center == "1110200121" assert first.cost_center == "1110200121"
assert first.fonds == "3310" assert first.fonds == "3310"
assert first.project == "1100000102" assert first.project == "1100068704"
assert first.kind == "1 - Personal" assert first.kind == "1 - Personal"

Loading…
Cancel
Save