From 4fb897728bbb3e19e277512b5fbe3f2f5c3b75e4 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 17:02:01 +0100 Subject: [PATCH 01/10] EXTF must be in quotes --- erpnext/regional/report/datev/datev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index bd70639ef2..0b0bb76f25 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -282,7 +282,7 @@ def get_header(filters, csv_class): # A = DATEV format # DTVF = created by DATEV software, # EXTF = created by other software - "EXTF", + '"EXTF"', # B = version of the DATEV format # 141 = 1.41, # 510 = 5.10, From 7d2777870ead35aba2e16d0f02183d4fe191f538 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 17:05:37 +0100 Subject: [PATCH 02/10] bump version number --- erpnext/regional/report/datev/datev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 0b0bb76f25..abbc56b5f5 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -287,7 +287,7 @@ def get_header(filters, csv_class): # 141 = 1.41, # 510 = 5.10, # 720 = 7.20 - "510", + "700", csv_class.DATA_CATEGORY, csv_class.FORMAT_NAME, # E = Format version (regarding format name) From 0070805bbbe939c6216d30d4eebbe3f0ed893fb4 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 17:06:06 +0100 Subject: [PATCH 03/10] add format version (data type) --- erpnext/regional/report/datev/datev.py | 2 +- erpnext/regional/report/datev/datev_constants.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index abbc56b5f5..a3cc180004 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -291,7 +291,7 @@ def get_header(filters, csv_class): csv_class.DATA_CATEGORY, csv_class.FORMAT_NAME, # E = Format version (regarding format name) - "", + csv_class.FORMAT_VERSION, # F = Generated on datetime.datetime.now().strftime("%Y%m%d"), # G = Imported on -- stays empty diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/report/datev/datev_constants.py index 1c9bd23ee1..1ca480c376 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/report/datev/datev_constants.py @@ -499,14 +499,17 @@ class FormatName(): class Transactions(): DATA_CATEGORY = DataCategory.TRANSACTIONS FORMAT_NAME = FormatName.TRANSACTIONS + FORMAT_VERSION = "9" COLUMNS = TRANSACTION_COLUMNS class DebtorsCreditors(): DATA_CATEGORY = DataCategory.DEBTORS_CREDITORS FORMAT_NAME = FormatName.DEBTORS_CREDITORS + FORMAT_VERSION = "5" COLUMNS = DEBTOR_CREDITOR_COLUMNS class AccountNames(): DATA_CATEGORY = DataCategory.ACCOUNT_NAMES FORMAT_NAME = FormatName.ACCOUNT_NAMES + FORMAT_VERSION = "2" COLUMNS = ACCOUNT_NAME_COLUMNS From 46cf20825b07af61f4f20704e0b7820d2b6fb64e Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 17:09:22 +0100 Subject: [PATCH 04/10] generated on is datetime, not date --- erpnext/regional/report/datev/datev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index a3cc180004..fe2add9bf4 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -293,7 +293,7 @@ def get_header(filters, csv_class): # E = Format version (regarding format name) csv_class.FORMAT_VERSION, # F = Generated on - datetime.datetime.now().strftime("%Y%m%d"), + datetime.datetime.now().strftime("%Y%m%d%H%M%S"), # G = Imported on -- stays empty "", # H = Origin (SV = other (?), RE = KARE) From 8818850174dc45a83bae1a18010b492e22b3b753 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 17:13:15 +0100 Subject: [PATCH 05/10] consutant number and client number are mandatory --- erpnext/regional/report/datev/datev.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index fe2add9bf4..bf6ac20e7e 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -303,10 +303,10 @@ def get_header(filters, csv_class): # J = Imported by -- stays empty "", # K = Tax consultant number (Beraternummer) - frappe.get_value("DATEV Settings", filters.get("company"), "consultant_number") or "", + frappe.get_value("DATEV Settings", filters.get("company"), "consultant_number"), "", # L = Tax client number (Mandantennummer) - frappe.get_value("DATEV Settings", filters.get("company"), "client_number") or "", + frappe.get_value("DATEV Settings", filters.get("company"), "client_number"), "", # M = Start of the fiscal year (Wirtschaftsjahresbeginn) frappe.utils.formatdate(frappe.defaults.get_user_default("year_start_date"), "yyyyMMdd"), From b555ed0cba85b10d3713d0e5d2044510133b9d73 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 20:05:27 +0100 Subject: [PATCH 06/10] update header accoding to "DATEV Format v7.0" --- erpnext/regional/report/datev/datev.py | 73 +++++++++++++++++--------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index bf6ac20e7e..05d817718b 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -274,66 +274,89 @@ def get_datev_csv(data, filters, csv_class): if not six.PY2: data = data.encode('latin_1') - return header + b'\r\n' + data + # 1st Row: Header with meta data + # 2nd Row: Data heading (Überschrift der Nutzdaten) + # 3rd Row: – n: Data (Nutzdaten) + return header + b'\r\n\r\n' + data def get_header(filters, csv_class): + coa = frappe.get_value("Company", filters.get("company"), "chart_of_accounts") + coa_used = "SKR04" if "SKR04" in coa else ("SKR03" if "SKR03" in coa else "") + header = [ - # A = DATEV format - # DTVF = created by DATEV software, - # EXTF = created by other software + # DATEV format + # "DTVF" = created by DATEV software, + # "EXTF" = created by other software '"EXTF"', - # B = version of the DATEV format + # version of the DATEV format # 141 = 1.41, # 510 = 5.10, # 720 = 7.20 - "700", + '700', csv_class.DATA_CATEGORY, csv_class.FORMAT_NAME, - # E = Format version (regarding format name) + # Format version (regarding format name) csv_class.FORMAT_VERSION, - # F = Generated on + # Generated on datetime.datetime.now().strftime("%Y%m%d%H%M%S"), - # G = Imported on -- stays empty - "", - # H = Origin (SV = other (?), RE = KARE) - "SV", + # Imported on -- stays empty + '', + # Origin. Any two symbols, will be replaced by "SV" on import. + '"EN"', # I = Exported by - frappe.session.user, + '"%s"' % frappe.session.user, # J = Imported by -- stays empty - "", + '', # K = Tax consultant number (Beraternummer) frappe.get_value("DATEV Settings", filters.get("company"), "consultant_number"), - "", # L = Tax client number (Mandantennummer) frappe.get_value("DATEV Settings", filters.get("company"), "client_number"), - "", # 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) - "4", + # minimum of 4, 5 if debtors/creditors are included + '5', # O = Transaction batch start date (YYYYMMDD) frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd"), # P = Transaction batch end date (YYYYMMDD) frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd"), # Q = Description (for example, "January - February 2019 Transactions") - "{} - {} {}".format( + '"{} - {} {}"'.format( frappe.utils.formatdate(filters.get('from_date'), "MMMM yyyy"), frappe.utils.formatdate(filters.get('to_date'), "MMMM yyyy"), csv_class.FORMAT_NAME ), # R = Diktatkürzel - "", + '', # S = Buchungstyp - # 1 = Transaction batch (Buchungsstapel), + # 1 = Transaction batch (Finanzbuchführung), # 2 = Annual financial statement (Jahresabschluss) - "1" if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else "", + '1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # T = Rechnungslegungszweck - "", + '', # U = Festschreibung - "", - # V = Kontoführungs-Währungskennzeichen des Geldkontos - frappe.get_value("Company", filters.get("company"), "default_currency") + '', + # V = Default currency, for example, "EUR" + '"%s"' % frappe.get_value("Company", filters.get("company"), "default_currency"), + # reserviert + '', + # Derivatskennzeichen + '', + # reserviert + '', + # reserviert + '', + # SKR + '"%s"' % coa_used, + # Branchen-Lösungs-ID + '', + # reserviert + '', + # reserviert + '', + # Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung) + '' ] return header From 96b66dfae6e4bcca6de5efbac07da54d2fe62a75 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 20:37:37 +0100 Subject: [PATCH 07/10] update TRANSACTION_COLUMNS according to "DATEV Format v7.0" --- erpnext/regional/report/datev/datev.py | 3 +- .../regional/report/datev/datev_constants.py | 93 +++++++++++++------ 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 05d817718b..ddc973ddae 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -315,8 +315,7 @@ def get_header(filters, csv_class): # 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) - # minimum of 4, 5 if debtors/creditors are included - '5', + '4', # O = Transaction batch start date (YYYYMMDD) frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd"), # P = Transaction batch end date (YYYYMMDD) diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/report/datev/datev_constants.py index 1ca480c376..501f2cea17 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/report/datev/datev_constants.py @@ -13,24 +13,27 @@ TRANSACTION_COLUMNS = [ "Basis-Umsatz", "WKZ Basis-Umsatz", # Konto/Gegenkonto - "Kontonummer", + "Konto", "Gegenkonto (ohne BU-Schlüssel)", "BU-Schlüssel", # Datum "Belegdatum", - # Belegfelder + # Rechnungs- / Belegnummer "Belegfeld 1", + # z.B. Fälligkeitsdatum Format: TTMMJJ "Belegfeld 2", - # Weitere Felder + # Skonto-Betrag / -Abzug (Der Wert 0 ist unzulässig) "Skonto", + # Beschreibung des Buchungssatzes "Buchungstext", - # OPOS-Informationen + # Mahn- / Zahl-Sperre (1 = Postensperre) "Postensperre", "Diverse Adressnummer", "Geschäftspartnerbank", "Sachverhalt", + # Keine Mahnzinsen "Zinssperre", - # Digitaler Beleg + # Link auf den Buchungsbeleg (Programmkürzel + GUID) "Beleglink", # Beleginfo "Beleginfo - Art 1", @@ -49,22 +52,30 @@ TRANSACTION_COLUMNS = [ "Beleginfo - Inhalt 7", "Beleginfo - Art 8", "Beleginfo - Inhalt 8", - # Kostenrechnung - "Kost 1 - Kostenstelle", - "Kost 2 - Kostenstelle", - "Kost-Menge", - # Steuerrechnung - "EU-Land u. UStID", + # Zuordnung des Geschäftsvorfalls für die Kostenrechnung + "KOST1 - Kostenstelle", + "KOST2 - Kostenstelle", + "KOST-Menge", + # USt-ID-Nummer (Beispiel: DE133546770) + "EU-Mitgliedstaat u. USt-IdNr.", + # Der im EU-Bestimmungsland gültige Steuersatz "EU-Steuersatz", + # I = Ist-Versteuerung, + # K = keine Umsatzsteuerrechnung + # P = Pauschalierung (z. B. für Land- und Forstwirtschaft), + # S = Soll-Versteuerung "Abw. Versteuerungsart", - # L+L Sachverhalt + # Sachverhalte gem. § 13b Abs. 1 Satz 1 Nrn. 1.-5. UStG "Sachverhalt L+L", + # Steuersatz / Funktion zum L+L-Sachverhalt (Beispiel: Wert 190 für 19%) "Funktionsergänzung L+L", - # Funktion Steuerschlüssel 49 + # Bei Verwendung des BU-Schlüssels 49 für „andere Steuersätze“ muss der + # steuerliche Sachverhalt mitgegeben werden "BU 49 Hauptfunktionstyp", "BU 49 Hauptfunktionsnummer", "BU 49 Funktionsergänzung", - # Zusatzinformationen + # Zusatzinformationen, besitzen den Charakter eines Notizzettels und können + # frei erfasst werden. "Zusatzinformation - Art 1", "Zusatzinformation - Inhalt 1", "Zusatzinformation - Art 2", @@ -105,54 +116,76 @@ TRANSACTION_COLUMNS = [ "Zusatzinformation - Inhalt 19", "Zusatzinformation - Art 20", "Zusatzinformation - Inhalt 20", - # Mengenfelder LuF + # Wirkt sich nur bei Sachverhalt mit SKR 14 Land- und Forstwirtschaft aus, + # für andere SKR werden die Felder beim Import / Export überlesen bzw. + # leer exportiert. "Stück", "Gewicht", - # Forderungsart + # 1 = Lastschrift + # 2 = Mahnung + # 3 = Zahlung "Zahlweise", "Forderungsart", + # JJJJ "Veranlagungsjahr", + # TTMMJJJJ "Zugeordnete Fälligkeit", - # Weitere Felder + # 1 = Einkauf von Waren + # 2 = Erwerb von Roh-Hilfs- und Betriebsstoffen "Skontotyp", - # Anzahlungen + # Allgemeine Bezeichnung, des Auftrags / Projekts. "Auftragsnummer", + # AA = Angeforderte Anzahlung / Abschlagsrechnung + # AG = Erhaltene Anzahlung (Geldeingang) + # AV = Erhaltene Anzahlung (Verbindlichkeit) + # SR = Schlussrechnung + # SU = Schlussrechnung (Umbuchung) + # SG = Schlussrechnung (Geldeingang) + # SO = Sonstige "Buchungstyp", "USt-Schlüssel (Anzahlungen)", - "EU-Land (Anzahlungen)", + "EU-Mitgliedstaat (Anzahlungen)", "Sachverhalt L+L (Anzahlungen)", "EU-Steuersatz (Anzahlungen)", "Erlöskonto (Anzahlungen)", - # Stapelinformationen + # Wird beim Import durch SV (Stapelverarbeitung) ersetzt. "Herkunft-Kz", - # Technische Identifikation - "Buchungs GUID", - # Kostenrechnung - "Kost-Datum", - # OPOS-Informationen + # Wird von DATEV verwendet. + "Leerfeld", + # Format TTMMJJJJ + "KOST-Datum", + # Vom Zahlungsempfänger individuell vergebenes Kennzeichen eines Mandats + # (z.B. Rechnungs- oder Kundennummer). "SEPA-Mandatsreferenz", + # 1 = Skontosperre + # 0 = Keine Skontosperre "Skontosperre", # Gesellschafter und Sonderbilanzsachverhalt "Gesellschaftername", + # Amtliche Nummer aus der Feststellungserklärung "Beteiligtennummer", "Identifikationsnummer", "Zeichnernummer", - # OPOS-Informationen + # Format TTMMJJJJ "Postensperre bis", # Gesellschafter und Sonderbilanzsachverhalt "Bezeichnung SoBil-Sachverhalt", "Kennzeichen SoBil-Buchung", - # Stapelinformationen + # 0 = keine Festschreibung + # 1 = Festschreibung "Festschreibung", - # Datum + # Format TTMMJJJJ "Leistungsdatum", + # Format TTMMJJJJ "Datum Zuord. Steuerperiode", - # OPOS-Informationen + # OPOS-Informationen, Format TTMMJJJJ "Fälligkeit", - # Konto/Gegenkonto + # G oder 1 = Generalumkehr + # 0 = keine Generalumkehr "Generalumkehr (GU)", # Steuersatz für Steuerschlüssel "Steuersatz", + # Beispiel: DE für Deutschland "Land" ] From 3bacdf1f4b3436697d90c11a71914cfcf7122d71 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 20:40:15 +0100 Subject: [PATCH 08/10] quote format name --- erpnext/regional/report/datev/datev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index ddc973ddae..a2ff442af4 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -295,7 +295,7 @@ def get_header(filters, csv_class): # 720 = 7.20 '700', csv_class.DATA_CATEGORY, - csv_class.FORMAT_NAME, + '"%s"' % csv_class.FORMAT_NAME, # Format version (regarding format name) csv_class.FORMAT_VERSION, # Generated on From 54717fa993a2490cba50672d9fdaadb0962de3df Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 20:58:44 +0100 Subject: [PATCH 09/10] update cloumn names --- erpnext/regional/report/datev/datev.py | 9 ++++---- .../regional/report/datev/datev_constants.py | 21 +++++++------------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index a2ff442af4..d4f480196c 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -72,17 +72,16 @@ def get_transactions(filters, as_dict=1): case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen', /* account number or, if empty, party account number */ - coalesce(acc.account_number, acc_pa.account_number) as 'Kontonummer', + coalesce(acc.account_number, acc_pa.account_number) as 'Konto', /* against number or, if empty, party against number */ coalesce(acc_against.account_number, acc_against_pa.account_number) as 'Gegenkonto (ohne BU-Schlüssel)', gl.posting_date as 'Belegdatum', + gl.voucher_no as 'Belegfeld 1', gl.remarks as 'Buchungstext', - gl.voucher_type as 'Beleginfo - Art 1', - gl.voucher_no as 'Beleginfo - Inhalt 1', - gl.against_voucher_type as 'Beleginfo - Art 2', - gl.against_voucher as 'Beleginfo - Inhalt 2' + gl.against_voucher_type as 'Beleginfo - Art 1', + gl.against_voucher as 'Beleginfo - Inhalt 1' FROM `tabGL Entry` gl diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/report/datev/datev_constants.py index 501f2cea17..a4cd5fc10e 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/report/datev/datev_constants.py @@ -472,8 +472,8 @@ QUERY_REPORT_COLUMNS = [ "fieldtype": "Data", }, { - "label": "Kontonummer", - "fieldname": "Kontonummer", + "label": "Konto", + "fieldname": "Konto", "fieldtype": "Data", }, { @@ -486,6 +486,11 @@ QUERY_REPORT_COLUMNS = [ "fieldname": "Belegdatum", "fieldtype": "Date", }, + { + "label": "Belegfeld 1", + "fieldname": "Belegfeld 1", + "fieldtype": "Data", + }, { "label": "Buchungstext", "fieldname": "Buchungstext", @@ -493,21 +498,11 @@ QUERY_REPORT_COLUMNS = [ }, { "label": "Beleginfo - Art 1", - "fieldname": "Beleginfo - Art 1", - "fieldtype": "Data", - }, - { - "label": "Beleginfo - Inhalt 1", - "fieldname": "Beleginfo - Inhalt 1", - "fieldtype": "Data", - }, - { - "label": "Beleginfo - Art 2", "fieldname": "Beleginfo - Art 2", "fieldtype": "Data", }, { - "label": "Beleginfo - Inhalt 2", + "label": "Beleginfo - Inhalt 1", "fieldname": "Beleginfo - Inhalt 2", "fieldtype": "Data", } From 772394b95aef049852361ab1b62712c40a1a576a Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Thu, 13 Feb 2020 20:58:59 +0100 Subject: [PATCH 10/10] fix header --- erpnext/regional/report/datev/datev.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index d4f480196c..7ceaf50134 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -239,8 +239,6 @@ def get_datev_csv(data, filters, csv_class): filters -- dict csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS """ - header = get_header(filters, csv_class) - empty_df = pd.DataFrame(columns=csv_class.COLUMNS) data_df = pd.DataFrame.from_records(data) @@ -252,7 +250,6 @@ def get_datev_csv(data, filters, csv_class): if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES: result['Sprach-ID'] = 'de-DE' - header = ';'.join(header).encode('latin_1') data = result.to_csv( # Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035 sep=str(';'), @@ -273,10 +270,13 @@ def get_datev_csv(data, filters, csv_class): 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) - # 3rd Row: – n: Data (Nutzdaten) - return header + b'\r\n\r\n' + 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):