diff --git a/README.md b/README.md index cb888a97..2fa5cd9e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ App to hold regional code for Germany, built on top of ERPNext. ### Features +- German accounting reports + + - _Summen- und Saldenliste_ + - Section for Register Information (Registerart, -gericht und nummer) in **Company**, **Customer** and **Supplier** ![Section with Register Information](docs/register_information.png) diff --git a/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/__init__.py b/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js b/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js new file mode 100644 index 00000000..31fe8386 --- /dev/null +++ b/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js @@ -0,0 +1,52 @@ +// Copyright (c) 2023, ALYF GmbH and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Summen- und Saldenliste"] = { + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(), + reqd: 1, + }, + { + fieldname: "month", + label: __("Month"), + fieldtype: "Select", + default: get_previous_month(), + options: [ + { value: 1, label: __("January") }, + { value: 2, label: __("February") }, + { value: 3, label: __("March") }, + { value: 4, label: __("April") }, + { value: 5, label: __("May") }, + { value: 6, label: __("June") }, + { value: 7, label: __("July") }, + { value: 8, label: __("August") }, + { value: 9, label: __("September") }, + { value: 10, label: __("October") }, + { value: 11, label: __("November") }, + { value: 12, label: __("December") }, + ], + reqd: 1, + }, + ], +}; + +function get_previous_month() { + /* Return the 1-indexed number of the previous month */ + const date = new Date(); + date.setDate(0); // set to last day of previous month + return date.getMonth() + 1; // getMonth() is 0-indexed +} diff --git a/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.json b/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.json new file mode 100644 index 00000000..f65c825a --- /dev/null +++ b/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.json @@ -0,0 +1,33 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2023-12-28 20:21:45.244186", + "disable_prepared_report": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letter_head": "ALYF GmbH", + "modified": "2023-12-28 20:21:50.528134", + "modified_by": "Administrator", + "module": "ERPNext Germany", + "name": "Summen- und Saldenliste", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "GL Entry", + "report_name": "Summen- und Saldenliste", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py b/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py new file mode 100644 index 00000000..937aac42 --- /dev/null +++ b/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py @@ -0,0 +1,187 @@ +# Copyright (c) 2023, ALYF GmbH and contributors +# For license information, please see license.txt + +from datetime import date +from calendar import monthrange +from babel.dates import format_date + +import frappe +from frappe import _ +from frappe.query_builder.functions import Sum, Cast +from pypika.terms import Case + + +def execute(filters=None): + fy_start, month_start, month_end = get_dates( + int(filters.month), filters.fiscal_year + ) + current_month_name = format_date( + month_start, format="MMMM", locale=frappe.local.lang + ) + return get_columns(current_month_name), get_data( + filters.company, fy_start, month_start, month_end + ) + + +def get_columns(current_month_name: str): + return [ + { + "fieldname": "account", + "label": _("Account"), + "fieldtype": "Link", + "options": "Account", + "width": 400, + }, + { + "fieldname": "account_currency", + "label": _("Currency"), + "fieldtype": "Link", + "options": "Currency", + "width": 100, + }, + { + "fieldname": "debit_opening_balance", + "label": _("Debit Opening Balance"), + "fieldtype": "Currency", + "width": 170, + "options": "account_currency", + }, + { + "fieldname": "credit_opening_balance", + "label": _("Credit Opening Balance"), + "fieldtype": "Currency", + "width": 170, + "options": "account_currency", + }, + { + "fieldname": "debit_until_evaluation_period", + "label": _("Debit until {0}").format(current_month_name), + "fieldtype": "Currency", + "width": 170, + "options": "account_currency", + }, + { + "fieldname": "credit_until_evaluation_period", + "label": _("Credit until {0}").format(current_month_name), + "fieldtype": "Currency", + "width": 170, + "options": "account_currency", + }, + { + "fieldname": "debit_in_evaluation_period", + "label": _("Debit in {0}").format(current_month_name), + "fieldtype": "Currency", + "width": 170, + "options": "account_currency", + }, + { + "fieldname": "credit_in_evaluation_period", + "label": _("Credit in {0}").format(current_month_name), + "fieldtype": "Currency", + "width": 170, + "options": "account_currency", + }, + ] + + +def get_data(company: str, fy_start, month_start, month_end): + gl_entry = frappe.qb.DocType("GL Entry") + account = frappe.qb.DocType("Account") + + opening_balance = ( + frappe.qb.from_(gl_entry) + .left_join(account) + .on(gl_entry.account == account.name) + .select( + gl_entry.account, + Case() + .when( + account.root_type == "Asset", + Sum(gl_entry.debit_in_account_currency) + - Sum(gl_entry.credit_in_account_currency), + ) + .else_(None) + .as_("debit"), + Case() + .when( + account.root_type.isin(("Liability", "Equity")), + Sum(gl_entry.credit_in_account_currency) + - Sum(gl_entry.debit_in_account_currency), + ) + .else_(None) + .as_("credit"), + ) + .where( + (gl_entry.company == company) + & (gl_entry.is_cancelled == 0) + & (gl_entry.posting_date < fy_start) + ) + .groupby(gl_entry.account) + ) + + sum_until_month = ( + frappe.qb.from_(gl_entry) + .select( + gl_entry.account, + Sum(gl_entry.debit_in_account_currency).as_("debit"), + Sum(gl_entry.credit_in_account_currency).as_("credit"), + ) + .where( + (gl_entry.company == company) + & (gl_entry.is_cancelled == 0) + & (gl_entry.posting_date >= fy_start) + & (gl_entry.posting_date < month_start) + & (gl_entry.voucher_type != "Period Closing Voucher") + ) + .groupby(gl_entry.account) + ) + + sum_in_month = ( + frappe.qb.from_(gl_entry) + .left_join(account) + .on(gl_entry.account == account.name) + .select( + gl_entry.account, + gl_entry.account_currency, + Sum(gl_entry.debit_in_account_currency).as_("debit"), + Sum(gl_entry.credit_in_account_currency).as_("credit"), + ) + .where( + (gl_entry.company == company) + & (gl_entry.is_cancelled == 0) + & (gl_entry.posting_date >= month_start) + & (gl_entry.posting_date <= month_end) + & (gl_entry.voucher_type != "Period Closing Voucher") + ) + .orderby(Cast(account.account_number, "int")) + .groupby(gl_entry.account, gl_entry.account_currency) + ) + + query = ( + frappe.qb.from_(sum_in_month) + .left_join(sum_until_month) + .on(sum_until_month.account == sum_in_month.account) + .left_join(opening_balance) + .on(opening_balance.account == sum_in_month.account) + .select( + sum_in_month.account, + sum_in_month.account_currency, + opening_balance.debit, + opening_balance.credit, + sum_until_month.debit, + sum_until_month.credit, + sum_in_month.debit, + sum_in_month.credit, + ) + ) + + return query.run() + + +def get_dates(month: int, fiscal_year: str): + """Returns the start and end date for the given month.""" + fy_start: date = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date") + month_start = fy_start.replace(month=month, day=1) + last_day_of_month = monthrange(month_start.year, month_start.month)[1] + month_end = month_start.replace(day=last_day_of_month) + return fy_start, month_start, month_end diff --git a/erpnext_germany/translations/de.csv b/erpnext_germany/translations/de.csv index 841f20b5..aabfa146 100644 --- a/erpnext_germany/translations/de.csv +++ b/erpnext_germany/translations/de.csv @@ -43,3 +43,9 @@ Invalid Input,Ungültige Eingabe Error,Fehler Cannot delete this transaction,Diese Transaktion kann nicht gelöscht werden Only the most recent {0} can be deleted in order to avoid gaps in numbering.,"Nur die neueste Transaktion vom Typ {0} kann gelöscht werden, um Lücken in der Nummerierung zu vermeiden." +Debit until {0},Soll bis {0}, +Credit until {0},Haben bis {0}, +Debit in {0},Soll im {0}, +Credit in {0},Haben im {0}, +Debit Opening Balance,Soll Anfangsbestand, +Credit Opening Balance,Haben Anfangsbestand,