From 9650066d48e46e50746979d657a32a2b7b51a126 Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sat, 19 Jan 2019 22:20:18 +0100
Subject: [PATCH] #268 - create pdf

---
 .../controller/ReportController.java          |  72 ++++-
 .../controller/TransactionController.java     |   4 +-
 .../entities/report/ReportSettings.java       |   9 +-
 .../budgetmaster/reports/Budget.java          |  32 ++
 .../budgetmaster/reports/CategoryBudget.java  |  41 +++
 .../budgetmaster/reports/ColumnType.java      |  13 +
 .../reports/ReportConfiguration.java          |  53 ++++
 .../reports/ReportConfigurationBuilder.java   |  43 +++
 .../budgetmaster/reports/ReportGenerator.java | 297 ------------------
 .../budgetmaster/reports/ReportItem.java      | 107 -------
 .../budgetmaster/services/HelpersService.java |   2 +-
 .../report/ReportGeneratorService.java        | 290 +++++++++++++++++
 .../budgetmaster/utils/Strings.java           |   7 +-
 src/main/resources/languages/_de.properties   |   3 +-
 src/main/resources/languages/_en.properties   |   3 +-
 15 files changed, 557 insertions(+), 419 deletions(-)
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/reports/Budget.java
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/reports/CategoryBudget.java
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfiguration.java
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfigurationBuilder.java
 delete mode 100644 src/main/java/de/deadlocker8/budgetmaster/reports/ReportGenerator.java
 delete mode 100644 src/main/java/de/deadlocker8/budgetmaster/reports/ReportItem.java
 create mode 100644 src/main/java/de/deadlocker8/budgetmaster/services/report/ReportGeneratorService.java

diff --git a/src/main/java/de/deadlocker8/budgetmaster/controller/ReportController.java b/src/main/java/de/deadlocker8/budgetmaster/controller/ReportController.java
index dd4812e61..71256d2fa 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/controller/ReportController.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/controller/ReportController.java
@@ -1,27 +1,41 @@
 package de.deadlocker8.budgetmaster.controller;
 
+import com.itextpdf.text.DocumentException;
+import de.deadlocker8.budgetmaster.entities.Transaction;
+import de.deadlocker8.budgetmaster.entities.account.Account;
 import de.deadlocker8.budgetmaster.entities.report.ReportColumn;
 import de.deadlocker8.budgetmaster.entities.report.ReportSettings;
-import de.deadlocker8.budgetmaster.repositories.SettingsRepository;
+import de.deadlocker8.budgetmaster.reports.Budget;
+import de.deadlocker8.budgetmaster.reports.ReportConfiguration;
+import de.deadlocker8.budgetmaster.reports.ReportConfigurationBuilder;
 import de.deadlocker8.budgetmaster.services.HelpersService;
+import de.deadlocker8.budgetmaster.services.SettingsService;
+import de.deadlocker8.budgetmaster.services.TransactionService;
 import de.deadlocker8.budgetmaster.services.report.ReportColumnService;
+import de.deadlocker8.budgetmaster.services.report.ReportGeneratorService;
 import de.deadlocker8.budgetmaster.services.report.ReportSettingsService;
+import de.thecodelabs.utils.util.Localization;
 import org.joda.time.DateTime;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
-import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.CookieValue;
 import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
 
 @Controller
 public class ReportController extends BaseController
 {
 	@Autowired
-	private SettingsRepository settingsRepository;
+	private SettingsService settingsService;
 
 	@Autowired
 	private ReportSettingsService reportSettingsService;
@@ -29,6 +43,12 @@ public class ReportController extends BaseController
 	@Autowired
 	private ReportColumnService reportColumnService;
 
+	@Autowired
+	private ReportGeneratorService reportGeneratorService;
+
+	@Autowired
+	private TransactionService transactionService;
+
 	@Autowired
 	private HelpersService helpers;
 
@@ -43,9 +63,8 @@ public class ReportController extends BaseController
 	}
 
 	@RequestMapping(value = "/reports/generate", method = RequestMethod.POST)
-	public String post(Model model,
-					   @ModelAttribute("NewReportSettings") ReportSettings reportSettings,
-					   BindingResult bindingResult)
+	public String post(HttpServletResponse response,
+					   @ModelAttribute("NewReportSettings") ReportSettings reportSettings)
 	{
 		reportSettingsService.getRepository().delete(0);
 		for(ReportColumn reportColumn : reportSettings.getColumns())
@@ -54,6 +73,47 @@ public class ReportController extends BaseController
 		}
 		reportSettingsService.getRepository().save(reportSettings);
 
+
+		LOGGER.debug("Exporting month report...");
+
+		//TODO handle all accounts
+		Account account = helpers.getCurrentAccount();
+		List<Transaction> transactions = transactionService.getTransactionsForMonthAndYear(account, reportSettings.getDate().getMonthOfYear(), reportSettings.getDate().getYear(), settingsService.getSettings().isRestActivated());
+		Budget budget = new Budget(helpers.getIncomeSumForTransactionList(transactions), helpers.getExpenditureSumForTransactionList(transactions));
+
+		ReportConfiguration reportConfiguration = new ReportConfigurationBuilder()
+				.setBudget(budget)
+				.setCategoryBudgets(new ArrayList<>())
+				.setReportSettings(reportSettings)
+				.setReportItems(new ArrayList<>())
+				.createReportConfiguration();
+
+		try
+		{
+			byte[] dataBytes = reportGeneratorService.generate(reportConfiguration);
+			String fileName = Localization.getString("report.initial.filename", reportSettings.getDate().toString("MM"), reportSettings.getDate().toString("YYYY"));
+			response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
+
+			response.setContentType("application/pdf; charset=UTF-8");
+			response.setContentLength(dataBytes.length);
+			response.setCharacterEncoding("UTF-8");
+
+			try(ServletOutputStream out = response.getOutputStream())
+			{
+				out.write(dataBytes);
+				out.flush();
+				LOGGER.debug("Exporting month report DONE");
+			}
+			catch(IOException e)
+			{
+				e.printStackTrace();
+			}
+		}
+		catch(DocumentException e)
+		{
+			e.printStackTrace();
+		}
+
 		return "redirect:/reports";
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/controller/TransactionController.java b/src/main/java/de/deadlocker8/budgetmaster/controller/TransactionController.java
index 67ed4305e..28571dc05 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/controller/TransactionController.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/controller/TransactionController.java
@@ -65,7 +65,7 @@ public class TransactionController extends BaseController
 
 		List<Transaction> transactions = transactionService.getTransactionsForMonthAndYear(helpers.getCurrentAccount(), date.getMonthOfYear(), date.getYear(), getSettings().isRestActivated());
 		int incomeSum = helpers.getIncomeSumForTransactionList(transactions);
