From dec2c90866a2f6df3fd95f9ad5c5923704a83bb8 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 29 Jul 2020 17:14:23 +0200 Subject: [PATCH 1/4] refactor: move datev-specific stuff to utils --- erpnext/regional/germany/utils/__init__.py | 0 .../regional/germany/utils/datev/__init__.py | 0 .../utils}/datev/datev_constants.py | 73 ----- .../regional/germany/utils/datev/datev_csv.py | 176 +++++++++++ erpnext/regional/report/datev/datev.py | 279 ++++++------------ erpnext/regional/report/datev/test_datev.py | 18 +- 6 files changed, 275 insertions(+), 271 deletions(-) create mode 100644 erpnext/regional/germany/utils/__init__.py create mode 100644 erpnext/regional/germany/utils/datev/__init__.py rename erpnext/regional/{report => germany/utils}/datev/datev_constants.py (89%) create mode 100644 erpnext/regional/germany/utils/datev/datev_csv.py diff --git a/erpnext/regional/germany/utils/__init__.py b/erpnext/regional/germany/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/germany/utils/datev/__init__.py b/erpnext/regional/germany/utils/datev/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py similarity index 89% rename from erpnext/regional/report/datev/datev_constants.py rename to erpnext/regional/germany/utils/datev/datev_constants.py index e063703005..88dc02dfca 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/germany/utils/datev/datev_constants.py @@ -460,79 +460,6 @@ ACCOUNT_NAME_COLUMNS = [ "Sprach-ID" ] -QUERY_REPORT_COLUMNS = [ - { - "label": "Umsatz (ohne Soll/Haben-Kz)", - "fieldname": "Umsatz (ohne Soll/Haben-Kz)", - "fieldtype": "Currency", - "width": 100 - }, - { - "label": "Soll/Haben-Kennzeichen", - "fieldname": "Soll/Haben-Kennzeichen", - "fieldtype": "Data", - "width": 100 - }, - { - "label": "Konto", - "fieldname": "Konto", - "fieldtype": "Data", - "width": 100 - }, - { - "label": "Gegenkonto (ohne BU-Schlüssel)", - "fieldname": "Gegenkonto (ohne BU-Schlüssel)", - "fieldtype": "Data", - "width": 100 - }, - { - "label": "Belegdatum", - "fieldname": "Belegdatum", - "fieldtype": "Date", - "width": 100 - }, - { - "label": "Belegfeld 1", - "fieldname": "Belegfeld 1", - "fieldtype": "Data", - "width": 150 - }, - { - "label": "Buchungstext", - "fieldname": "Buchungstext", - "fieldtype": "Text", - "width": 300 - }, - { - "label": "Beleginfo - Art 1", - "fieldname": "Beleginfo - Art 1", - "fieldtype": "Link", - "options": "DocType", - "width": 100 - }, - { - "label": "Beleginfo - Inhalt 1", - "fieldname": "Beleginfo - Inhalt 1", - "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 1", - "width": 150 - }, - { - "label": "Beleginfo - Art 2", - "fieldname": "Beleginfo - Art 2", - "fieldtype": "Link", - "options": "DocType", - "width": 100 - }, - { - "label": "Beleginfo - Inhalt 2", - "fieldname": "Beleginfo - Inhalt 2", - "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 2", - "width": 150 - } -] - class DataCategory(): """Field of the CSV Header.""" diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py new file mode 100644 index 0000000000..be41a4ab45 --- /dev/null +++ b/erpnext/regional/germany/utils/datev/datev_csv.py @@ -0,0 +1,176 @@ +# coding: utf-8 +from __future__ import unicode_literals + +import datetime +import zipfile +import six +import frappe +import pandas as pd +from frappe import _ +from csv import QUOTE_NONNUMERIC +from six import BytesIO +from six import string_types +from .datev_constants import DataCategory +from .datev_constants import Transactions +from .datev_constants import AccountNames + + +def get_datev_csv(data, filters, csv_class): + """ + Fill in missing columns and return a CSV in DATEV Format. + + For automatic processing, DATEV requires the first line of the CSV file to + hold meta data such as the length of account numbers oder the category of + the data. + + Arguments: + data -- array of dictionaries + filters -- dict + csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS + """ + empty_df = pd.DataFrame(columns=csv_class.COLUMNS) + data_df = pd.DataFrame.from_records(data) + result = empty_df.append(data_df, sort=True) + + if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS: + result['Belegdatum'] = pd.to_datetime(result['Belegdatum']) + + if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES: + result['Sprach-ID'] = 'de-DE' + + data = result.to_csv( + # Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035 + sep=str(';'), + # European decimal seperator + decimal=',', + # Windows "ANSI" encoding + encoding='latin_1', + # format date as DDMM + date_format='%d%m', + # Windows line terminator + line_terminator='\r\n', + # Do not number rows + index=False, + # Use all columns defined above + columns=csv_class.COLUMNS, + # Quote most fields, even currency values with "," separator + quoting=QUOTE_NONNUMERIC + ) + + if not six.PY2: + data = data.encode('latin_1') + + header = get_header(filters, csv_class) + header = ';'.join(header).encode('latin_1') + + # 1st Row: Header with meta data + # 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here. + # 3rd - nth Row: Data (Nutzdaten) + return header + b'\r\n' + data + + +def get_header(filters, csv_class): + description = filters.get('voucher_type', csv_class.FORMAT_NAME) + company = filters.get('company') + datev_settings = frappe.get_doc('DATEV Settings', {'client': company}) + default_currency = frappe.get_value('Company', company, 'default_currency') + coa = frappe.get_value('Company', company, 'chart_of_accounts') + coa_short_code = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '') + + header = [ + # DATEV format + # "DTVF" = created by DATEV software, + # "EXTF" = created by other software + '"EXTF"', + # version of the DATEV format + # 141 = 1.41, + # 510 = 5.10, + # 720 = 7.20 + '700', + csv_class.DATA_CATEGORY, + '"%s"' % csv_class.FORMAT_NAME, + # Format version (regarding format name) + csv_class.FORMAT_VERSION, + # Generated on + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000', + # Imported on -- stays empty + '', + # Origin. Any two symbols, will be replaced by "SV" on import. + '"EN"', + # I = Exported by + '"%s"' % frappe.session.user, + # J = Imported by -- stays empty + '', + # K = Tax consultant number (Beraternummer) + datev_settings.get('consultant_number', '0000000'), + # L = Tax client number (Mandantennummer) + datev_settings.get('client_number', '00000'), + # M = Start of the fiscal year (Wirtschaftsjahresbeginn) + frappe.utils.formatdate(frappe.defaults.get_user_default('year_start_date'), 'yyyyMMdd'), + # N = Length of account numbers (Sachkontenlänge) + datev_settings.get('account_number_length', '4'), + # O = Transaction batch start date (YYYYMMDD) + frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # P = Transaction batch end date (YYYYMMDD) + frappe.utils.formatdate(filters.get('to_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # Q = Description (for example, "Sales Invoice") Max. 30 chars + '"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # R = Diktatkürzel + '', + # S = Buchungstyp + # 1 = Transaction batch (Finanzbuchführung), + # 2 = Annual financial statement (Jahresabschluss) + '1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # T = Rechnungslegungszweck + # 0 oder leer = vom Rechnungslegungszweck unabhängig + # 50 = Handelsrecht + # 30 = Steuerrecht + # 64 = IFRS + # 40 = Kalkulatorik + # 11 = Reserviert + # 12 = Reserviert + '0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # U = Festschreibung + # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" + '0', + # V = Default currency, for example, "EUR" + '"%s"' % default_currency if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # reserviert + '', + # Derivatskennzeichen + '', + # reserviert + '', + # reserviert + '', + # SKR + '"%s"' % coa_short_code, + # Branchen-Lösungs-ID + '', + # reserviert + '', + # reserviert + '', + # Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung) + '' + ] + return header + + +def download_csv_files_as_zip(csv_data_list): + """ + Put CSV files in a zip archive and send that to the client. + + Params: + csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}] + """ + zip_buffer = BytesIO() + + datev_zip = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED) + for csv_file in csv_data_list: + datev_zip.writestr(csv_file.get('file_name'), csv_file.get('csv_data')) + datev_zip.close() + + frappe.response['filecontent'] = zip_buffer.getvalue() + frappe.response['filename'] = 'DATEV.zip' + frappe.response['type'] = 'binary' diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 7fec94e740..97d93d3b80 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -9,31 +9,92 @@ Provide a report and downloadable CSV according to the German DATEV format. """ from __future__ import unicode_literals -import datetime import json -import zipfile import six import frappe -import pandas as pd - from frappe import _ -from csv import QUOTE_NONNUMERIC -from six import BytesIO from six import string_types -from .datev_constants import DataCategory -from .datev_constants import Transactions -from .datev_constants import DebtorsCreditors -from .datev_constants import AccountNames -from .datev_constants import QUERY_REPORT_COLUMNS +from erpnext.regional.germany.utils.datev.datev_csv import download_csv_files_as_zip, get_datev_csv +from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames + +COLUMNS = [ + { + "label": "Umsatz (ohne Soll/Haben-Kz)", + "fieldname": "Umsatz (ohne Soll/Haben-Kz)", + "fieldtype": "Currency", + "width": 100 + }, + { + "label": "Soll/Haben-Kennzeichen", + "fieldname": "Soll/Haben-Kennzeichen", + "fieldtype": "Data", + "width": 100 + }, + { + "label": "Konto", + "fieldname": "Konto", + "fieldtype": "Data", + "width": 100 + }, + { + "label": "Gegenkonto (ohne BU-Schlüssel)", + "fieldname": "Gegenkonto (ohne BU-Schlüssel)", + "fieldtype": "Data", + "width": 100 + }, + { + "label": "Belegdatum", + "fieldname": "Belegdatum", + "fieldtype": "Date", + "width": 100 + }, + { + "label": "Belegfeld 1", + "fieldname": "Belegfeld 1", + "fieldtype": "Data", + "width": 150 + }, + { + "label": "Buchungstext", + "fieldname": "Buchungstext", + "fieldtype": "Text", + "width": 300 + }, + { + "label": "Beleginfo - Art 1", + "fieldname": "Beleginfo - Art 1", + "fieldtype": "Link", + "options": "DocType", + "width": 100 + }, + { + "label": "Beleginfo - Inhalt 1", + "fieldname": "Beleginfo - Inhalt 1", + "fieldtype": "Dynamic Link", + "options": "Beleginfo - Art 1", + "width": 150 + }, + { + "label": "Beleginfo - Art 2", + "fieldname": "Beleginfo - Art 2", + "fieldtype": "Link", + "options": "DocType", + "width": 100 + }, + { + "label": "Beleginfo - Inhalt 2", + "fieldname": "Beleginfo - Inhalt 2", + "fieldtype": "Dynamic Link", + "options": "Beleginfo - Art 2", + "width": 150 + } +] def execute(filters=None): """Entry point for frappe.""" validate(filters) - result = get_transactions(filters, as_dict=0) - columns = QUERY_REPORT_COLUMNS - - return columns, result + return COLUMNS, get_transactions(filters, as_dict=0) def validate(filters): @@ -240,146 +301,8 @@ def get_account_names(filters): """, filters, as_dict=1) -def get_datev_csv(data, filters, csv_class): - """ - Fill in missing columns and return a CSV in DATEV Format. - - For automatic processing, DATEV requires the first line of the CSV file to - hold meta data such as the length of account numbers oder the category of - the data. - - Arguments: - data -- array of dictionaries - filters -- dict - csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS - """ - empty_df = pd.DataFrame(columns=csv_class.COLUMNS) - data_df = pd.DataFrame.from_records(data) - - result = empty_df.append(data_df, sort=True) - - if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS: - result['Belegdatum'] = pd.to_datetime(result['Belegdatum']) - - if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES: - result['Sprach-ID'] = 'de-DE' - - data = result.to_csv( - # Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035 - sep=str(';'), - # European decimal seperator - decimal=',', - # Windows "ANSI" encoding - encoding='latin_1', - # format date as DDMM - date_format='%d%m', - # Windows line terminator - line_terminator='\r\n', - # Do not number rows - index=False, - # Use all columns defined above - columns=csv_class.COLUMNS, - # Quote most fields, even currency values with "," separator - quoting=QUOTE_NONNUMERIC - ) - - if not six.PY2: - data = data.encode('latin_1') - - header = get_header(filters, csv_class) - header = ';'.join(header).encode('latin_1') - - # 1st Row: Header with meta data - # 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here. - # 3rd - nth Row: Data (Nutzdaten) - return header + b'\r\n' + data - - -def get_header(filters, csv_class): - description = filters.get('voucher_type', csv_class.FORMAT_NAME) - - header = [ - # DATEV format - # "DTVF" = created by DATEV software, - # "EXTF" = created by other software - '"EXTF"', - # version of the DATEV format - # 141 = 1.41, - # 510 = 5.10, - # 720 = 7.20 - '700', - csv_class.DATA_CATEGORY, - '"%s"' % csv_class.FORMAT_NAME, - # Format version (regarding format name) - csv_class.FORMAT_VERSION, - # Generated on - datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000', - # Imported on -- stays empty - '', - # Origin. Any two symbols, will be replaced by "SV" on import. - '"EN"', - # I = Exported by - '"%s"' % frappe.session.user, - # J = Imported by -- stays empty - '', - # K = Tax consultant number (Beraternummer) - filters.get('consultant_number', '0000000'), - # L = Tax client number (Mandantennummer) - filters.get('client_number', '00000'), - # M = Start of the fiscal year (Wirtschaftsjahresbeginn) - frappe.utils.formatdate(frappe.defaults.get_user_default("year_start_date"), "yyyyMMdd"), - # N = Length of account numbers (Sachkontenlänge) - '%d' % filters.get('acc_len', 4), - # O = Transaction batch start date (YYYYMMDD) - frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # P = Transaction batch end date (YYYYMMDD) - frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # Q = Description (for example, "Sales Invoice") Max. 30 chars - '"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # R = Diktatkürzel - '', - # S = Buchungstyp - # 1 = Transaction batch (Finanzbuchführung), - # 2 = Annual financial statement (Jahresabschluss) - '1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # T = Rechnungslegungszweck - # 0 oder leer = vom Rechnungslegungszweck unabhängig - # 50 = Handelsrecht - # 30 = Steuerrecht - # 64 = IFRS - # 40 = Kalkulatorik - # 11 = Reserviert - # 12 = Reserviert - '0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # U = Festschreibung - # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" - '0', - # V = Default currency, for example, "EUR" - '"%s"' % filters.get('default_currency', 'EUR') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # reserviert - '', - # Derivatskennzeichen - '', - # reserviert - '', - # reserviert - '', - # SKR - '"%s"' % filters.get('skr', '04'), - # Branchen-Lösungs-ID - '', - # reserviert - '', - # reserviert - '', - # Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung) - '' - ] - return header - - @frappe.whitelist() -def download_datev_csv(filters=None): +def download_datev_csv(filters): """ Provide accounting entries for download in DATEV format. @@ -400,38 +323,26 @@ def download_datev_csv(filters=None): coa = frappe.get_value('Company', filters.get('company'), 'chart_of_accounts') filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '') - # set account number length - account_numbers = frappe.get_list('Account', fields=['account_number'], filters={'is_group': 0, 'account_number': ('!=', '')}) - filters['acc_len'] = max([len(a.account_number) for a in account_numbers]) - - filters['consultant_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'consultant_number') - filters['client_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'client_number') - filters['default_currency'] = frappe.get_value('Company', filters.get('company'), 'default_currency') - - # This is where my zip will be written - zip_buffer = BytesIO() - # This is my zip file - datev_zip = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED) - transactions = get_transactions(filters) - transactions_csv = get_datev_csv(transactions, filters, csv_class=Transactions) - datev_zip.writestr('EXTF_Buchungsstapel.csv', transactions_csv) - account_names = get_account_names(filters) - account_names_csv = get_datev_csv(account_names, filters, csv_class=AccountNames) - datev_zip.writestr('EXTF_Kontenbeschriftungen.csv', account_names_csv) - customers = get_customers(filters) - customers_csv = get_datev_csv(customers, filters, csv_class=DebtorsCreditors) - datev_zip.writestr('EXTF_Kunden.csv', customers_csv) - suppliers = get_suppliers(filters) - suppliers_csv = get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors) - datev_zip.writestr('EXTF_Lieferanten.csv', suppliers_csv) - - # You must call close() before exiting your program or essential records will not be written. - datev_zip.close() - frappe.response['filecontent'] = zip_buffer.getvalue() - frappe.response['filename'] = 'DATEV.zip' - frappe.response['type'] = 'binary' + download_csv_files_as_zip([ + { + 'file_name': 'EXTF_Buchungsstapel.csv', + 'csv_data': get_datev_csv(transactions, filters, csv_class=Transactions) + }, + { + 'file_name': 'EXTF_Kontenbeschriftungen.csv', + 'csv_data': get_datev_csv(account_names, filters, csv_class=AccountNames) + }, + { + 'file_name': 'EXTF_Kunden.csv', + 'csv_data': get_datev_csv(customers, filters, csv_class=DebtorsCreditors) + }, + { + 'file_name': 'EXTF_Lieferanten.csv', + 'csv_data': get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors) + }, + ]) diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py index eed62a8690..9529923a73 100644 --- a/erpnext/regional/report/datev/test_datev.py +++ b/erpnext/regional/report/datev/test_datev.py @@ -1,32 +1,22 @@ # coding=utf-8 from __future__ import unicode_literals -import os -import json import zipfile +import frappe from six import BytesIO from unittest import TestCase - -import frappe -from frappe.utils import getdate, today, now_datetime, cstr -from frappe.test_runner import make_test_objects +from frappe.utils import today, now_datetime, cstr from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts from erpnext.regional.report.datev.datev import validate from erpnext.regional.report.datev.datev import get_transactions from erpnext.regional.report.datev.datev import get_customers from erpnext.regional.report.datev.datev import get_suppliers from erpnext.regional.report.datev.datev import get_account_names -from erpnext.regional.report.datev.datev import get_datev_csv -from erpnext.regional.report.datev.datev import get_header from erpnext.regional.report.datev.datev import download_datev_csv -from erpnext.regional.report.datev.datev_constants import DataCategory -from erpnext.regional.report.datev.datev_constants import Transactions -from erpnext.regional.report.datev.datev_constants import DebtorsCreditors -from erpnext.regional.report.datev.datev_constants import AccountNames -from erpnext.regional.report.datev.datev_constants import QUERY_REPORT_COLUMNS +from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, get_header +from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames def make_company(company_name, abbr): if not frappe.db.exists("Company", company_name): From b719378ee5e2d1d8701db82650d14f0536d0b4cd Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 29 Jul 2020 18:11:04 +0200 Subject: [PATCH 2/4] fix: codacy --- erpnext/regional/germany/utils/datev/datev_csv.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py index be41a4ab45..df95a5b95a 100644 --- a/erpnext/regional/germany/utils/datev/datev_csv.py +++ b/erpnext/regional/germany/utils/datev/datev_csv.py @@ -9,15 +9,12 @@ import pandas as pd from frappe import _ from csv import QUOTE_NONNUMERIC from six import BytesIO -from six import string_types from .datev_constants import DataCategory from .datev_constants import Transactions -from .datev_constants import AccountNames def get_datev_csv(data, filters, csv_class): - """ - Fill in missing columns and return a CSV in DATEV Format. + """Fill in missing columns and return a CSV in DATEV Format. For automatic processing, DATEV requires the first line of the CSV file to hold meta data such as the length of account numbers oder the category of @@ -158,8 +155,7 @@ def get_header(filters, csv_class): def download_csv_files_as_zip(csv_data_list): - """ - Put CSV files in a zip archive and send that to the client. + """Put CSV files in a zip archive and send that to the client. Params: csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}] From 517df5fb0eba9e06c3623c80b05bfba42b75b470 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 29 Jul 2020 18:16:49 +0200 Subject: [PATCH 3/4] fix: codacy (again) --- erpnext/regional/germany/utils/datev/datev_constants.py | 2 ++ erpnext/regional/report/datev/datev.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/germany/utils/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py index 88dc02dfca..63f9a777bb 100644 --- a/erpnext/regional/germany/utils/datev/datev_constants.py +++ b/erpnext/regional/germany/utils/datev/datev_constants.py @@ -461,6 +461,7 @@ ACCOUNT_NAME_COLUMNS = [ ] class DataCategory(): + """Field of the CSV Header.""" DEBTORS_CREDITORS = "16" @@ -469,6 +470,7 @@ class DataCategory(): POSTING_TEXT_CONSTANTS = "67" class FormatName(): + """Field of the CSV Header, corresponds to DataCategory.""" DEBTORS_CREDITORS = "Debitoren/Kreditoren" diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 97d93d3b80..dd818e6054 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -10,7 +10,6 @@ Provide a report and downloadable CSV according to the German DATEV format. from __future__ import unicode_literals import json -import six import frappe from frappe import _ from six import string_types From 16f1435d630eff379ff809b975bec430fccc6c7e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 11 Sep 2020 18:57:58 +0200 Subject: [PATCH 4/4] fix: codacy --- erpnext/regional/germany/utils/datev/datev_csv.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py index df95a5b95a..aae734f8e2 100644 --- a/erpnext/regional/germany/utils/datev/datev_csv.py +++ b/erpnext/regional/germany/utils/datev/datev_csv.py @@ -3,18 +3,19 @@ from __future__ import unicode_literals import datetime import zipfile +from csv import QUOTE_NONNUMERIC +from six import BytesIO + import six import frappe import pandas as pd from frappe import _ -from csv import QUOTE_NONNUMERIC -from six import BytesIO from .datev_constants import DataCategory -from .datev_constants import Transactions def get_datev_csv(data, filters, csv_class): - """Fill in missing columns and return a CSV in DATEV Format. + """ + Fill in missing columns and return a CSV in DATEV Format. For automatic processing, DATEV requires the first line of the CSV file to hold meta data such as the length of account numbers oder the category of @@ -89,7 +90,7 @@ def get_header(filters, csv_class): # Format version (regarding format name) csv_class.FORMAT_VERSION, # Generated on - datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000', + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '000', # Imported on -- stays empty '', # Origin. Any two symbols, will be replaced by "SV" on import. @@ -155,7 +156,8 @@ def get_header(filters, csv_class): def download_csv_files_as_zip(csv_data_list): - """Put CSV files in a zip archive and send that to the client. + """ + Put CSV files in a zip archive and send that to the client. Params: csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}]