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)