-		int paymentSum = helpers.getPaymentSumForTransactionList(transactions);
+		int paymentSum = helpers.getExpenditureSumForTransactionList(transactions);
 		int rest = incomeSum + paymentSum;
 
 		model.addAttribute("transactions", transactions);
@@ -88,7 +88,7 @@ public class TransactionController extends BaseController
 		DateTime date = helpers.getDateTimeFromCookie(cookieDate);
 		List<Transaction> transactions = transactionService.getTransactionsForMonthAndYear(helpers.getCurrentAccount(), date.getMonthOfYear(), date.getYear(), getSettings().isRestActivated());
 		int incomeSum = helpers.getIncomeSumForTransactionList(transactions);
-		int paymentSum = helpers.getPaymentSumForTransactionList(transactions);
+		int paymentSum = helpers.getExpenditureSumForTransactionList(transactions);
 		int rest = incomeSum + paymentSum;
 
 		model.addAttribute("transactions", transactions);
diff --git a/src/main/java/de/deadlocker8/budgetmaster/entities/report/ReportSettings.java b/src/main/java/de/deadlocker8/budgetmaster/entities/report/ReportSettings.java
index 7ddead4f0..2b84a578a 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/entities/report/ReportSettings.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/entities/report/ReportSettings.java
@@ -9,6 +9,7 @@ import javax.persistence.OneToMany;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Entity
 public class ReportSettings
@@ -96,8 +97,12 @@ public class ReportSettings
 
 	public List<ReportColumn> getColumnsSorted()
 	{
-		columns.sort(Comparator.comparing(ReportColumn::getPosition));
-		return columns;
+		return columns.stream().sorted(Comparator.comparing(ReportColumn::getPosition)).collect(Collectors.toList());
+	}
+
+	public List<ReportColumn> getColumnsSortedAndFiltered()
+	{
+		return columns.stream().filter(ReportColumn::isActivated).sorted(Comparator.comparing(ReportColumn::getPosition)).collect(Collectors.toList());
 	}
 
 	public List<ReportColumn> getColumns()
