From 536c6897f7d698ebf4a26338a95c67aef13b29b5 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 6 Apr 2022 01:29:54 +0200
Subject: [PATCH 1/3] feat: remove DATEV integration
---
erpnext/patches.txt | 2 -
.../germany_fill_debtor_creditor_number.py | 30 -
.../v13_0/germany_make_custom_fields.py | 20 -
erpnext/regional/germany/__init__.py | 0
erpnext/regional/germany/setup.py | 31 -
erpnext/regional/germany/utils/__init__.py | 0
.../regional/germany/utils/datev/__init__.py | 0
.../germany/utils/datev/datev_constants.py | 496 ---------------
.../regional/germany/utils/datev/datev_csv.py | 180 ------
erpnext/regional/report/datev/__init__.py | 0
erpnext/regional/report/datev/datev.js | 56 --
erpnext/regional/report/datev/datev.json | 22 -
erpnext/regional/report/datev/datev.py | 585 ------------------
erpnext/regional/report/datev/test_datev.py | 242 --------
14 files changed, 1664 deletions(-)
delete mode 100644 erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
delete mode 100644 erpnext/patches/v13_0/germany_make_custom_fields.py
delete mode 100644 erpnext/regional/germany/__init__.py
delete mode 100644 erpnext/regional/germany/setup.py
delete mode 100644 erpnext/regional/germany/utils/__init__.py
delete mode 100644 erpnext/regional/germany/utils/datev/__init__.py
delete mode 100644 erpnext/regional/germany/utils/datev/datev_constants.py
delete mode 100644 erpnext/regional/germany/utils/datev/datev_csv.py
delete mode 100644 erpnext/regional/report/datev/__init__.py
delete mode 100644 erpnext/regional/report/datev/datev.js
delete mode 100644 erpnext/regional/report/datev/datev.json
delete mode 100644 erpnext/regional/report/datev/datev.py
delete mode 100644 erpnext/regional/report/datev/test_datev.py
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index dc1d6929aaf4..191d5507e715 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -261,8 +261,6 @@ erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021 #17-01-2022
erpnext.patches.v13_0.update_shipment_status
erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
erpnext.patches.v12_0.add_ewaybill_validity_field
-erpnext.patches.v13_0.germany_make_custom_fields
-erpnext.patches.v13_0.germany_fill_debtor_creditor_number
erpnext.patches.v13_0.set_pos_closing_as_failed
erpnext.patches.v13_0.rename_stop_to_send_birthday_reminders
execute:frappe.rename_doc("Workspace", "Loan Management", "Loans", force=True)
diff --git a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
deleted file mode 100644
index 72cda751e6cb..000000000000
--- a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-
-def execute():
- """Move account number into the new custom field debtor_creditor_number.
-
- German companies used to use a dedicated payable/receivable account for
- every party to mimick party accounts in the external accounting software
- "DATEV". This is no longer necessary. The reference ID for DATEV will be
- stored in a new custom field "debtor_creditor_number".
- """
- company_list = frappe.get_all('Company', filters={'country': 'Germany'})
-
- for company in company_list:
- party_account_list = frappe.get_all('Party Account', filters={'company': company.name}, fields=['name', 'account', 'debtor_creditor_number'])
- for party_account in party_account_list:
- if (not party_account.account) or party_account.debtor_creditor_number:
- # account empty or debtor_creditor_number already filled
- continue
-
- account_number = frappe.db.get_value('Account', party_account.account, 'account_number')
- if not account_number:
- continue
-
- frappe.db.set_value('Party Account', party_account.name, 'debtor_creditor_number', account_number)
- frappe.db.set_value('Party Account', party_account.name, 'account', '')
diff --git a/erpnext/patches/v13_0/germany_make_custom_fields.py b/erpnext/patches/v13_0/germany_make_custom_fields.py
deleted file mode 100644
index 80b6a3954a67..000000000000
--- a/erpnext/patches/v13_0/germany_make_custom_fields.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-from erpnext.regional.germany.setup import make_custom_fields
-
-
-def execute():
- """Execute the make_custom_fields method for german companies.
-
- It is usually run once at setup of a new company. Since it's new, run it
- once for existing companies as well.
- """
- company_list = frappe.get_all('Company', filters = {'country': 'Germany'})
- if not company_list:
- return
-
- make_custom_fields()
diff --git a/erpnext/regional/germany/__init__.py b/erpnext/regional/germany/__init__.py
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py
deleted file mode 100644
index a68cecc0d632..000000000000
--- a/erpnext/regional/germany/setup.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-
-def setup(company=None, patch=True):
- make_custom_fields()
- add_custom_roles_for_reports()
-
-
-def make_custom_fields():
- custom_fields = {
- 'Party Account': [
- dict(fieldname='debtor_creditor_number', label='Debtor/Creditor Number',
- fieldtype='Data', insert_after='account', translatable=0)
- ]
- }
-
- create_custom_fields(custom_fields)
-
-
-def add_custom_roles_for_reports():
- """Add Access Control to UAE VAT 201."""
- if not frappe.db.get_value('Custom Role', dict(report='DATEV')):
- frappe.get_doc(dict(
- doctype='Custom Role',
- report='DATEV',
- roles= [
- dict(role='Accounts User'),
- dict(role='Accounts Manager')
- ]
- )).insert()
diff --git a/erpnext/regional/germany/utils/__init__.py b/erpnext/regional/germany/utils/__init__.py
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/erpnext/regional/germany/utils/datev/__init__.py b/erpnext/regional/germany/utils/datev/__init__.py
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/erpnext/regional/germany/utils/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py
deleted file mode 100644
index 8f2dc2dec391..000000000000
--- a/erpnext/regional/germany/utils/datev/datev_constants.py
+++ /dev/null
@@ -1,496 +0,0 @@
-"""Constants used in datev.py."""
-
-TRANSACTION_COLUMNS = [
- # All possible columns must tbe listed here, because DATEV requires them to
- # be present in the CSV.
- # ---
- # Umsatz
- "Umsatz (ohne Soll/Haben-Kz)",
- "Soll/Haben-Kennzeichen",
- "WKZ Umsatz",
- "Kurs",
- "Basis-Umsatz",
- "WKZ Basis-Umsatz",
- # Konto/Gegenkonto
- "Konto",
- "Gegenkonto (ohne BU-Schlüssel)",
- "BU-Schlüssel",
- # Datum
- "Belegdatum",
- # Rechnungs- / Belegnummer
- "Belegfeld 1",
- # z.B. Fälligkeitsdatum Format: TTMMJJ
- "Belegfeld 2",
- # Skonto-Betrag / -Abzug (Der Wert 0 ist unzulässig)
- "Skonto",
- # Beschreibung des Buchungssatzes
- "Buchungstext",
- # Mahn- / Zahl-Sperre (1 = Postensperre)
- "Postensperre",
- "Diverse Adressnummer",
- "Geschäftspartnerbank",
- "Sachverhalt",
- # Keine Mahnzinsen
- "Zinssperre",
- # Link auf den Buchungsbeleg (Programmkürzel + GUID)
- "Beleglink",
- # Beleginfo
- "Beleginfo - Art 1",
- "Beleginfo - Inhalt 1",
- "Beleginfo - Art 2",
- "Beleginfo - Inhalt 2",
- "Beleginfo - Art 3",
- "Beleginfo - Inhalt 3",
- "Beleginfo - Art 4",
- "Beleginfo - Inhalt 4",
- "Beleginfo - Art 5",
- "Beleginfo - Inhalt 5",
- "Beleginfo - Art 6",
- "Beleginfo - Inhalt 6",
- "Beleginfo - Art 7",
- "Beleginfo - Inhalt 7",
- "Beleginfo - Art 8",
- "Beleginfo - Inhalt 8",
- # 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",
- # 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",
- # 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, besitzen den Charakter eines Notizzettels und können
- # frei erfasst werden.
- "Zusatzinformation - Art 1",
- "Zusatzinformation - Inhalt 1",
- "Zusatzinformation - Art 2",
- "Zusatzinformation - Inhalt 2",
- "Zusatzinformation - Art 3",
- "Zusatzinformation - Inhalt 3",
- "Zusatzinformation - Art 4",
- "Zusatzinformation - Inhalt 4",
- "Zusatzinformation - Art 5",
- "Zusatzinformation - Inhalt 5",
- "Zusatzinformation - Art 6",
- "Zusatzinformation - Inhalt 6",
- "Zusatzinformation - Art 7",
- "Zusatzinformation - Inhalt 7",
- "Zusatzinformation - Art 8",
- "Zusatzinformation - Inhalt 8",
- "Zusatzinformation - Art 9",
- "Zusatzinformation - Inhalt 9",
- "Zusatzinformation - Art 10",
- "Zusatzinformation - Inhalt 10",
- "Zusatzinformation - Art 11",
- "Zusatzinformation - Inhalt 11",
- "Zusatzinformation - Art 12",
- "Zusatzinformation - Inhalt 12",
- "Zusatzinformation - Art 13",
- "Zusatzinformation - Inhalt 13",
- "Zusatzinformation - Art 14",
- "Zusatzinformation - Inhalt 14",
- "Zusatzinformation - Art 15",
- "Zusatzinformation - Inhalt 15",
- "Zusatzinformation - Art 16",
- "Zusatzinformation - Inhalt 16",
- "Zusatzinformation - Art 17",
- "Zusatzinformation - Inhalt 17",
- "Zusatzinformation - Art 18",
- "Zusatzinformation - Inhalt 18",
- "Zusatzinformation - Art 19",
- "Zusatzinformation - Inhalt 19",
- "Zusatzinformation - Art 20",
- "Zusatzinformation - Inhalt 20",
- # 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",
- # 1 = Lastschrift
- # 2 = Mahnung
- # 3 = Zahlung
- "Zahlweise",
- "Forderungsart",
- # JJJJ
- "Veranlagungsjahr",
- # TTMMJJJJ
- "Zugeordnete Fälligkeit",
- # 1 = Einkauf von Waren
- # 2 = Erwerb von Roh-Hilfs- und Betriebsstoffen
- "Skontotyp",
- # 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-Mitgliedstaat (Anzahlungen)",
- "Sachverhalt L+L (Anzahlungen)",
- "EU-Steuersatz (Anzahlungen)",
- "Erlöskonto (Anzahlungen)",
- # Wird beim Import durch SV (Stapelverarbeitung) ersetzt.
- "Herkunft-Kz",
- # 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",
- # Format TTMMJJJJ
- "Postensperre bis",
- # Gesellschafter und Sonderbilanzsachverhalt
- "Bezeichnung SoBil-Sachverhalt",
- "Kennzeichen SoBil-Buchung",
- # 0 = keine Festschreibung
- # 1 = Festschreibung
- "Festschreibung",
- # Format TTMMJJJJ
- "Leistungsdatum",
- # Format TTMMJJJJ
- "Datum Zuord. Steuerperiode",
- # OPOS-Informationen, Format TTMMJJJJ
- "Fälligkeit",
- # G oder 1 = Generalumkehr
- # 0 = keine Generalumkehr
- "Generalumkehr (GU)",
- # Steuersatz für Steuerschlüssel
- "Steuersatz",
- # Beispiel: DE für Deutschland
- "Land"
-]
-
-DEBTOR_CREDITOR_COLUMNS = [
- # All possible columns must tbe listed here, because DATEV requires them to
- # be present in the CSV.
- # Columns "Leerfeld" have been replaced with "Leerfeld #" to not confuse pandas
- # ---
- "Konto",
- "Name (Adressatentyp Unternehmen)",
- "Unternehmensgegenstand",
- "Name (Adressatentyp natürl. Person)",
- "Vorname (Adressatentyp natürl. Person)",
- "Name (Adressatentyp keine Angabe)",
- "Adressatentyp",
- "Kurzbezeichnung",
- "EU-Land",
- "EU-USt-IdNr.",
- "Anrede",
- "Titel/Akad. Grad",
- "Adelstitel",
- "Namensvorsatz",
- "Adressart",
- "Straße",
- "Postfach",
- "Postleitzahl",
- "Ort",
- "Land",
- "Versandzusatz",
- "Adresszusatz",
- "Abweichende Anrede",
- "Abw. Zustellbezeichnung 1",
- "Abw. Zustellbezeichnung 2",
- "Kennz. Korrespondenzadresse",
- "Adresse gültig von",
- "Adresse gültig bis",
- "Telefon",
- "Bemerkung (Telefon)",
- "Telefon Geschäftsleitung",
- "Bemerkung (Telefon GL)",
- "E-Mail",
- "Bemerkung (E-Mail)",
- "Internet",
- "Bemerkung (Internet)",
- "Fax",
- "Bemerkung (Fax)",
- "Sonstige",
- "Bemerkung (Sonstige)",
- "Bankleitzahl 1",
- "Bankbezeichnung 1",
- "Bankkonto-Nummer 1",
- "Länderkennzeichen 1",
- "IBAN 1",
- "Leerfeld 1",
- "SWIFT-Code 1",
- "Abw. Kontoinhaber 1",
- "Kennz. Haupt-Bankverb. 1",
- "Bankverb. 1 Gültig von",
- "Bankverb. 1 Gültig bis",
- "Bankleitzahl 2",
- "Bankbezeichnung 2",
- "Bankkonto-Nummer 2",
- "Länderkennzeichen 2",
- "IBAN 2",
- "Leerfeld 2",
- "SWIFT-Code 2",
- "Abw. Kontoinhaber 2",
- "Kennz. Haupt-Bankverb. 2",
- "Bankverb. 2 gültig von",
- "Bankverb. 2 gültig bis",
- "Bankleitzahl 3",
- "Bankbezeichnung 3",
- "Bankkonto-Nummer 3",
- "Länderkennzeichen 3",
- "IBAN 3",
- "Leerfeld 3",
- "SWIFT-Code 3",
- "Abw. Kontoinhaber 3",
- "Kennz. Haupt-Bankverb. 3",
- "Bankverb. 3 gültig von",
- "Bankverb. 3 gültig bis",
- "Bankleitzahl 4",
- "Bankbezeichnung 4",
- "Bankkonto-Nummer 4",
- "Länderkennzeichen 4",
- "IBAN 4",
- "Leerfeld 4",
- "SWIFT-Code 4",
- "Abw. Kontoinhaber 4",
- "Kennz. Haupt-Bankverb. 4",
- "Bankverb. 4 Gültig von",
- "Bankverb. 4 Gültig bis",
- "Bankleitzahl 5",
- "Bankbezeichnung 5",
- "Bankkonto-Nummer 5",
- "Länderkennzeichen 5",
- "IBAN 5",
- "Leerfeld 5",
- "SWIFT-Code 5",
- "Abw. Kontoinhaber 5",
- "Kennz. Haupt-Bankverb. 5",
- "Bankverb. 5 gültig von",
- "Bankverb. 5 gültig bis",
- "Leerfeld 6",
- "Briefanrede",
- "Grußformel",
- "Kundennummer",
- "Steuernummer",
- "Sprache",
- "Ansprechpartner",
- "Vertreter",
- "Sachbearbeiter",
- "Diverse-Konto",
- "Ausgabeziel",
- "Währungssteuerung",
- "Kreditlimit (Debitor)",
- "Zahlungsbedingung",
- "Fälligkeit in Tagen (Debitor)",
- "Skonto in Prozent (Debitor)",
- "Kreditoren-Ziel 1 (Tage)",
- "Kreditoren-Skonto 1 (%)",
- "Kreditoren-Ziel 2 (Tage)",
- "Kreditoren-Skonto 2 (%)",
- "Kreditoren-Ziel 3 Brutto (Tage)",
- "Kreditoren-Ziel 4 (Tage)",
- "Kreditoren-Skonto 4 (%)",
- "Kreditoren-Ziel 5 (Tage)",
- "Kreditoren-Skonto 5 (%)",
- "Mahnung",
- "Kontoauszug",
- "Mahntext 1",
- "Mahntext 2",
- "Mahntext 3",
- "Kontoauszugstext",
- "Mahnlimit Betrag",
- "Mahnlimit %",
- "Zinsberechnung",
- "Mahnzinssatz 1",
- "Mahnzinssatz 2",
- "Mahnzinssatz 3",
- "Lastschrift",
- "Verfahren",
- "Mandantenbank",
- "Zahlungsträger",
- "Indiv. Feld 1",
- "Indiv. Feld 2",
- "Indiv. Feld 3",
- "Indiv. Feld 4",
- "Indiv. Feld 5",
- "Indiv. Feld 6",
- "Indiv. Feld 7",
- "Indiv. Feld 8",
- "Indiv. Feld 9",
- "Indiv. Feld 10",
- "Indiv. Feld 11",
- "Indiv. Feld 12",
- "Indiv. Feld 13",
- "Indiv. Feld 14",
- "Indiv. Feld 15",
- "Abweichende Anrede (Rechnungsadresse)",
- "Adressart (Rechnungsadresse)",
- "Straße (Rechnungsadresse)",
- "Postfach (Rechnungsadresse)",
- "Postleitzahl (Rechnungsadresse)",
- "Ort (Rechnungsadresse)",
- "Land (Rechnungsadresse)",
- "Versandzusatz (Rechnungsadresse)",
- "Adresszusatz (Rechnungsadresse)",
- "Abw. Zustellbezeichnung 1 (Rechnungsadresse)",
- "Abw. Zustellbezeichnung 2 (Rechnungsadresse)",
- "Adresse Gültig von (Rechnungsadresse)",
- "Adresse Gültig bis (Rechnungsadresse)",
- "Bankleitzahl 6",
- "Bankbezeichnung 6",
- "Bankkonto-Nummer 6",
- "Länderkennzeichen 6",
- "IBAN 6",
- "Leerfeld 7",
- "SWIFT-Code 6",
- "Abw. Kontoinhaber 6",
- "Kennz. Haupt-Bankverb. 6",
- "Bankverb 6 gültig von",
- "Bankverb 6 gültig bis",
- "Bankleitzahl 7",
- "Bankbezeichnung 7",
- "Bankkonto-Nummer 7",
- "Länderkennzeichen 7",
- "IBAN 7",
- "Leerfeld 8",
- "SWIFT-Code 7",
- "Abw. Kontoinhaber 7",
- "Kennz. Haupt-Bankverb. 7",
- "Bankverb 7 gültig von",
- "Bankverb 7 gültig bis",
- "Bankleitzahl 8",
- "Bankbezeichnung 8",
- "Bankkonto-Nummer 8",
- "Länderkennzeichen 8",
- "IBAN 8",
- "Leerfeld 9",
- "SWIFT-Code 8",
- "Abw. Kontoinhaber 8",
- "Kennz. Haupt-Bankverb. 8",
- "Bankverb 8 gültig von",
- "Bankverb 8 gültig bis",
- "Bankleitzahl 9",
- "Bankbezeichnung 9",
- "Bankkonto-Nummer 9",
- "Länderkennzeichen 9",
- "IBAN 9",
- "Leerfeld 10",
- "SWIFT-Code 9",
- "Abw. Kontoinhaber 9",
- "Kennz. Haupt-Bankverb. 9",
- "Bankverb 9 gültig von",
- "Bankverb 9 gültig bis",
- "Bankleitzahl 10",
- "Bankbezeichnung 10",
- "Bankkonto-Nummer 10",
- "Länderkennzeichen 10",
- "IBAN 10",
- "Leerfeld 11",
- "SWIFT-Code 10",
- "Abw. Kontoinhaber 10",
- "Kennz. Haupt-Bankverb. 10",
- "Bankverb 10 gültig von",
- "Bankverb 10 gültig bis",
- "Nummer Fremdsystem",
- "Insolvent",
- "SEPA-Mandatsreferenz 1",
- "SEPA-Mandatsreferenz 2",
- "SEPA-Mandatsreferenz 3",
- "SEPA-Mandatsreferenz 4",
- "SEPA-Mandatsreferenz 5",
- "SEPA-Mandatsreferenz 6",
- "SEPA-Mandatsreferenz 7",
- "SEPA-Mandatsreferenz 8",
- "SEPA-Mandatsreferenz 9",
- "SEPA-Mandatsreferenz 10",
- "Verknüpftes OPOS-Konto",
- "Mahnsperre bis",
- "Lastschriftsperre bis",
- "Zahlungssperre bis",
- "Gebührenberechnung",
- "Mahngebühr 1",
- "Mahngebühr 2",
- "Mahngebühr 3",
- "Pauschalberechnung",
- "Verzugspauschale 1",
- "Verzugspauschale 2",
- "Verzugspauschale 3",
- "Alternativer Suchname",
- "Status",
- "Anschrift manuell geändert (Korrespondenzadresse)",
- "Anschrift individuell (Korrespondenzadresse)",
- "Anschrift manuell geändert (Rechnungsadresse)",
- "Anschrift individuell (Rechnungsadresse)",
- "Fristberechnung bei Debitor",
- "Mahnfrist 1",
- "Mahnfrist 2",
- "Mahnfrist 3",
- "Letzte Frist"
-]
-
-ACCOUNT_NAME_COLUMNS = [
- # Account number
- "Konto",
- # Account name
- "Kontenbeschriftung",
- # Language of the account name
- # "de-DE" or "en-GB"
- "Sprach-ID"
-]
-
-class DataCategory():
-
- """Field of the CSV Header."""
-
- DEBTORS_CREDITORS = "16"
- ACCOUNT_NAMES = "20"
- TRANSACTIONS = "21"
- POSTING_TEXT_CONSTANTS = "67"
-
-class FormatName():
-
- """Field of the CSV Header, corresponds to DataCategory."""
-
- DEBTORS_CREDITORS = "Debitoren/Kreditoren"
- ACCOUNT_NAMES = "Kontenbeschriftungen"
- TRANSACTIONS = "Buchungsstapel"
- POSTING_TEXT_CONSTANTS = "Buchungstextkonstanten"
-
-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
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
deleted file mode 100644
index ec271a10294e..000000000000
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ /dev/null
@@ -1,180 +0,0 @@
-import datetime
-import zipfile
-from csv import QUOTE_NONNUMERIC
-from io import BytesIO
-
-import frappe
-import pandas as pd
-from frappe import _
-
-from .datev_constants import DataCategory
-
-
-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'])
-
- result['Beleginfo - Inhalt 6'] = pd.to_datetime(result['Beleginfo - Inhalt 6'])
- result['Beleginfo - Inhalt 6'] = result['Beleginfo - Inhalt 6'].dt.strftime('%d%m%Y')
-
- result['Fälligkeit'] = pd.to_datetime(result['Fälligkeit'])
- result['Fälligkeit'] = result['Fälligkeit'].dt.strftime('%d%m%y')
-
- result.sort_values(by='Belegdatum', inplace=True, kind='stable', ignore_index=True)
-
- 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=';',
- # 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
- )
-
- data = data.encode('latin_1', errors='replace')
-
- header = get_header(filters, csv_class)
- header = ';'.join(header).encode('latin_1', errors='replace')
-
- # 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(filters.get('fiscal_year_start'), 'yyyyMMdd'),
- # N = Length of account numbers (Sachkontenlänge)
- str(filters.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 zip_and_download(zip_filename, csv_files):
- """
- Put CSV files in a zip archive and send that to the client.
-
- Params:
- zip_filename Name of the zip file
- csv_files list of dicts [{'file_name': 'my_file.csv', 'csv_data': 'comma,separated,values'}]
- """
- zip_buffer = BytesIO()
-
- zip_file = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
- for csv_file in csv_files:
- zip_file.writestr(csv_file.get('file_name'), csv_file.get('csv_data'))
-
- zip_file.close()
-
- frappe.response['filecontent'] = zip_buffer.getvalue()
- frappe.response['filename'] = zip_filename
- frappe.response['type'] = 'binary'
diff --git a/erpnext/regional/report/datev/__init__.py b/erpnext/regional/report/datev/__init__.py
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js
deleted file mode 100644
index 03c729e6df46..000000000000
--- a/erpnext/regional/report/datev/datev.js
+++ /dev/null
@@ -1,56 +0,0 @@
-frappe.query_reports["DATEV"] = {
- "filters": [
- {
- "fieldname": "company",
- "label": __("Company"),
- "fieldtype": "Link",
- "options": "Company",
- "default": frappe.defaults.get_user_default("Company") || frappe.defaults.get_global_default("Company"),
- "reqd": 1
- },
- {
- "fieldname": "from_date",
- "label": __("From Date"),
- "default": moment().subtract(1, 'month').startOf('month').format(),
- "fieldtype": "Date",
- "reqd": 1
- },
- {
- "fieldname": "to_date",
- "label": __("To Date"),
- "default": moment().subtract(1, 'month').endOf('month').format(),
- "fieldtype": "Date",
- "reqd": 1
- },
- {
- "fieldname": "voucher_type",
- "label": __("Voucher Type"),
- "fieldtype": "Select",
- "options": "\nSales Invoice\nPurchase Invoice\nPayment Entry\nExpense Claim\nPayroll Entry\nBank Reconciliation\nAsset\nStock Entry"
- }
- ],
- onload: function(query_report) {
- let company = frappe.query_report.get_filter_value('company');
- frappe.db.exists('DATEV Settings', company).then((settings_exist) => {
- if (!settings_exist) {
- frappe.confirm(__('DATEV Settings for your Company are missing. Would you like to create them now?'),
- () => frappe.new_doc('DATEV Settings', {'company': company})
- );
- }
- });
-
- query_report.page.add_menu_item(__("Download DATEV File"), () => {
- const filters = encodeURIComponent(
- JSON.stringify(
- query_report.get_values()
- )
- );
- window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`);
- });
-
- query_report.page.add_menu_item(__("Change DATEV Settings"), () => {
- let company = frappe.query_report.get_filter_value('company'); // read company from filters again – it might have changed by now.
- frappe.set_route('Form', 'DATEV Settings', company);
- });
- }
-};
diff --git a/erpnext/regional/report/datev/datev.json b/erpnext/regional/report/datev/datev.json
deleted file mode 100644
index 94e3960eadec..000000000000
--- a/erpnext/regional/report/datev/datev.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "add_total_row": 0,
- "columns": [],
- "creation": "2019-04-24 08:45:16.650129",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "filters": [],
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2021-04-06 12:23:00.379517",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "DATEV",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "GL Entry",
- "report_name": "DATEV",
- "report_type": "Script Report",
- "roles": []
-}
\ No newline at end of file
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
deleted file mode 100644
index 92a10c288f8c..000000000000
--- a/erpnext/regional/report/datev/datev.py
+++ /dev/null
@@ -1,585 +0,0 @@
-"""
-Provide a report and downloadable CSV according to the German DATEV format.
-
-- Query report showing only the columns that contain data, formatted nicely for
- dispay to the user.
-- CSV download functionality `download_datev_csv` that provides a CSV file with
- all required columns. Used to import the data into the DATEV Software.
-"""
-
-import json
-
-import frappe
-from frappe import _
-
-from erpnext.accounts.utils import get_fiscal_year
-from erpnext.regional.germany.utils.datev.datev_constants import (
- AccountNames,
- DebtorsCreditors,
- Transactions,
-)
-from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, zip_and_download
-
-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": "BU-Schlüssel",
- "fieldname": "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
- },
- {
- "label": "Beleginfo - Art 3",
- "fieldname": "Beleginfo - Art 3",
- "fieldtype": "Link",
- "options": "DocType",
- "width": 100
- },
- {
- "label": "Beleginfo - Inhalt 3",
- "fieldname": "Beleginfo - Inhalt 3",
- "fieldtype": "Dynamic Link",
- "options": "Beleginfo - Art 3",
- "width": 150
- },
- {
- "label": "Beleginfo - Art 4",
- "fieldname": "Beleginfo - Art 4",
- "fieldtype": "Data",
- "width": 100
- },
- {
- "label": "Beleginfo - Inhalt 4",
- "fieldname": "Beleginfo - Inhalt 4",
- "fieldtype": "Data",
- "width": 150
- },
- {
- "label": "Beleginfo - Art 5",
- "fieldname": "Beleginfo - Art 5",
- "fieldtype": "Data",
- "width": 150
- },
- {
- "label": "Beleginfo - Inhalt 5",
- "fieldname": "Beleginfo - Inhalt 5",
- "fieldtype": "Data",
- "width": 100
- },
- {
- "label": "Beleginfo - Art 6",
- "fieldname": "Beleginfo - Art 6",
- "fieldtype": "Data",
- "width": 150
- },
- {
- "label": "Beleginfo - Inhalt 6",
- "fieldname": "Beleginfo - Inhalt 6",
- "fieldtype": "Date",
- "width": 100
- },
- {
- "label": "Fälligkeit",
- "fieldname": "Fälligkeit",
- "fieldtype": "Date",
- "width": 100
- }
-]
-
-
-def execute(filters=None):
- """Entry point for frappe."""
- data = []
- if filters and validate(filters):
- fn = 'temporary_against_account_number'
- filters[fn] = frappe.get_value('DATEV Settings', filters.get('company'), fn)
- data = get_transactions(filters, as_dict=0)
-
- return COLUMNS, data
-
-
-def validate(filters):
- """Make sure all mandatory filters and settings are present."""
- company = filters.get('company')
- if not company:
- frappe.throw(_('Company is a mandatory filter.'))
-
- from_date = filters.get('from_date')
- if not from_date:
- frappe.throw(_('From Date is a mandatory filter.'))
-
- to_date = filters.get('to_date')
- if not to_date:
- frappe.throw(_('To Date is a mandatory filter.'))
-
- validate_fiscal_year(from_date, to_date, company)
-
- if not frappe.db.exists('DATEV Settings', filters.get('company')):
- msg = 'Please create DATEV Settings for Company {}'.format(filters.get('company'))
- frappe.log_error(msg, title='DATEV Settings missing')
- return False
-
- return True
-
-
-def validate_fiscal_year(from_date, to_date, company):
- from_fiscal_year = get_fiscal_year(date=from_date, company=company)
- to_fiscal_year = get_fiscal_year(date=to_date, company=company)
- if from_fiscal_year != to_fiscal_year:
- frappe.throw(_('Dates {} and {} are not in the same fiscal year.').format(from_date, to_date))
-
-
-def get_transactions(filters, as_dict=1):
- def run(params_method, filters):
- extra_fields, extra_joins, extra_filters = params_method(filters)
- return run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=as_dict)
-
- def sort_by(row):
- # "Belegdatum" is in the fifth column when list format is used
- return row["Belegdatum" if as_dict else 5]
-
- type_map = {
- # specific query methods for some voucher types
- "Payment Entry": get_payment_entry_params,
- "Sales Invoice": get_sales_invoice_params,
- "Purchase Invoice": get_purchase_invoice_params
- }
-
- only_voucher_type = filters.get("voucher_type")
- transactions = []
-
- for voucher_type, get_voucher_params in type_map.items():
- if only_voucher_type and only_voucher_type != voucher_type:
- continue
-
- transactions.extend(run(params_method=get_voucher_params, filters=filters))
-
- if not only_voucher_type or only_voucher_type not in type_map:
- # generic query method for all other voucher types
- filters["exclude_voucher_types"] = type_map.keys()
- transactions.extend(run(params_method=get_generic_params, filters=filters))
-
- return sorted(transactions, key=sort_by)
-
-
-def get_payment_entry_params(filters):
- extra_fields = """
- , 'Zahlungsreferenz' as 'Beleginfo - Art 5'
- , pe.reference_no as 'Beleginfo - Inhalt 5'
- , 'Buchungstag' as 'Beleginfo - Art 6'
- , pe.reference_date as 'Beleginfo - Inhalt 6'
- , '' as 'Fälligkeit'
- """
-
- extra_joins = """
- LEFT JOIN `tabPayment Entry` pe
- ON gl.voucher_no = pe.name
- """
-
- extra_filters = """
- AND gl.voucher_type = 'Payment Entry'
- """
-
- return extra_fields, extra_joins, extra_filters
-
-
-def get_sales_invoice_params(filters):
- extra_fields = """
- , '' as 'Beleginfo - Art 5'
- , '' as 'Beleginfo - Inhalt 5'
- , '' as 'Beleginfo - Art 6'
- , '' as 'Beleginfo - Inhalt 6'
- , si.due_date as 'Fälligkeit'
- """
-
- extra_joins = """
- LEFT JOIN `tabSales Invoice` si
- ON gl.voucher_no = si.name
- """
-
- extra_filters = """
- AND gl.voucher_type = 'Sales Invoice'
- """
-
- return extra_fields, extra_joins, extra_filters
-
-
-def get_purchase_invoice_params(filters):
- extra_fields = """
- , 'Lieferanten-Rechnungsnummer' as 'Beleginfo - Art 5'
- , pi.bill_no as 'Beleginfo - Inhalt 5'
- , 'Lieferanten-Rechnungsdatum' as 'Beleginfo - Art 6'
- , pi.bill_date as 'Beleginfo - Inhalt 6'
- , pi.due_date as 'Fälligkeit'
- """
-
- extra_joins = """
- LEFT JOIN `tabPurchase Invoice` pi
- ON gl.voucher_no = pi.name
- """
-
- extra_filters = """
- AND gl.voucher_type = 'Purchase Invoice'
- """
-
- return extra_fields, extra_joins, extra_filters
-
-
-def get_generic_params(filters):
- # produce empty fields so all rows will have the same length
- extra_fields = """
- , '' as 'Beleginfo - Art 5'
- , '' as 'Beleginfo - Inhalt 5'
- , '' as 'Beleginfo - Art 6'
- , '' as 'Beleginfo - Inhalt 6'
- , '' as 'Fälligkeit'
- """
- extra_joins = ""
-
- if filters.get("exclude_voucher_types"):
- # exclude voucher types that are queried by a dedicated method
- exclude = "({})".format(', '.join("'{}'".format(key) for key in filters.get("exclude_voucher_types")))
- extra_filters = "AND gl.voucher_type NOT IN {}".format(exclude)
-
- # if voucher type filter is set, allow only this type
- if filters.get("voucher_type"):
- extra_filters += " AND gl.voucher_type = %(voucher_type)s"
-
- return extra_fields, extra_joins, extra_filters
-
-
-def run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=1):
- """
- Get a list of accounting entries.
-
- Select GL Entries joined with Account and Party Account in order to get the
- account numbers. Returns a list of accounting entries.
-
- Arguments:
- filters -- dict of filters to be passed to the sql query
- as_dict -- return as list of dicts [0,1]
- """
- query = """
- SELECT
-
- /* either debit or credit amount; always positive */
- case gl.debit when 0 then gl.credit else gl.debit end as 'Umsatz (ohne Soll/Haben-Kz)',
-
- /* 'H' when credit, 'S' when debit */
- case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
-
- /* account number or, if empty, party account number */
- acc.account_number as 'Konto',
-
- /* against number or, if empty, party against number */
- %(temporary_against_account_number)s as 'Gegenkonto (ohne BU-Schlüssel)',
-
- '' as 'BU-Schlüssel',
-
- gl.posting_date as 'Belegdatum',
- gl.voucher_no as 'Belegfeld 1',
- REPLACE(LEFT(gl.remarks, 60), '\n', ' ') 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.party_type as 'Beleginfo - Art 3',
- gl.party as 'Beleginfo - Inhalt 3',
- case gl.party_type when 'Customer' then 'Debitorennummer' when 'Supplier' then 'Kreditorennummer' else NULL end as 'Beleginfo - Art 4',
- par.debtor_creditor_number as 'Beleginfo - Inhalt 4'
-
- {extra_fields}
-
- FROM `tabGL Entry` gl
-
- /* Kontonummer */
- LEFT JOIN `tabAccount` acc
- ON gl.account = acc.name
-
- LEFT JOIN `tabParty Account` par
- ON par.parent = gl.party
- AND par.parenttype = gl.party_type
- AND par.company = %(company)s
-
- {extra_joins}
-
- WHERE gl.company = %(company)s
- AND DATE(gl.posting_date) >= %(from_date)s
- AND DATE(gl.posting_date) <= %(to_date)s
-
- {extra_filters}
-
- ORDER BY 'Belegdatum', gl.voucher_no""".format(
- extra_fields=extra_fields,
- extra_joins=extra_joins,
- extra_filters=extra_filters
- )
-
- gl_entries = frappe.db.sql(query, filters, as_dict=as_dict)
-
- return gl_entries
-
-
-def get_customers(filters):
- """
- Get a list of Customers.
-
- Arguments:
- filters -- dict of filters to be passed to the sql query
- """
- return frappe.db.sql("""
- SELECT
-
- par.debtor_creditor_number as 'Konto',
- CASE cus.customer_type
- WHEN 'Company' THEN cus.customer_name
- ELSE null
- END as 'Name (Adressatentyp Unternehmen)',
- CASE cus.customer_type
- WHEN 'Individual' THEN TRIM(SUBSTR(cus.customer_name, LOCATE(' ', cus.customer_name)))
- ELSE null
- END as 'Name (Adressatentyp natürl. Person)',
- CASE cus.customer_type
- WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(cus.customer_name, ' ', 1), ' ', -1)
- ELSE null
- END as 'Vorname (Adressatentyp natürl. Person)',
- CASE cus.customer_type
- WHEN 'Individual' THEN '1'
- WHEN 'Company' THEN '2'
- ELSE '0'
- END as 'Adressatentyp',
- adr.address_line1 as 'Straße',
- adr.pincode as 'Postleitzahl',
- adr.city as 'Ort',
- UPPER(country.code) as 'Land',
- adr.address_line2 as 'Adresszusatz',
- adr.email_id as 'E-Mail',
- adr.phone as 'Telefon',
- adr.fax as 'Fax',
- cus.website as 'Internet',
- cus.tax_id as 'Steuernummer'
-
- FROM `tabCustomer` cus
-
- left join `tabParty Account` par
- on par.parent = cus.name
- and par.parenttype = 'Customer'
- and par.company = %(company)s
-
- left join `tabDynamic Link` dyn_adr
- on dyn_adr.link_name = cus.name
- and dyn_adr.link_doctype = 'Customer'
- and dyn_adr.parenttype = 'Address'
-
- left join `tabAddress` adr
- on adr.name = dyn_adr.parent
- and adr.is_primary_address = '1'
-
- left join `tabCountry` country
- on country.name = adr.country
-
- WHERE adr.is_primary_address = '1'
- """, filters, as_dict=1)
-
-
-def get_suppliers(filters):
- """
- Get a list of Suppliers.
-
- Arguments:
- filters -- dict of filters to be passed to the sql query
- """
- return frappe.db.sql("""
- SELECT
-
- par.debtor_creditor_number as 'Konto',
- CASE sup.supplier_type
- WHEN 'Company' THEN sup.supplier_name
- ELSE null
- END as 'Name (Adressatentyp Unternehmen)',
- CASE sup.supplier_type
- WHEN 'Individual' THEN TRIM(SUBSTR(sup.supplier_name, LOCATE(' ', sup.supplier_name)))
- ELSE null
- END as 'Name (Adressatentyp natürl. Person)',
- CASE sup.supplier_type
- WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(sup.supplier_name, ' ', 1), ' ', -1)
- ELSE null
- END as 'Vorname (Adressatentyp natürl. Person)',
- CASE sup.supplier_type
- WHEN 'Individual' THEN '1'
- WHEN 'Company' THEN '2'
- ELSE '0'
- END as 'Adressatentyp',
- adr.address_line1 as 'Straße',
- adr.pincode as 'Postleitzahl',
- adr.city as 'Ort',
- UPPER(country.code) as 'Land',
- adr.address_line2 as 'Adresszusatz',
- adr.email_id as 'E-Mail',
- adr.phone as 'Telefon',
- adr.fax as 'Fax',
- sup.website as 'Internet',
- sup.tax_id as 'Steuernummer',
- case sup.on_hold when 1 then sup.release_date else null end as 'Zahlungssperre bis'
-
- FROM `tabSupplier` sup
-
- left join `tabParty Account` par
- on par.parent = sup.name
- and par.parenttype = 'Supplier'
- and par.company = %(company)s
-
- left join `tabDynamic Link` dyn_adr
- on dyn_adr.link_name = sup.name
- and dyn_adr.link_doctype = 'Supplier'
- and dyn_adr.parenttype = 'Address'
-
- left join `tabAddress` adr
- on adr.name = dyn_adr.parent
- and adr.is_primary_address = '1'
-
- left join `tabCountry` country
- on country.name = adr.country
-
- WHERE adr.is_primary_address = '1'
- """, filters, as_dict=1)
-
-
-def get_account_names(filters):
- return frappe.db.sql("""
- SELECT
-
- account_number as 'Konto',
- LEFT(account_name, 40) as 'Kontenbeschriftung',
- 'de-DE' as 'Sprach-ID'
-
- FROM `tabAccount`
- WHERE company = %(company)s
- AND is_group = 0
- AND account_number != ''
- """, filters, as_dict=1)
-
-
-@frappe.whitelist()
-def download_datev_csv(filters):
- """
- Provide accounting entries for download in DATEV format.
-
- Validate the filters, get the data, produce the CSV file and provide it for
- download. Can be called like this:
-
- GET /api/method/erpnext.regional.report.datev.datev.download_datev_csv
-
- Arguments / Params:
- filters -- dict of filters to be passed to the sql query
- """
- if isinstance(filters, str):
- filters = json.loads(filters)
-
- validate(filters)
- company = filters.get('company')
-
- fiscal_year = get_fiscal_year(date=filters.get('from_date'), company=company)
- filters['fiscal_year_start'] = fiscal_year[1]
-
- # set chart of accounts used
- coa = frappe.get_value('Company', company, 'chart_of_accounts')
- filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
-
- datev_settings = frappe.get_doc('DATEV Settings', company)
- filters['account_number_length'] = datev_settings.account_number_length
- filters['temporary_against_account_number'] = datev_settings.temporary_against_account_number
-
- transactions = get_transactions(filters)
- account_names = get_account_names(filters)
- customers = get_customers(filters)
- suppliers = get_suppliers(filters)
-
- zip_name = '{} DATEV.zip'.format(frappe.utils.datetime.date.today())
- zip_and_download(zip_name, [
- {
- '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
deleted file mode 100644
index 052fb2a7244d..000000000000
--- a/erpnext/regional/report/datev/test_datev.py
+++ /dev/null
@@ -1,242 +0,0 @@
-import zipfile
-from io import BytesIO
-from unittest import TestCase
-
-import frappe
-from frappe.utils import cstr, now_datetime, today
-
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.regional.germany.utils.datev.datev_constants import (
- AccountNames,
- DebtorsCreditors,
- Transactions,
-)
-from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, get_header
-from erpnext.regional.report.datev.datev import (
- download_datev_csv,
- get_account_names,
- get_customers,
- get_suppliers,
- get_transactions,
-)
-
-
-def make_company(company_name, abbr):
- if not frappe.db.exists("Company", company_name):
- company = frappe.get_doc({
- "doctype": "Company",
- "company_name": company_name,
- "abbr": abbr,
- "default_currency": "EUR",
- "country": "Germany",
- "create_chart_of_accounts_based_on": "Standard Template",
- "chart_of_accounts": "SKR04 mit Kontonummern"
- })
- company.insert()
- else:
- company = frappe.get_doc("Company", company_name)
-
- # indempotent
- company.create_default_warehouses()
-
- if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}):
- company.create_default_cost_center()
-
- company.save()
- return company
-
-def setup_fiscal_year():
- fiscal_year = None
- year = cstr(now_datetime().year)
- if not frappe.db.get_value("Fiscal Year", {"year": year}, "name"):
- try:
- fiscal_year = frappe.get_doc({
- "doctype": "Fiscal Year",
- "year": year,
- "year_start_date": "{0}-01-01".format(year),
- "year_end_date": "{0}-12-31".format(year)
- })
- fiscal_year.insert()
- except frappe.NameError:
- pass
-
- if fiscal_year:
- fiscal_year.set_as_default()
-
-def make_customer_with_account(customer_name, company):
- acc_name = frappe.db.get_value("Account", {
- "account_name": customer_name,
- "company": company.name
- }, "name")
-
- if not acc_name:
- acc = frappe.get_doc({
- "doctype": "Account",
- "parent_account": "1 - Forderungen aus Lieferungen und Leistungen - _TG",
- "account_name": customer_name,
- "company": company.name,
- "account_type": "Receivable",
- "account_number": "10001"
- })
- acc.insert()
- acc_name = acc.name
-
- if not frappe.db.exists("Customer", customer_name):
- customer = frappe.get_doc({
- "doctype": "Customer",
- "customer_name": customer_name,
- "customer_type": "Company",
- "accounts": [{
- "company": company.name,
- "account": acc_name
- }]
- })
- customer.insert()
- else:
- customer = frappe.get_doc("Customer", customer_name)
-
- return customer
-
-def make_item(item_code, company):
- warehouse_name = frappe.db.get_value("Warehouse", {
- "warehouse_name": "Stores",
- "company": company.name
- }, "name")
-
- if not frappe.db.exists("Item", item_code):
- item = frappe.get_doc({
- "doctype": "Item",
- "item_code": item_code,
- "item_name": item_code,
- "description": item_code,
- "item_group": "All Item Groups",
- "is_stock_item": 0,
- "is_purchase_item": 0,
- "is_customer_provided_item": 0,
- "item_defaults": [{
- "default_warehouse": warehouse_name,
- "company": company.name
- }]
- })
- item.insert()
- else:
- item = frappe.get_doc("Item", item_code)
- return item
-
-def make_datev_settings(company):
- if not frappe.db.exists("DATEV Settings", company.name):
- frappe.get_doc({
- "doctype": "DATEV Settings",
- "client": company.name,
- "client_number": "12345",
- "consultant_number": "67890",
- "temporary_against_account_number": "9999"
- }).insert()
-
-
-class TestDatev(TestCase):
- def setUp(self):
- self.company = make_company("_Test GmbH", "_TG")
- self.customer = make_customer_with_account("_Test Kunde GmbH", self.company)
- self.filters = {
- "company": self.company.name,
- "from_date": today(),
- "to_date": today(),
- "temporary_against_account_number": "9999"
- }
-
- make_datev_settings(self.company)
- item = make_item("_Test Item", self.company)
- setup_fiscal_year()
-
- warehouse = frappe.db.get_value("Item Default", {
- "parent": item.name,
- "company": self.company.name
- }, "default_warehouse")
-
- income_account = frappe.db.get_value("Account", {
- "account_number": "4200",
- "company": self.company.name
- }, "name")
-
- tax_account = frappe.db.get_value("Account", {
- "account_number": "3806",
- "company": self.company.name
- }, "name")
-
- si = create_sales_invoice(
- company=self.company.name,
- customer=self.customer.name,
- currency=self.company.default_currency,
- debit_to=self.customer.accounts[0].account,
- income_account="4200 - Erlöse - _TG",
- expense_account="6990 - Herstellungskosten - _TG",
- cost_center=self.company.cost_center,
- warehouse=warehouse,
- item=item.name,
- do_not_save=1
- )
-
- si.append("taxes", {
- "charge_type": "On Net Total",
- "account_head": tax_account,
- "description": "Umsatzsteuer 19 %",
- "rate": 19,
- "cost_center": self.company.cost_center
- })
-
- si.cost_center = self.company.cost_center
-
- si.save()
- si.submit()
-
- def test_columns(self):
- def is_subset(get_data, allowed_keys):
- """
- Validate that the dict contains only allowed keys.
-
- Params:
- get_data -- Function that returns a list of dicts.
- allowed_keys -- List of allowed keys
- """
- data = get_data(self.filters)
- if data == []:
- # No data and, therefore, no columns is okay
- return True
- actual_set = set(data[0].keys())
- # allowed set must be interpreted as unicode to match the actual set
- allowed_set = set({frappe.as_unicode(key) for key in allowed_keys})
- return actual_set.issubset(allowed_set)
-
- self.assertTrue(is_subset(get_transactions, Transactions.COLUMNS))
- self.assertTrue(is_subset(get_customers, DebtorsCreditors.COLUMNS))
- self.assertTrue(is_subset(get_suppliers, DebtorsCreditors.COLUMNS))
- self.assertTrue(is_subset(get_account_names, AccountNames.COLUMNS))
-
- def test_header(self):
- self.assertTrue(Transactions.DATA_CATEGORY in get_header(self.filters, Transactions))
- self.assertTrue(AccountNames.DATA_CATEGORY in get_header(self.filters, AccountNames))
- self.assertTrue(DebtorsCreditors.DATA_CATEGORY in get_header(self.filters, DebtorsCreditors))
-
- def test_csv(self):
- test_data = [{
- "Umsatz (ohne Soll/Haben-Kz)": 100,
- "Soll/Haben-Kennzeichen": "H",
- "Kontonummer": "4200",
- "Gegenkonto (ohne BU-Schlüssel)": "10000",
- "Belegdatum": today(),
- "Buchungstext": "No remark",
- "Beleginfo - Art 1": "Sales Invoice",
- "Beleginfo - Inhalt 1": "SINV-0001"
- }]
- get_datev_csv(data=test_data, filters=self.filters, csv_class=Transactions)
-
- def test_download(self):
- """Assert that the returned file is a ZIP file."""
- download_datev_csv(self.filters)
-
- # zipfile.is_zipfile() expects a file-like object
- zip_buffer = BytesIO()
- zip_buffer.write(frappe.response['filecontent'])
-
- self.assertTrue(zipfile.is_zipfile(zip_buffer))
From 6ff5f1759b94dba53e92c816f146240b90018bd6 Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Wed, 13 Apr 2022 12:21:27 +0530
Subject: [PATCH 2/3] refactor: remove datev settings
(moved to separate app)
---
.../doctype/datev_settings/__init__.py | 0
.../doctype/datev_settings/datev_settings.js | 8 --
.../datev_settings/datev_settings.json | 125 ------------------
.../doctype/datev_settings/datev_settings.py | 10 --
.../datev_settings/test_datev_settings.py | 9 --
5 files changed, 152 deletions(-)
delete mode 100644 erpnext/regional/doctype/datev_settings/__init__.py
delete mode 100644 erpnext/regional/doctype/datev_settings/datev_settings.js
delete mode 100644 erpnext/regional/doctype/datev_settings/datev_settings.json
delete mode 100644 erpnext/regional/doctype/datev_settings/datev_settings.py
delete mode 100644 erpnext/regional/doctype/datev_settings/test_datev_settings.py
diff --git a/erpnext/regional/doctype/datev_settings/__init__.py b/erpnext/regional/doctype/datev_settings/__init__.py
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.js b/erpnext/regional/doctype/datev_settings/datev_settings.js
deleted file mode 100644
index 3c365494c49d..000000000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('DATEV Settings', {
- refresh: function(frm) {
- frm.add_custom_button(__('Show Report'), () => frappe.set_route('query-report', 'DATEV'), "fa fa-table");
- }
-});
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
deleted file mode 100644
index f60de4c8af42..000000000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "actions": [],
- "autoname": "field:client",
- "creation": "2019-08-13 23:56:34.259906",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "client",
- "client_number",
- "column_break_2",
- "consultant_number",
- "consultant",
- "section_break_4",
- "account_number_length",
- "column_break_6",
- "temporary_against_account_number"
- ],
- "fields": [
- {
- "fieldname": "client",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Client",
- "options": "Company",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "client_number",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Client ID",
- "length": 5,
- "reqd": 1
- },
- {
- "fieldname": "consultant",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Consultant",
- "options": "Supplier"
- },
- {
- "fieldname": "consultant_number",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Consultant ID",
- "length": 7,
- "reqd": 1
- },
- {
- "fieldname": "column_break_2",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "section_break_4",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "column_break_6",
- "fieldtype": "Column Break"
- },
- {
- "default": "4",
- "fieldname": "account_number_length",
- "fieldtype": "Int",
- "label": "Account Number Length",
- "reqd": 1
- },
- {
- "allow_in_quick_entry": 1,
- "fieldname": "temporary_against_account_number",
- "fieldtype": "Data",
- "label": "Temporary Against Account Number",
- "reqd": 1
- }
- ],
- "links": [],
- "modified": "2020-11-19 19:00:09.088816",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "DATEV Settings",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts Manager",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts User",
- "share": 1
- }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.py b/erpnext/regional/doctype/datev_settings/datev_settings.py
deleted file mode 100644
index 686a93e529d8..000000000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-# import frappe
-from frappe.model.document import Document
-
-
-class DATEVSettings(Document):
- pass
diff --git a/erpnext/regional/doctype/datev_settings/test_datev_settings.py b/erpnext/regional/doctype/datev_settings/test_datev_settings.py
deleted file mode 100644
index ba70eb472fee..000000000000
--- a/erpnext/regional/doctype/datev_settings/test_datev_settings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-# import frappe
-import unittest
-
-
-class TestDATEVSettings(unittest.TestCase):
- pass
From b9e3cbdaa7c7b787694b71c14ddccc85983e645a Mon Sep 17 00:00:00 2001
From: Ankush Menat
Date: Wed, 13 Apr 2022 12:23:17 +0530
Subject: [PATCH 3/3] fix(patch): remove datev report/doctypes from db
---
erpnext/patches.txt | 3 ++-
erpnext/patches/v14_0/delete_datev_doctypes.py | 13 +++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
create mode 100644 erpnext/patches/v14_0/delete_datev_doctypes.py
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 1ce4433210c5..63b6bb73e883 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -341,6 +341,7 @@ erpnext.patches.v14_0.delete_shopify_doctypes
erpnext.patches.v14_0.delete_hub_doctypes
erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022
erpnext.patches.v14_0.delete_agriculture_doctypes
+erpnext.patches.v14_0.delete_datev_doctypes
erpnext.patches.v14_0.rearrange_company_fields
erpnext.patches.v14_0.update_leave_notification_template
erpnext.patches.v14_0.restore_einvoice_fields
@@ -362,4 +363,4 @@ erpnext.patches.v13_0.add_cost_center_in_loans
erpnext.patches.v13_0.set_return_against_in_pos_invoice_references
erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
-erpnext.patches.v13_0.create_gst_custom_fields_in_quotation
\ No newline at end of file
+erpnext.patches.v13_0.create_gst_custom_fields_in_quotation
diff --git a/erpnext/patches/v14_0/delete_datev_doctypes.py b/erpnext/patches/v14_0/delete_datev_doctypes.py
new file mode 100644
index 000000000000..a5de91f9c2b4
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_datev_doctypes.py
@@ -0,0 +1,13 @@
+import frappe
+
+
+def execute():
+ install_apps = frappe.get_installed_apps()
+ if "erpnext_datev_uo" in install_apps or "erpnext_datev" in install_apps:
+ return
+
+ # doctypes
+ frappe.delete_doc("DocType", "DATEV Settings", ignore_missing=True, force=True)
+
+ # reports
+ frappe.delete_doc("Report", "DATEV", ignore_missing=True, force=True)