diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index bd70639ef2..7ceaf50134 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 @@ -240,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) @@ -253,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(';'), @@ -274,66 +270,91 @@ 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), included in `data` here. + # 3rd - nth Row: Data (Nutzdaten) return header + b'\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 - "EXTF", - # B = version of the DATEV format + # 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 - "510", + '700', csv_class.DATA_CATEGORY, - csv_class.FORMAT_NAME, - # E = Format version (regarding format name) - "", - # F = Generated on - datetime.datetime.now().strftime("%Y%m%d"), - # G = Imported on -- stays empty - "", - # H = Origin (SV = other (?), RE = KARE) - "SV", + '"%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"), + # 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") 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"), # N = Length of account numbers (Sachkontenlänge) - "4", + '4', # 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 diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/report/datev/datev_constants.py index 1c9bd23ee1..a4cd5fc10e 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" ] @@ -439,8 +472,8 @@ QUERY_REPORT_COLUMNS = [ "fieldtype": "Data", }, { - "label": "Kontonummer", - "fieldname": "Kontonummer", + "label": "Konto", + "fieldname": "Konto", "fieldtype": "Data", }, { @@ -453,6 +486,11 @@ QUERY_REPORT_COLUMNS = [ "fieldname": "Belegdatum", "fieldtype": "Date", }, + { + "label": "Belegfeld 1", + "fieldname": "Belegfeld 1", + "fieldtype": "Data", + }, { "label": "Buchungstext", "fieldname": "Buchungstext", @@ -460,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", } @@ -499,14 +527,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