diff --git a/src/main/java/de/deadlocker8/budgetmaster/reports/Budget.java b/src/main/java/de/deadlocker8/budgetmaster/reports/Budget.java
new file mode 100644
index 000000000..5ac312bee
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/reports/Budget.java
@@ -0,0 +1,32 @@
+package de.deadlocker8.budgetmaster.reports;
+
+public class Budget
+{
+	private double incomeSum;
+	private double expenditureSum;
+
+	public Budget(double incomeSum, double expenditureSum)
+	{
+		this.incomeSum = incomeSum;
+		this.expenditureSum = expenditureSum;
+	}
+
+	public double getIncomeSum()
+	{
+		return incomeSum;
+	}
+
+	public double getExpenditureSum()
+	{
+		return expenditureSum;
+	}
+
+	@Override
+	public String toString()
+	{
+		return "Budget{" +
+				"incomeSum=" + incomeSum +
+				", expenditureSum=" + expenditureSum +
+				'}';
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/reports/CategoryBudget.java b/src/main/java/de/deadlocker8/budgetmaster/reports/CategoryBudget.java
new file mode 100644
index 000000000..5c174297f
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/reports/CategoryBudget.java
@@ -0,0 +1,41 @@
+package de.deadlocker8.budgetmaster.reports;
+
+import de.deadlocker8.budgetmaster.entities.category.Category;
+
+public class CategoryBudget
+{
+	private Category category;
+	private double budget;
+
+	public CategoryBudget(Category category, double budget)
+	{
+		this.category = category;
+		this.budget = budget;
+	}
+
+	public Category getCategory()
+	{
+		return category;
+	}
+
+	public void setCategory(Category category)
+	{
+		this.category = category;
+	}
+
+	public double getBudget()
+	{
+		return budget;
+	}
+
+	public void setBudget(double budget)
+	{
+		this.budget = budget;
+	}
+
+	@Override
+	public String toString()
+	{
+		return "CategoryBudget [category=" + category + ", budget=" + budget + "]";
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/reports/ColumnType.java b/src/main/java/de/deadlocker8/budgetmaster/reports/ColumnType.java
index 89d417eab..afb9a7c4b 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/reports/ColumnType.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/reports/ColumnType.java
@@ -26,6 +26,19 @@ public enum ColumnType
 		this.proportion = proportion;
 	}
 
+	public static ColumnType getByName(String name)
+	{
+		for(ColumnType type : ColumnType.values())
+		{
+			if(type.name.equalsIgnoreCase(name))
+			{
+				return type;
+			}
+		}
+
+		return null;
+	}
+
 	public String getName()
 	{
 		return Localization.getString(name);
diff --git a/src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfiguration.java b/src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfiguration.java
new file mode 100644
index 000000000..283349f6d
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfiguration.java
@@ -0,0 +1,53 @@
+package de.deadlocker8.budgetmaster.reports;
+
+import de.deadlocker8.budgetmaster.entities.Transaction;
+import de.deadlocker8.budgetmaster.entities.report.ReportSettings;
+
+import java.util.ArrayList;
+
+public class ReportConfiguration
+{
+	private ArrayList<Transaction> transactions;
+	private ArrayList<CategoryBudget> categoryBudgets;
+	private ReportSettings reportSettings;
+	private Budget budget;
+
+	public ReportConfiguration(ArrayList<Transaction> transactions, ArrayList<CategoryBudget> categoryBudgets, ReportSettings reportSettings, Budget budget)
+	{
+		this.transactions = transactions;
+		this.categoryBudgets = categoryBudgets;
+		this.reportSettings = reportSettings;
+		this.budget = budget;
+	}
+
+	public ArrayList<Transaction> getTransactions()
+	{
+		return transactions;
+	}
+
+	public ArrayList<CategoryBudget> getCategoryBudgets()
+	{
+		return categoryBudgets;
+	}
+
+	public ReportSettings getReportSettings()
+	{
+		return reportSettings;
+	}
+
+	public Budget getBudget()
+	{
+		return budget;
+	}
+
+	@Override
+	public String toString()
+	{
+		return "ReportConfiguration{" +
+				"transactions=" + transactions +
+				", categoryBudgets=" + categoryBudgets +
+				", reportSettings=" + reportSettings +
+				", budget=" + budget +
+				'}';
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfigurationBuilder.java b/src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfigurationBuilder.java
new file mode 100644
index 000000000..1668579db
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/reports/ReportConfigurationBuilder.java
@@ -0,0 +1,43 @@
+package de.deadlocker8.budgetmaster.reports;
+
+import de.deadlocker8.budgetmaster.entities.Transaction;
+import de.deadlocker8.budgetmaster.entities.report.ReportSettings;
+
+import java.util.ArrayList;
+
+public class ReportConfigurationBuilder
+{
+	private ArrayList<Transaction> transactions;
+	private ArrayList<CategoryBudget> categoryBudgets;
+	private ReportSettings reportSettings;
+	private Budget budget;
+
+	public ReportConfigurationBuilder setReportItems(ArrayList<Transaction> transactions)
+	{
+		this.transactions = transactions;
+		return this;
+	}
+
+	public ReportConfigurationBuilder setCategoryBudgets(ArrayList<CategoryBudget> categoryBudgets)
+	{
+		this.categoryBudgets = categoryBudgets;
+		return this;
+	}
+
+	public ReportConfigurationBuilder setReportSettings(ReportSettings reportSettings)
+	{
+		this.reportSettings = reportSettings;
+		return this;
+	}
+
+	public ReportConfigurationBuilder setBudget(Budget budget)
+	{
+		this.budget = budget;
+		return this;
+	}
+
+	public ReportConfiguration createReportConfiguration()
+	{
+		return new ReportConfiguration(transactions, categoryBudgets, reportSettings, budget);
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/reports/ReportGenerator.java b/src/main/java/de/deadlocker8/budgetmaster/reports/ReportGenerator.java
deleted file mode 100644
index d015ac81e..000000000
--- a/src/main/java/de/deadlocker8/budgetmaster/reports/ReportGenerator.java
+++ /dev/null
@@ -1,297 +0,0 @@
-package de.deadlocker8.budgetmaster.reports;
-
-//import java.awt.Color;
-//import java.io.File;
-//import java.io.FileNotFoundException;
-//import java.io.FileOutputStream;
-//import java.util.ArrayList;
-//
-//import org.joda.time.DateTime;
-//import org.joda.time.format.DateTimeFormat;
-//
-//import com.itextpdf.text.BaseColor;
-//import com.itextpdf.text.Chapter;
-//import com.itextpdf.text.Chunk;
-//import com.itextpdf.text.Document;
-//import com.itextpdf.text.DocumentException;
-//import com.itextpdf.text.Element;
-//import com.itextpdf.text.Font;
-//import com.itextpdf.text.FontFactory;
-//import com.itextpdf.text.Paragraph;
-//import com.itextpdf.text.Phrase;
-//import com.itextpdf.text.pdf.BaseFont;
-//import com.itextpdf.text.pdf.GrayColor;
-//import com.itextpdf.text.pdf.PdfPCell;
-//import com.itextpdf.text.pdf.PdfPTable;
-//import com.itextpdf.text.pdf.PdfWriter;
-//
-//import de.deadlocker8.budgetmaster.logic.Budget;
-//import de.deadlocker8.budgetmaster.logic.category.CategoryBudget;
-//import de.deadlocker8.budgetmaster.logic.utils.Fonts;
-//import de.deadlocker8.budgetmaster.logic.utils.Helpers;
-//import de.deadlocker8.budgetmaster.logic.utils.Strings;
-//import tools.Localization;
-
-public class ReportGenerator
-{
-//	private ArrayList<ReportItem> reportItems;
-//	private ArrayList<CategoryBudget> categoryBudgets;
-//	private ReportPreferences reportPreferences;
-//	private File savePath;
-//	private String currency;
-//	private DateTime date;
-//	private Budget budget;
-//	private final String FONT = Fonts.OPEN_SANS;
-//
-//	public ReportGenerator(ArrayList<ReportItem> reportItems, ArrayList<CategoryBudget> categoryBudgets, ReportPreferences reportPreferences, File savePath, String currency, DateTime date, Budget budget)
-//	{
-//		this.reportItems = reportItems;
-//		this.categoryBudgets = categoryBudgets;
-//		this.reportPreferences = reportPreferences;
-//		this.savePath = savePath;
-//		this.currency = currency;
-//		this.date = date;
-//		this.budget = budget;
-//	}
-//
-//	private Chapter generateHeader()
-//	{
-//		Font font = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 16, Font.BOLDITALIC, BaseColor.BLACK);
-//		Chunk chunk = new Chunk(Localization.getString(Strings.REPORT_HEADLINE, date.toString("MMMM yyyy")), font);
-//		Chapter chapter = new Chapter(new Paragraph(chunk), 1);
-//		chapter.setNumberDepth(0);
-//		chapter.add(Chunk.NEWLINE);
-//		return chapter;
-//	}
-//
-//	private PdfPTable generateTable(int tableWidth, AmountType amountType)
-//	{
-//		int numberOfColumns = reportPreferences.getColumnOrder().getColumns().size();
-//		int totalIncome = 0;
-//		int totalPayment = 0;
-//
-//		if(numberOfColumns > 0)
-//		{
-//			float[] proportions = new float[numberOfColumns];
-//			for(int i = 0; i < reportPreferences.getColumnOrder().getColumns().size(); i++)
-//			{
-//				proportions[i] = reportPreferences.getColumnOrder().getColumns().get(i).getProportion();
-//			}
-//
-//			PdfPTable table = new PdfPTable(proportions);
-//			table.setWidthPercentage(tableWidth);
-//			Font font = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 8, Font.NORMAL, GrayColor.BLACK);
-//
-//			for(ColumnType column : reportPreferences.getColumnOrder().getColumns())
-//			{
-//				PdfPCell cell = new PdfPCell(new Phrase(column.getName(), font));
-//				cell.setBackgroundColor(GrayColor.LIGHT_GRAY);
-//				cell.setHorizontalAlignment(Element.ALIGN_CENTER);
-//				cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
-//				table.addCell(cell);
-//			}
-//
-//			for(ReportItem currentItem : reportItems)
-//			{
-//				if(currentItem.getAmount() > 0)
-//				{
-//					totalIncome += currentItem.getAmount();
-//					if(amountType == AmountType.PAYMENT)
-//					{
-//						continue;
-//					}
-//				}
-//				else
-//				{
-//					totalPayment += currentItem.getAmount();
-//					if(amountType == AmountType.INCOME)
-//					{
-//						continue;
-//					}
-//				}
-//
-//				for(ColumnType column : reportPreferences.getColumnOrder().getColumns())
-//				{
-//					PdfPCell cell = new PdfPCell(new Phrase(getProperty(currentItem, column), font));
-//					cell.setBackgroundColor(new BaseColor(Color.WHITE));
-//					cell.setHorizontalAlignment(Element.ALIGN_CENTER);
-//					cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
-//					table.addCell(cell);
-//				}
-//			}
-//
-//			PdfPCell cellTotal;
-//			String total = "";
-//			switch(amountType)
-//			{
-//				case BOTH:
-//					String totalIncomeString = Helpers.getCurrencyString(totalIncome, currency);
-//					String totalPaymentString = Helpers.getCurrencyString(totalPayment, currency);
-//					total = Localization.getString(Strings.REPORT_SUM_TOTAL, totalIncomeString, totalPaymentString);
-//					break;
-//				case INCOME:
-//					total = Localization.getString(Strings.REPORT_SUM, Helpers.getCurrencyString(totalIncome, currency));
-//					break;
-//				case PAYMENT:
-//					total = Localization.getString(Strings.REPORT_SUM, Helpers.getCurrencyString(totalPayment, currency));
-//					break;
-//				default:
-//					break;
-//			}
-//
-//			cellTotal = new PdfPCell(new Phrase(total, font));
-//			cellTotal.setBackgroundColor(new BaseColor(Color.WHITE));
-//			cellTotal.setColspan(numberOfColumns);
-//			cellTotal.setHorizontalAlignment(Element.ALIGN_RIGHT);
-//			cellTotal.setVerticalAlignment(Element.ALIGN_MIDDLE);
-//			table.addCell(cellTotal);
-//
-//			return table;
-//		}
-//		return null;
-//	}
-//
-//	public void generate() throws FileNotFoundException, DocumentException
-//	{
-//		Document document = new Document();
-//		PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(savePath));
-//		writer.setPageEvent(new HeaderFooterPageEvent());
-//		document.open();
-//		document.setMargins(50, 45, 50, 70);
-//		Font headerFont = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 14, Font.BOLD, BaseColor.BLACK);
-//		Font smallHeaderFont = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.BOLD, BaseColor.BLACK);
-//
-//		document.add(generateHeader());
-//		document.add(Chunk.NEWLINE);
-//
-//		if(reportPreferences.isIncludeBudget())
-//		{
-//			Font fontGreen = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.NORMAL, new BaseColor(36, 122, 45));
-//			Font fontRed = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.NORMAL, BaseColor.RED);
-//			Font fontBlack = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.BOLD, BaseColor.BLACK);
-//
-//			document.add(new Paragraph(Localization.getString(Strings.REPORT_BUDGET), headerFont));
-//			document.add(Chunk.NEWLINE);
-//			document.add(new Paragraph(Localization.getString(Strings.REPORT_INCOMES) + Helpers.getCurrencyString(budget.getIncomeSum(), currency), fontGreen));
-//			document.add(new Paragraph(Localization.getString(Strings.REPORT_PAYMENTS) + Helpers.getCurrencyString(budget.getPaymentSum(), currency), fontRed));
-//			document.add(new Paragraph(Localization.getString(Strings.REPORT_BUDGET_REST) + Helpers.getCurrencyString(budget.getIncomeSum() + budget.getPaymentSum(), currency), fontBlack));
-//			document.add(Chunk.NEWLINE);
-//		}
-//
-//		document.add(new Paragraph(Localization.getString(Strings.REPORT_HEADLINE_PAYMENTS_OVERVIEW), headerFont));
-//		document.add(Chunk.NEWLINE);
-//
-//		if(reportPreferences.isSplitTable())
-//		{
-//			document.add(new Paragraph(Localization.getString(Strings.TITLE_INCOMES), smallHeaderFont));
-//			document.add(Chunk.NEWLINE);
-//
-//			PdfPTable table = generateTable(100, AmountType.INCOME);
-//			if(table != null)
-//			{
-//				document.add(table);
-//			}
-//
-//			document.add(Chunk.NEWLINE);
-//			document.add(new Paragraph(Localization.getString(Strings.TITLE_PAYMENTS), smallHeaderFont));
-//			document.add(Chunk.NEWLINE);
-//
-//			table = generateTable(100, AmountType.PAYMENT);
-//			if(table != null)
-//			{
-//				document.add(table);
-//			}
-//		}
-//		else
-//		{
-//			PdfPTable table = generateTable(100, AmountType.BOTH);
-//			if(table != null)
-//			{
-//				document.add(table);
-//			}
-//		}
-//
-//		if(reportPreferences.isIncludeCategoryBudgets())
-//		{
-//			document.add(Chunk.NEWLINE);
-//			document.add(new Paragraph(Localization.getString(Strings.TITLE_CATEGORY_BUDGETS), smallHeaderFont));
-//			document.add(Chunk.NEWLINE);
-//
-//			PdfPTable table = generateCategoryBudgets();
-//			if(table != null)
-//			{
-//				document.add(table);
-//			}
-//		}
-//
-//		document.close();
-//	}
-//
-//	private PdfPTable generateCategoryBudgets()
-//	{
-//		PdfPTable table = new PdfPTable(2);
-//		table.setWidthPercentage(100);
-//		Font font = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 8, Font.NORMAL, BaseColor.BLACK);
-//
-//		//header cells
-//		PdfPCell cellHeaderCategory = new PdfPCell(new Phrase(Localization.getString(Strings.TITLE_CATEGORY), font));
-//		cellHeaderCategory.setBackgroundColor(GrayColor.LIGHT_GRAY);
-//		cellHeaderCategory.setHorizontalAlignment(Element.ALIGN_CENTER);
-//		table.addCell(cellHeaderCategory);
-//		PdfPCell cellHeaderAmount = new PdfPCell(new Phrase(Localization.getString(Strings.TITLE_AMOUNT), font));
-//		cellHeaderAmount.setBackgroundColor(GrayColor.LIGHT_GRAY);
-//		cellHeaderAmount.setHorizontalAlignment(Element.ALIGN_CENTER);
-//		table.addCell(cellHeaderAmount);
-//
-//		for(CategoryBudget budget : categoryBudgets)
-//		{
-//			PdfPCell cellName = new PdfPCell(new Phrase(budget.getCategory().getName(), font));
-//			cellName.setBackgroundColor(new BaseColor(Color.WHITE));
-//			cellName.setHorizontalAlignment(Element.ALIGN_CENTER);
-//			cellName.setVerticalAlignment(Element.ALIGN_MIDDLE);
-//			table.addCell(cellName);
-//
-//			PdfPCell cellAmount = new PdfPCell(new Phrase(Helpers.getCurrencyString(budget.getBudget() / 100.0, currency), font));
-//			cellAmount.setBackgroundColor(new BaseColor(Color.WHITE));
-//			cellAmount.setHorizontalAlignment(Element.ALIGN_CENTER);
-//			cellAmount.setVerticalAlignment(Element.ALIGN_MIDDLE);
-//			table.addCell(cellAmount);
-//		}
-//
-//		return table;
-//	}
-//
-//	private String getProperty(ReportItem reportItem, ColumnType columnType)
-//	{
-//		switch(columnType)
-//		{
-//			case AMOUNT:
-//				return Helpers.getCurrencyString(reportItem.getAmount(), currency);
-//			case CATEGORY:
-//				return reportItem.getCategory().getName();
-//			case DATE:
-//				return DateTime.parse(reportItem.getDate(), DateTimeFormat.forPattern("YYYY-MM-dd")).toString("dd.MM.YYYY");
-//			case DESCRIPTION:
-//				return reportItem.getDescription();
-//			case TAGS:
-//				return reportItem.getTags();
-//			case NAME:
-//				return reportItem.getName();
-//			case POSITION:
-//				return String.valueOf(reportItem.getPosition());
-//			case RATING:
-//				return reportItem.getAmount() > 0 ? "+" : "-";
-//			case REPEATING:
-//				if(reportItem.getRepeating())
-//				{
-//					return Localization.getString(Strings.REPORT_REPEATING_YES);
-//				}
-//				else
-//				{
-//					return Localization.getString(Strings.REPORT_REPEATING_NO);
-//				}
-//			default:
-//				return null;
-//		}
-//	}
-}
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/reports/ReportItem.java b/src/main/java/de/deadlocker8/budgetmaster/reports/ReportItem.java
deleted file mode 100644
index 3223635cc..000000000
--- a/src/main/java/de/deadlocker8/budgetmaster/reports/ReportItem.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package de.deadlocker8.budgetmaster.reports;
-
-
-import de.deadlocker8.budgetmaster.entities.category.Category;
-
-public class ReportItem
-{
-	private int position;
-	private int amount;
-	private String date;
-	private Category category;
-	private String name;
-	private String description;
-	private String tags;
-	private boolean repeating;
-
-	public ReportItem()
-	{
-		
-	}
-
-	public int getPosition()
-	{
-		return position;
-	}
-
-	public void setPosition(int position)
-	{
-		this.position = position;
-	}
-
-	public int getAmount()
-	{
-		return amount;
-	}
-
-	public void setAmount(int amount)
-	{
-		this.amount = amount;
-	}
-
-	public String getDate()
-	{
-		return date;
-	}
-
-	public void setDate(String date)
-	{
-		this.date = date;
-	}
-
-	public Category getCategory()
-	{
-		return category;
-	}
-
-	public void setCategory(Category category)
-	{
-		this.category = category;
-	}
-
-	public String getName()
-	{
-		return name;
-	}
-
-	public void setName(String name)
-	{
-		this.name = name;
-	}
-
-	public String getDescription()
-	{
-		return description;
-	}
-
-	public void setDescription(String description)
-	{
-		this.description = description;
-	}	
-
-	public String getTags()
-	{
-		return tags;
-	}
-
-	public void setTags(String tags)
-	{
-		this.tags = tags;
-	}
-
-	public boolean getRepeating()
-	{
-		return repeating;
-	}
-
-	public void setRepeating(boolean repeating)
-	{
-		this.repeating = repeating;
-	}
-
-	@Override
-	public String toString()
-	{
-		return "ReportItem [position=" + position + ", amount=" + amount + ", date=" + date + ", category=" + category + ", name=" + name + ", description=" + description + ", tags=" + tags + ", repeating=" + repeating + "]";
-	}
-}
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java b/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java
index 32ce882fd..823701936 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/services/HelpersService.java
@@ -231,7 +231,7 @@ public class HelpersService
 		return sum;
 	}
 
-	public int getPaymentSumForTransactionList(List<Transaction> transactions)
+	public int getExpenditureSumForTransactionList(List<Transaction> transactions)
 	{
 		int sum = 0;
 		for(Transaction transaction : transactions)
diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/report/ReportGeneratorService.java b/src/main/java/de/deadlocker8/budgetmaster/services/report/ReportGeneratorService.java
new file mode 100644
index 000000000..79aa8a211
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/services/report/ReportGeneratorService.java
@@ -0,0 +1,290 @@
+package de.deadlocker8.budgetmaster.services.report;
+
+import com.itextpdf.text.*;
+import com.itextpdf.text.pdf.*;
+import de.deadlocker8.budgetmaster.entities.Tag;
+import de.deadlocker8.budgetmaster.entities.Transaction;
+import de.deadlocker8.budgetmaster.entities.report.ReportColumn;
+import de.deadlocker8.budgetmaster.reports.*;
+import de.deadlocker8.budgetmaster.services.HelpersService;
+import de.deadlocker8.budgetmaster.utils.Strings;
+import de.thecodelabs.utils.util.Localization;
+import javafx.scene.paint.Color;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class ReportGeneratorService
+{
+	@Autowired
+	HelpersService helpersService;
+
+	private final static String FONT = Fonts.OPEN_SANS;
+
+	@Autowired
+	public ReportGeneratorService(HelpersService helpersService)
+	{
+		this.helpersService = helpersService;
+	}
+
+	private Chapter generateHeader(ReportConfiguration reportConfiguration)
+	{
+		Font font = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 16, Font.BOLDITALIC, BaseColor.BLACK);
+		Chunk chunk = new Chunk(Localization.getString(Strings.REPORT_HEADLINE, reportConfiguration.getReportSettings().getDate().toString("MMMM yyyy")), font);
+		Chapter chapter = new Chapter(new Paragraph(chunk), 1);
+		chapter.setNumberDepth(0);
+		chapter.add(Chunk.NEWLINE);
+		return chapter;
+	}
+
+	private PdfPTable generateTable(ReportConfiguration reportConfiguration, int tableWidth, AmountType amountType)
+	{
+		List<ReportColumn> columns = reportConfiguration.getReportSettings().getColumnsSortedAndFiltered();
+		int numberOfColumns = columns.size();
+		int totalIncome = 0;
+		int totalPayment = 0;
+
+		if(numberOfColumns > 0)
+		{
+			float[] proportions = new float[numberOfColumns];
+			for(int i = 0; i < columns.size(); i++)
+			{
+				ReportColumn column = columns.get(i);
+				proportions[i] = ColumnType.getByName(column.getKey()).getProportion();
+			}
+
+			PdfPTable table = new PdfPTable(proportions);
+			table.setWidthPercentage(tableWidth);
+			Font font = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 8, Font.NORMAL, GrayColor.BLACK);
+
+			// add table header
+			for(ReportColumn column : columns)
+			{
+				ColumnType columnType = ColumnType.getByName(column.getKey());
+
+				PdfPCell cell = new PdfPCell(new Phrase(columnType.getName(), font));
+				cell.setBackgroundColor(GrayColor.LIGHT_GRAY);
+				cell.setHorizontalAlignment(Element.ALIGN_CENTER);
+				cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
+				table.addCell(cell);
+			}
+
+			for(Transaction currentItem : reportConfiguration.getTransactions())
+			{
+				if(currentItem.getAmount() > 0)
+				{
+					totalIncome += currentItem.getAmount();
+					if(amountType == AmountType.PAYMENT)
+					{
+						continue;
+					}
+				}
+				else
+				{
+					totalPayment += currentItem.getAmount();
+					if(amountType == AmountType.INCOME)
+					{
+						continue;
+					}
+				}
+
+				for(int i = 0; i < columns.size(); i++)
+				{
+					ReportColumn column = columns.get(i);
+					ColumnType columnType = ColumnType.getByName(column.getKey());
+					PdfPCell cell = new PdfPCell(new Phrase(getProperty(currentItem, columnType, i), font));
+					cell.setBackgroundColor(getBaseColor(Color.WHITE));
+					cell.setHorizontalAlignment(Element.ALIGN_CENTER);
+					cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
+					table.addCell(cell);
+				}
+			}
+
+			PdfPCell cellTotal;
+			String total = "";
+			switch(amountType)
+			{
+				case BOTH:
+					String totalIncomeString = helpersService.getCurrencyString(totalIncome);
+					String totalPaymentString = helpersService.getCurrencyString(totalPayment);
+					total = Localization.getString(Strings.REPORT_SUM_TOTAL, totalIncomeString, totalPaymentString);
+					break;
+				case INCOME:
+					total = Localization.getString(Strings.REPORT_SUM, helpersService.getCurrencyString(totalIncome));
+					break;
+				case PAYMENT:
+					total = Localization.getString(Strings.REPORT_SUM, helpersService.getCurrencyString(totalPayment));
+					break;
+				default:
+					break;
+			}
+
+			cellTotal = new PdfPCell(new Phrase(total, font));
+			cellTotal.setBackgroundColor(getBaseColor(Color.WHITE));
+			cellTotal.setColspan(numberOfColumns);
+			cellTotal.setHorizontalAlignment(Element.ALIGN_RIGHT);
+			cellTotal.setVerticalAlignment(Element.ALIGN_MIDDLE);
+			table.addCell(cellTotal);
+
+			return table;
+		}
+		return null;
+	}
+
+	public byte[] generate(ReportConfiguration reportConfiguration) throws DocumentException
+	{
+		Document document = new Document();
+		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+		PdfWriter writer = PdfWriter.getInstance(document, byteArrayOutputStream);
+		writer.setPageEvent(new HeaderFooterPageEvent());
+		document.open();
+		document.setMargins(50, 45, 50, 70);
+		Font headerFont = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 14, Font.BOLD, BaseColor.BLACK);
+		Font smallHeaderFont = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.BOLD, BaseColor.BLACK);
+
+		document.add(generateHeader(reportConfiguration));
+		document.add(Chunk.NEWLINE);
+
+		if(reportConfiguration.getReportSettings().isIncludeBudget())
+		{
+			Font fontGreen = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.NORMAL, new BaseColor(36, 122, 45));
+			Font fontRed = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.NORMAL, BaseColor.RED);
+			Font fontBlack = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, Font.BOLD, BaseColor.BLACK);
+
+			Budget budget = reportConfiguration.getBudget();
+
+			document.add(new Paragraph(Localization.getString(Strings.REPORT_BUDGET), headerFont));
+			document.add(Chunk.NEWLINE);
+			document.add(new Paragraph(Localization.getString(Strings.REPORT_INCOMES) + helpersService.getCurrencyString(budget.getIncomeSum()), fontGreen));
+			document.add(new Paragraph(Localization.getString(Strings.REPORT_PAYMENTS) + helpersService.getCurrencyString(budget.getExpenditureSum()), fontRed));
+			document.add(new Paragraph(Localization.getString(Strings.REPORT_BUDGET_REST) + helpersService.getCurrencyString(budget.getIncomeSum() + budget.getExpenditureSum()), fontBlack));
+			document.add(Chunk.NEWLINE);
+		}
+
+		document.add(new Paragraph(Localization.getString(Strings.REPORT_HEADLINE_TRANSACTIONS_OVERVIEW), headerFont));
+		document.add(Chunk.NEWLINE);
+
+		if(reportConfiguration.getReportSettings().isSplitTables())
+		{
+			document.add(new Paragraph(Localization.getString(Strings.TITLE_INCOMES), smallHeaderFont));
+			document.add(Chunk.NEWLINE);
+
+			PdfPTable table = generateTable(reportConfiguration, 100, AmountType.INCOME);
+			if(table != null)
+			{
+				document.add(table);
+			}
+
+			document.add(Chunk.NEWLINE);
+			document.add(new Paragraph(Localization.getString(Strings.TITLE_EXPENDITURES), smallHeaderFont));
+			document.add(Chunk.NEWLINE);
+
+			table = generateTable(reportConfiguration,100, AmountType.PAYMENT);
+			if(table != null)
+			{
+				document.add(table);
+			}
+		}
+		else
+		{
+			PdfPTable table = generateTable(reportConfiguration, 100, AmountType.BOTH);
+			if(table != null)
+			{
+				document.add(table);
+			}
+		}
+
+		if(reportConfiguration.getReportSettings().isIncludeCategoryBudgets())
+		{
+			document.add(Chunk.NEWLINE);
+			document.add(new Paragraph(Localization.getString(Strings.TITLE_CATEGORY_BUDGETS), smallHeaderFont));
+			document.add(Chunk.NEWLINE);
+
+			PdfPTable table = generateCategoryBudgets(reportConfiguration);
+			if(table != null)
+			{
+				document.add(table);
+			}
+		}
+
+		document.close();
+		return byteArrayOutputStream.toByteArray();
+	}
+
+	private PdfPTable generateCategoryBudgets(ReportConfiguration reportConfiguration)
+	{
+		PdfPTable table = new PdfPTable(2);
+		table.setWidthPercentage(100);
+		Font font = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 8, Font.NORMAL, BaseColor.BLACK);
+
+		//header cells
+		PdfPCell cellHeaderCategory = new PdfPCell(new Phrase(Localization.getString(Strings.REPORT_CATEGORY), font));
+		cellHeaderCategory.setBackgroundColor(GrayColor.LIGHT_GRAY);
+		cellHeaderCategory.setHorizontalAlignment(Element.ALIGN_CENTER);
+		table.addCell(cellHeaderCategory);
+		PdfPCell cellHeaderAmount = new PdfPCell(new Phrase(Localization.getString(Strings.REPORT_AMOUNT), font));
+		cellHeaderAmount.setBackgroundColor(GrayColor.LIGHT_GRAY);
+		cellHeaderAmount.setHorizontalAlignment(Element.ALIGN_CENTER);
+		table.addCell(cellHeaderAmount);
+
+		for(CategoryBudget budget : reportConfiguration.getCategoryBudgets())
+		{
+			PdfPCell cellName = new PdfPCell(new Phrase(budget.getCategory().getName(), font));
+			cellName.setBackgroundColor(getBaseColor(Color.WHITE));
+			cellName.setHorizontalAlignment(Element.ALIGN_CENTER);
+			cellName.setVerticalAlignment(Element.ALIGN_MIDDLE);
+			table.addCell(cellName);
+
+			PdfPCell cellAmount = new PdfPCell(new Phrase(helpersService.getCurrencyString(budget.getBudget() / 100.0), font));
+			cellAmount.setBackgroundColor(getBaseColor(Color.WHITE));
+			cellAmount.setHorizontalAlignment(Element.ALIGN_CENTER);
+			cellAmount.setVerticalAlignment(Element.ALIGN_MIDDLE);
+			table.addCell(cellAmount);
+		}
+
+		return table;
+	}
+
+	private String getProperty(Transaction transaction, ColumnType columnType, int position)
+	{
+		switch(columnType)
+		{
+			case AMOUNT:
+				return helpersService.getCurrencyString(transaction.getAmount());
+			case CATEGORY:
+				return transaction.getCategory().getName();
+			case DATE:
+				return transaction.getDate().toString("dd.MM.YYYY");
+			case DESCRIPTION:
+				return transaction.getDescription();
+			case TAGS:
+				return transaction.getTags().stream().map(Tag::getName).collect(Collectors.joining(", "));
+			case NAME:
+				return transaction.getName();
+			case POSITION:
+				return String.valueOf(position);
+			case RATING:
+				return transaction.getAmount() > 0 ? "+" : "-";
+			case REPEATING:
+				if(transaction.isRepeating())
+				{
+					return Localization.getString(Strings.REPORT_REPEATING_YES);
+				}
+				else
+				{
+					return Localization.getString(Strings.REPORT_REPEATING_NO);
+				}
+			default:
+				return null;
+		}
+	}
+
+	private BaseColor getBaseColor(Color color)
+	{
+		return new BaseColor((float) color.getRed(), (float) color.getGreen(), (float) color.getBlue());
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/de/deadlocker8/budgetmaster/utils/Strings.java b/src/main/java/de/deadlocker8/budgetmaster/utils/Strings.java
index 23bb89567..0ea9e82c5 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/utils/Strings.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/utils/Strings.java
@@ -6,6 +6,9 @@ public class Strings
     public static final String CATEGORY_NONE = "category.none";
     public static final String CATEGORY_REST = "category.rest";
     public static final String VERSION = "version";
+    public static final String TITLE_INCOMES = "title.incomes";
+    public static final String TITLE_EXPENDITURES = "title.expenditures";
+    public static final String TITLE_CATEGORY_BUDGETS = "title.category.budgets";
 
     //ACCOUNT
     public static final String ACCOUNT_DEFAULT_NAME = "account.default.name";
@@ -59,7 +62,7 @@ public class Strings
     public static final String REPORT_RATING = "report.rating";
     public static final String REPORT_AMOUNT = "report.amount";
     public static final String REPORT_HEADLINE = "report.headline";
-    public static final String REPORT_HEADLINE_PAYMENTS_OVERVIEW = "report.headline.payments.overview";
+    public static final String REPORT_HEADLINE_TRANSACTIONS_OVERVIEW = "report.headline.transactions.overview";
     public static final String REPORT_SUM_TOTAL = "report.sum.total";
     public static final String REPORT_SUM = "report.sum";
     public static final String REPORT_REPEATING_YES ="report.repeating.yes";
@@ -67,6 +70,6 @@ public class Strings
     public static final String REPORT_INITIAL_FILENAME ="report.initial.filename";
     public static final String REPORT_BUDGET = "report.budget";
     public static final String REPORT_INCOMES = "report.incomes";
-    public static final String REPORT_PAYMENTS = "report.payments";
+    public static final String REPORT_PAYMENTS = "report.expenditures";
     public static final String REPORT_BUDGET_REST = "report.budget.rest";
 }
\ No newline at end of file
diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties
index ef2ca7744..14449f562 100644
--- a/src/main/resources/languages/_de.properties
+++ b/src/main/resources/languages/_de.properties
@@ -35,6 +35,7 @@ title.transaction.new=Neue Buchung
 title.datepicker=Datum w�hlen
 title.account.new=Neues Konto
 title.account.edit=Konto bearbeiten
+title.category.budgets=Verbrauch nach Kategorien
 
 # MISC
 category.none=Keine Kategorie
@@ -231,7 +232,7 @@ report.repeating.no=Nein
 report.initial.filename=BudgetMaster Monatsbericht - {0}_{1}.pdf
 report.budget=Budget
 report.incomes=Einnahmen: 
-report.payments=Ausgaben: 
+report.expenditures=Ausgaben: 
 report.budget.rest=Restbudget: 
 
 # home menu
diff --git a/src/main/resources/languages/_en.properties b/src/main/resources/languages/_en.properties
index 28e4810ab..c11d823c7 100644
--- a/src/main/resources/languages/_en.properties
+++ b/src/main/resources/languages/_en.properties
@@ -35,6 +35,7 @@ title.transaction.new=New Transaction
 title.datepicker=Choose date
 title.account.new=New Account
 title.account.edit=Edit Account
+title.category.budgets=Consumption by categories
 
 # MISC
 category.none=No Category
@@ -230,7 +231,7 @@ report.repeating.no=No
 report.initial.filename=BudgetMaster Month Report - {0}_{1}.pdf
 report.budget=Budget
 report.incomes=Incomes: 
-report.payments=Expenditures: 
+report.expenditures=Expenditures: 
 report.budget.rest=Remaining Budget: 
 
 # home menu
-- 
GitLab