├── erpnext_datev
├── config
│ ├── __init__.py
│ ├── desktop.py
│ └── docs.py
├── utils
│ ├── __init__.py
│ ├── datev_csv.py
│ └── datev_constants.py
├── modules.txt
├── templates
│ ├── __init__.py
│ └── pages
│ │ └── __init__.py
├── erpnext_datev
│ ├── __init__.py
│ ├── doctype
│ │ ├── __init__.py
│ │ ├── datev_settings
│ │ │ ├── __init__.py
│ │ │ ├── test_datev_settings.py
│ │ │ ├── datev_settings.js
│ │ │ ├── datev_settings.py
│ │ │ └── datev_settings.json
│ │ ├── datev_voucher_config
│ │ │ ├── __init__.py
│ │ │ ├── datev_voucher_config.py
│ │ │ └── datev_voucher_config.json
│ │ └── datev_unternehmen_online_settings
│ │ │ ├── __init__.py
│ │ │ ├── test_datev_unternehmen_online_settings.py
│ │ │ ├── datev_unternehmen_online_settings.js
│ │ │ ├── datev_unternehmen_online_settings.json
│ │ │ └── datev_unternehmen_online_settings.py
│ └── report
│ │ ├── __init__.py
│ │ └── datev
│ │ ├── __init__.py
│ │ ├── datev.json
│ │ ├── datev.js
│ │ ├── test_datev.py
│ │ └── datev.py
├── __init__.py
├── patches.txt
├── install.py
├── hooks.py
└── locale
│ ├── main.pot
│ └── de.po
├── .gitignore
├── .editorconfig
├── .releaserc
├── .github
└── workflows
│ └── release.yaml
├── .pre-commit-config.yaml
├── pyproject.toml
├── .eslintrc
└── README.md
/erpnext_datev/config/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/modules.txt:
--------------------------------------------------------------------------------
1 | Erpnext Datev
--------------------------------------------------------------------------------
/erpnext_datev/templates/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/templates/pages/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/report/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = "15.3.3"
2 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/report/datev/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_settings/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_voucher_config/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.pyc
3 | *.egg-info
4 | *.swp
5 | tags
6 | erpnext_datev/docs/current
7 | build/
8 |
--------------------------------------------------------------------------------
/erpnext_datev/patches.txt:
--------------------------------------------------------------------------------
1 | [pre_model_sync]
2 |
3 | [post_model_sync]
4 | execute:frappe.db.delete("Custom Role", {"report": "DATEV"})
5 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_settings/test_datev_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | import unittest
6 |
7 |
8 | class TestDATEVSettings(unittest.TestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_datev/config/desktop.py:
--------------------------------------------------------------------------------
1 | from frappe import _
2 |
3 |
4 | def get_data():
5 | return [
6 | {
7 | "module_name": "ERPNext DATEV",
8 | "color": "grey",
9 | "icon": "octicon octicon-file-directory",
10 | "type": "module",
11 | "label": _("ERPNext DATEV"),
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/test_datev_unternehmen_online_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021, Alyf and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | import unittest
6 |
7 |
8 | class TestDATEVUnternehmenOnlineSettings(unittest.TestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021, Alyf and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class DATEVVoucherConfig(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_datev/config/docs.py:
--------------------------------------------------------------------------------
1 | """
2 | Configuration for docs
3 | """
4 |
5 | # source_link = "https://github.com/[org_name]/erpnext_datev"
6 | # headline = "App that does everything"
7 | # sub_heading = "Yes, you got that right the first time, everything"
8 |
9 |
10 | def get_context(context):
11 | context.brand_html = "ERPNext DATEV"
12 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("DATEV Settings", {
5 | refresh: function (frm) {
6 | frm.add_custom_button(
7 | "Show Report",
8 | () => frappe.set_route("query-report", "DATEV"),
9 | "fa fa-table"
10 | );
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Root editor config file
2 | root = true
3 |
4 | # Common settings
5 | [*]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 | charset = utf-8
10 |
11 | # python, js indentation settings
12 | [{*.py,*.js,*.vue,*.css,*.scss,*.html}]
13 | indent_style = tab
14 | indent_size = 4
15 | max_line_length = 99
16 |
17 | # JSON files - mostly doctype schema files
18 | [{*.json}]
19 | insert_final_newline = false
20 | indent_style = space
21 | indent_size = 2
22 |
--------------------------------------------------------------------------------
/erpnext_datev/install.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
3 |
4 |
5 | def after_install():
6 | make_custom_fields()
7 |
8 |
9 | def make_custom_fields():
10 | custom_fields = {
11 | "Party Account": [
12 | dict(
13 | fieldname="debtor_creditor_number",
14 | label="Debtor/Creditor Number",
15 | fieldtype="Data",
16 | insert_after="account",
17 | translatable=0,
18 | )
19 | ]
20 | }
21 |
22 | create_custom_fields(custom_fields)
23 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/report/datev/datev.json:
--------------------------------------------------------------------------------
1 | {
2 | "add_total_row": 0,
3 | "columns": [],
4 | "creation": "2022-02-17 23:29:46.760108",
5 | "disabled": 0,
6 | "docstatus": 0,
7 | "doctype": "Report",
8 | "filters": [],
9 | "idx": 0,
10 | "is_standard": "Yes",
11 | "letterhead": null,
12 | "modified": "2024-07-15 23:10:51.572551",
13 | "modified_by": "Administrator",
14 | "module": "Erpnext Datev",
15 | "name": "DATEV",
16 | "owner": "Administrator",
17 | "prepared_report": 0,
18 | "ref_doctype": "GL Entry",
19 | "report_name": "DATEV",
20 | "report_type": "Script Report",
21 | "roles": [
22 | {
23 | "role": "Accounts User"
24 | },
25 | {
26 | "role": "Accounts Manager"
27 | }
28 | ]
29 | }
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021, Alyf and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("DATEV Unternehmen Online Settings", {
5 | refresh: function (frm) {
6 | frm.set_query("voucher_type", "datev_voucher_config", function (doc, cdt, cdn) {
7 | return {
8 | filters: {
9 | name: [
10 | "in",
11 | ["Sales Invoice", "Purchase Invoice", "Expense Claim", "E Invoice Import"],
12 | ],
13 | },
14 | };
15 | });
16 |
17 | frm.set_query("print_format", "datev_voucher_config", function (doc, cdt, cdn) {
18 | let row = locals[cdt][cdn];
19 | return {
20 | filters: {
21 | doc_type: row.voucher_type,
22 | },
23 | };
24 | });
25 | },
26 | });
27 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, ALYF GmbH and contributors
2 | # For license information, please see license.txt
3 |
4 | from frappe import _, throw
5 | from frappe.model.document import Document
6 |
7 |
8 | class DATEVSettings(Document):
9 | def validate(self):
10 | if (
11 | self.temporary_against_account_number
12 | and len(self.temporary_against_account_number) != self.account_number_length
13 | ):
14 | throw(
15 | _("Temporary Against Account Number must be {0} digits long").format(
16 | self.account_number_length
17 | )
18 | )
19 |
20 | if (
21 | self.opening_against_account_number
22 | and len(self.opening_against_account_number) != self.account_number_length
23 | ):
24 | throw(
25 | _("Opening Against Account Number must be {0} digits long").format(self.account_number_length)
26 | )
27 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "branches": ["version-14", "version-15"],
3 | "plugins": [
4 | [
5 | "@semantic-release/commit-analyzer", {
6 | "releaseRules": [
7 | {"breaking": true, "release": "minor"},
8 | {"revert": true, "release": "patch"},
9 | {"type": "feat", "release": "minor"},
10 | {"type": "patch", "release": "minor"},
11 | {"type": "fix", "release": "patch"},
12 | {"type": "perf", "release": "patch"},
13 | {"type": "refactor", "release": "patch"},
14 | {"type": "docs", "release": "patch"},
15 | {"type": "chore", "release": "patch"},
16 | {"type": "ci", "release": "patch"}
17 | ]
18 | }
19 | ],
20 | "@semantic-release/release-notes-generator",
21 | [
22 | "@semantic-release/exec", {
23 | "prepareCmd": 'sed -ir -E "s/\"[0-9]+\.[0-9]+\.[0-9]+\"/\"${nextRelease.version}\"/" erpnext_datev/__init__.py'
24 | }
25 | ],
26 | [
27 | "@semantic-release/git", {
28 | "assets": ["erpnext_datev/__init__.py"],
29 | "message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
30 | }
31 | ],
32 | "@semantic-release/github"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Generate Semantic Release
2 | on:
3 | push:
4 | branches:
5 | - version-15
6 |
7 | jobs:
8 | release:
9 | name: Release
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout Entire Repository
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 | persist-credentials: false # https://github.com/semantic-release/semantic-release/blob/master/docs/recipes/ci-configurations/github-actions.md#pushing-packagejson-changes-to-a-master-branch
17 | - name: Setup Node.js
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: "lts/*"
21 | - name: Setup dependencies
22 | run: |
23 | npm install @semantic-release/git @semantic-release/exec --no-save
24 | - name: Create Release
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
27 | GIT_AUTHOR_NAME: "alyf-linus"
28 | GIT_AUTHOR_EMAIL: "136631072+alyf-linus@users.noreply.github.com"
29 | GIT_COMMITTER_NAME: "alyf-linus"
30 | GIT_COMMITTER_EMAIL: "136631072+alyf-linus@users.noreply.github.com"
31 | run: npx semantic-release
32 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | exclude: 'node_modules|.git'
2 | default_stages: [pre-commit]
3 | fail_fast: false
4 |
5 |
6 | repos:
7 | - repo: https://github.com/pre-commit/pre-commit-hooks
8 | rev: v4.3.0
9 | hooks:
10 | - id: trailing-whitespace
11 | files: "frappe.*"
12 | exclude: ".*json$|.*txt$|.*csv|.*md|.*svg"
13 | - id: check-yaml
14 | - id: no-commit-to-branch
15 | args: ['--branch', 'develop']
16 | - id: check-merge-conflict
17 | - id: check-ast
18 | - id: check-json
19 | - id: check-toml
20 | - id: check-yaml
21 | - id: debug-statements
22 |
23 | - repo: https://github.com/astral-sh/ruff-pre-commit
24 | rev: v0.2.0
25 | hooks:
26 | - id: ruff
27 | name: "Run ruff import sorter"
28 | args: ["--select=I", "--fix"]
29 |
30 | - id: ruff
31 | name: "Run ruff linter"
32 |
33 | - id: ruff-format
34 | name: "Run ruff formatter"
35 |
36 | - repo: https://github.com/pre-commit/mirrors-prettier
37 | rev: v2.7.1
38 | hooks:
39 | - id: prettier
40 | types_or: [javascript, vue, scss]
41 |
42 | - repo: https://github.com/pre-commit/mirrors-eslint
43 | rev: v8.44.0
44 | hooks:
45 | - id: eslint
46 | types_or: [javascript]
47 | args: ['--quiet']
48 |
49 | ci:
50 | autoupdate_schedule: weekly
51 | skip: []
52 | submodules: false
53 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "erpnext_datev"
3 | authors = [
4 | { name = "ALYF GmbH", email = "hallo@alyf.de"}
5 | ]
6 | description = "ERPNext-DATEV Inegration"
7 | requires-python = ">=3.10"
8 | readme = "README.md"
9 | dynamic = ["version"]
10 | dependencies = [
11 | "pandas~=2.2.2",
12 | ]
13 |
14 | [build-system]
15 | requires = ["flit_core >=3.4,<4"]
16 | build-backend = "flit_core.buildapi"
17 |
18 | [tool.bench.frappe-dependencies]
19 | frappe = ">=15.0.0,<16.0.0"
20 | erpnext = ">=15.0.0,<16.0.0"
21 |
22 | [tool.ruff]
23 | line-length = 110
24 | target-version = "py310"
25 |
26 | [tool.ruff.lint]
27 | select = [
28 | "F",
29 | "E",
30 | "W",
31 | "I",
32 | "UP",
33 | "B",
34 | "RUF",
35 | ]
36 | ignore = [
37 | "B017", # assertRaises(Exception) - should be more specific
38 | "B018", # useless expression, not assigned to anything
39 | "B023", # function doesn't bind loop variable - will have last iteration's value
40 | "B904", # raise inside except without from
41 | "E101", # indentation contains mixed spaces and tabs
42 | "E402", # module level import not at top of file
43 | "E501", # line too long
44 | "E741", # ambiguous variable name
45 | "F401", # "unused" imports
46 | "F403", # can't detect undefined names from * import
47 | "F405", # can't detect undefined names from * import
48 | "F722", # syntax error in forward type annotation
49 | "W191", # indentation contains tabs
50 | "RUF001", # string contains ambiguous unicode character
51 | "UP032", # use f-string instead of format
52 | ]
53 | typing-modules = ["frappe.types.DF"]
54 |
55 | [tool.ruff.format]
56 | quote-style = "double"
57 | indent-style = "tab"
58 | docstring-code-format = true
59 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "creation": "2021-12-03 18:53:41.716854",
4 | "doctype": "DocType",
5 | "editable_grid": 1,
6 | "engine": "InnoDB",
7 | "field_order": [
8 | "voucher_type",
9 | "recipient",
10 | "attach_print",
11 | "print_format",
12 | "attach_files"
13 | ],
14 | "fields": [
15 | {
16 | "columns": 2,
17 | "fieldname": "voucher_type",
18 | "fieldtype": "Link",
19 | "in_list_view": 1,
20 | "label": "Voucher Type",
21 | "options": "DocType",
22 | "reqd": 1
23 | },
24 | {
25 | "depends_on": "attach_print",
26 | "fieldname": "print_format",
27 | "fieldtype": "Link",
28 | "label": "Print Format",
29 | "mandatory_depends_on": "eval: doc.attach_print === 1",
30 | "options": "Print Format"
31 | },
32 | {
33 | "columns": 4,
34 | "fieldname": "recipient",
35 | "fieldtype": "Data",
36 | "in_list_view": 1,
37 | "label": "Recipient",
38 | "options": "Email",
39 | "reqd": 1
40 | },
41 | {
42 | "columns": 2,
43 | "default": "0",
44 | "description": "Send the voucher printed as PDF",
45 | "fieldname": "attach_print",
46 | "fieldtype": "Check",
47 | "in_list_view": 1,
48 | "label": "Attach Print"
49 | },
50 | {
51 | "columns": 2,
52 | "default": "0",
53 | "description": "Send files attached to the voucher",
54 | "fieldname": "attach_files",
55 | "fieldtype": "Check",
56 | "in_list_view": 1,
57 | "label": "Attach Files"
58 | }
59 | ],
60 | "istable": 1,
61 | "links": [],
62 | "modified": "2021-12-14 14:44:10.678868",
63 | "modified_by": "Administrator",
64 | "module": "Erpnext Datev",
65 | "name": "DATEV Voucher Config",
66 | "owner": "Administrator",
67 | "permissions": [],
68 | "sort_field": "modified",
69 | "sort_order": "DESC"
70 | }
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/report/datev/datev.js:
--------------------------------------------------------------------------------
1 | frappe.query_reports["DATEV"] = {
2 | filters: [
3 | {
4 | fieldname: "company",
5 | label: __("Company"),
6 | fieldtype: "Link",
7 | options: "Company",
8 | default:
9 | frappe.defaults.get_user_default("Company") ||
10 | frappe.defaults.get_global_default("Company"),
11 | reqd: 1,
12 | },
13 | {
14 | fieldname: "from_date",
15 | label: __("From Date"),
16 | default: moment().subtract(1, "month").startOf("month").format(),
17 | fieldtype: "Date",
18 | reqd: 1,
19 | },
20 | {
21 | fieldname: "to_date",
22 | label: __("To Date"),
23 | default: moment().subtract(1, "month").endOf("month").format(),
24 | fieldtype: "Date",
25 | reqd: 1,
26 | },
27 | {
28 | fieldname: "voucher_type",
29 | label: __("Voucher Type"),
30 | fieldtype: "Select",
31 | options:
32 | "\nSales Invoice\nPurchase Invoice\nPayment Entry\nExpense Claim\nPayroll Entry\nBank Reconciliation\nAsset\nStock Entry\nJournal Entry",
33 | },
34 | ],
35 | onload: function (query_report) {
36 | let company = frappe.query_report.get_filter_value("company");
37 | frappe.db.exists("DATEV Settings", company).then((settings_exist) => {
38 | if (!settings_exist) {
39 | frappe.confirm(
40 | __(
41 | "DATEV Settings for your Company are missing. Would you like to create them now?"
42 | ),
43 | () => frappe.new_doc("DATEV Settings", { company: company })
44 | );
45 | }
46 | });
47 |
48 | query_report.page.set_primary_action(__("Download DATEV File"), () => {
49 | const filters = encodeURIComponent(JSON.stringify(query_report.get_values()));
50 | window.open(
51 | `/api/method/erpnext_datev.erpnext_datev.report.datev.datev.download_datev_csv?filters=${filters}`
52 | );
53 | });
54 |
55 | query_report.page.add_menu_item(__("Change DATEV Settings"), () => {
56 | let company = frappe.query_report.get_filter_value("company"); // read company from filters again – it might have changed by now.
57 | frappe.set_route("Form", "DATEV Settings", company);
58 | });
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2021-12-03 18:51:04.279493",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "enabled",
10 | "column_break_xkqo",
11 | "default_print_language",
12 | "section_break_2",
13 | "sender",
14 | "datev_voucher_config"
15 | ],
16 | "fields": [
17 | {
18 | "default": "0",
19 | "fieldname": "enabled",
20 | "fieldtype": "Check",
21 | "label": "Enabled"
22 | },
23 | {
24 | "fieldname": "section_break_2",
25 | "fieldtype": "Section Break"
26 | },
27 | {
28 | "fieldname": "datev_voucher_config",
29 | "fieldtype": "Table",
30 | "label": "DATEV Voucher Config",
31 | "mandatory_depends_on": "eval: doc.enabled === 1",
32 | "options": "DATEV Voucher Config"
33 | },
34 | {
35 | "fieldname": "sender",
36 | "fieldtype": "Link",
37 | "label": "Sender",
38 | "mandatory_depends_on": "eval: doc.enabled === 1",
39 | "options": "Email Account"
40 | },
41 | {
42 | "fieldname": "column_break_xkqo",
43 | "fieldtype": "Column Break"
44 | },
45 | {
46 | "description": "A document is printed according to its language field. If it doesn't have a language field or it is empty, the Default Print Language is used. If Default Print Language is empty, the language according to System Settings is used.",
47 | "fieldname": "default_print_language",
48 | "fieldtype": "Link",
49 | "label": "Default Print Language",
50 | "options": "Language"
51 | }
52 | ],
53 | "index_web_pages_for_search": 1,
54 | "issingle": 1,
55 | "links": [],
56 | "modified": "2025-03-10 12:10:58.029963",
57 | "modified_by": "Administrator",
58 | "module": "Erpnext Datev",
59 | "name": "DATEV Unternehmen Online Settings",
60 | "owner": "Administrator",
61 | "permissions": [
62 | {
63 | "create": 1,
64 | "delete": 1,
65 | "email": 1,
66 | "print": 1,
67 | "read": 1,
68 | "role": "System Manager",
69 | "share": 1,
70 | "write": 1
71 | }
72 | ],
73 | "sort_field": "modified",
74 | "sort_order": "DESC",
75 | "states": []
76 | }
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "node": true,
5 | "es2022": true
6 | },
7 | "parserOptions": {
8 | "sourceType": "module"
9 | },
10 | "extends": "eslint:recommended",
11 | "rules": {
12 | "indent": "off",
13 | "brace-style": "off",
14 | "no-mixed-spaces-and-tabs": "off",
15 | "no-useless-escape": "off",
16 | "space-unary-ops": ["error", { "words": true }],
17 | "linebreak-style": "off",
18 | "quotes": ["off"],
19 | "semi": "off",
20 | "camelcase": "off",
21 | "no-unused-vars": "off",
22 | "no-console": ["warn"],
23 | "no-extra-boolean-cast": ["off"],
24 | "no-control-regex": ["off"]
25 | },
26 | "root": true,
27 | "globals": {
28 | "frappe": true,
29 | "Vue": true,
30 | "SetVueGlobals": true,
31 | "__": true,
32 | "repl": true,
33 | "Class": true,
34 | "locals": true,
35 | "cint": true,
36 | "cstr": true,
37 | "cur_frm": true,
38 | "cur_dialog": true,
39 | "cur_page": true,
40 | "cur_list": true,
41 | "cur_tree": true,
42 | "msg_dialog": true,
43 | "is_null": true,
44 | "in_list": true,
45 | "has_common": true,
46 | "posthog": true,
47 | "has_words": true,
48 | "validate_email": true,
49 | "open_web_template_values_editor": true,
50 | "validate_name": true,
51 | "validate_phone": true,
52 | "validate_url": true,
53 | "get_number_format": true,
54 | "format_number": true,
55 | "format_currency": true,
56 | "comment_when": true,
57 | "open_url_post": true,
58 | "toTitle": true,
59 | "lstrip": true,
60 | "rstrip": true,
61 | "strip": true,
62 | "strip_html": true,
63 | "replace_all": true,
64 | "flt": true,
65 | "precision": true,
66 | "CREATE": true,
67 | "AMEND": true,
68 | "CANCEL": true,
69 | "copy_dict": true,
70 | "get_number_format_info": true,
71 | "strip_number_groups": true,
72 | "print_table": true,
73 | "Layout": true,
74 | "web_form_settings": true,
75 | "$c": true,
76 | "$a": true,
77 | "$i": true,
78 | "$bg": true,
79 | "$y": true,
80 | "$c_obj": true,
81 | "refresh_many": true,
82 | "refresh_field": true,
83 | "toggle_field": true,
84 | "get_field_obj": true,
85 | "get_query_params": true,
86 | "unhide_field": true,
87 | "hide_field": true,
88 | "set_field_options": true,
89 | "getCookie": true,
90 | "getCookies": true,
91 | "get_url_arg": true,
92 | "md5": true,
93 | "$": true,
94 | "jQuery": true,
95 | "moment": true,
96 | "hljs": true,
97 | "Awesomplete": true,
98 | "Sortable": true,
99 | "Showdown": true,
100 | "Taggle": true,
101 | "Gantt": true,
102 | "Slick": true,
103 | "Webcam": true,
104 | "PhotoSwipe": true,
105 | "PhotoSwipeUI_Default": true,
106 | "io": true,
107 | "JsBarcode": true,
108 | "L": true,
109 | "Chart": true,
110 | "DataTable": true,
111 | "Cypress": true,
112 | "cy": true,
113 | "it": true,
114 | "describe": true,
115 | "expect": true,
116 | "context": true,
117 | "before": true,
118 | "beforeEach": true,
119 | "after": true,
120 | "qz": true,
121 | "localforage": true,
122 | "extend_cscript": true
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "autoname": "field:client",
4 | "creation": "2022-02-17 23:29:46.086115",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "client",
10 | "client_number",
11 | "column_break_2",
12 | "consultant_number",
13 | "consultant",
14 | "section_break_4",
15 | "account_number_length",
16 | "column_break_6",
17 | "temporary_against_account_number",
18 | "opening_against_account_number"
19 | ],
20 | "fields": [
21 | {
22 | "fieldname": "client",
23 | "fieldtype": "Link",
24 | "in_list_view": 1,
25 | "label": "Client",
26 | "options": "Company",
27 | "reqd": 1,
28 | "unique": 1
29 | },
30 | {
31 | "fieldname": "client_number",
32 | "fieldtype": "Data",
33 | "in_list_view": 1,
34 | "label": "Client ID",
35 | "length": 5,
36 | "reqd": 1
37 | },
38 | {
39 | "fieldname": "consultant",
40 | "fieldtype": "Link",
41 | "in_list_view": 1,
42 | "label": "Consultant",
43 | "options": "Supplier"
44 | },
45 | {
46 | "fieldname": "consultant_number",
47 | "fieldtype": "Data",
48 | "in_list_view": 1,
49 | "label": "Consultant ID",
50 | "length": 7,
51 | "reqd": 1
52 | },
53 | {
54 | "fieldname": "column_break_2",
55 | "fieldtype": "Column Break"
56 | },
57 | {
58 | "fieldname": "section_break_4",
59 | "fieldtype": "Section Break"
60 | },
61 | {
62 | "fieldname": "column_break_6",
63 | "fieldtype": "Column Break"
64 | },
65 | {
66 | "default": "4",
67 | "fieldname": "account_number_length",
68 | "fieldtype": "Int",
69 | "label": "Account Number Length",
70 | "reqd": 1
71 | },
72 | {
73 | "allow_in_quick_entry": 1,
74 | "default": "9090",
75 | "description": "Will be used as against account for all normal ledger entries",
76 | "fieldname": "temporary_against_account_number",
77 | "fieldtype": "Data",
78 | "label": "Temporary Against Account Number",
79 | "reqd": 1
80 | },
81 | {
82 | "default": "9000",
83 | "description": "Will be used as against account for opening ledger entries",
84 | "fieldname": "opening_against_account_number",
85 | "fieldtype": "Data",
86 | "label": "Opening Against Account Number"
87 | }
88 | ],
89 | "grid_page_length": 50,
90 | "links": [],
91 | "modified": "2025-06-12 16:30:22.776994",
92 | "modified_by": "Administrator",
93 | "module": "Erpnext Datev",
94 | "name": "DATEV Settings",
95 | "naming_rule": "By fieldname",
96 | "owner": "Administrator",
97 | "permissions": [
98 | {
99 | "create": 1,
100 | "delete": 1,
101 | "email": 1,
102 | "export": 1,
103 | "print": 1,
104 | "read": 1,
105 | "report": 1,
106 | "role": "System Manager",
107 | "share": 1,
108 | "write": 1
109 | },
110 | {
111 | "create": 1,
112 | "delete": 1,
113 | "email": 1,
114 | "export": 1,
115 | "print": 1,
116 | "read": 1,
117 | "report": 1,
118 | "role": "Accounts Manager",
119 | "share": 1,
120 | "write": 1
121 | },
122 | {
123 | "create": 1,
124 | "email": 1,
125 | "export": 1,
126 | "print": 1,
127 | "read": 1,
128 | "report": 1,
129 | "role": "Accounts User",
130 | "share": 1
131 | }
132 | ],
133 | "quick_entry": 1,
134 | "row_format": "Dynamic",
135 | "sort_field": "modified",
136 | "sort_order": "DESC",
137 | "states": [],
138 | "track_changes": 1
139 | }
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021, ALYF GmbH and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe import _
6 | from frappe.core.doctype.communication.email import make as make_communication
7 | from frappe.model.document import Document
8 | from frappe.translate import print_language
9 |
10 |
11 | class DATEVUnternehmenOnlineSettings(Document):
12 | def validate(self):
13 | for voucher_config in self.datev_voucher_config:
14 | if not voucher_config.attach_print and not voucher_config.attach_files:
15 | frappe.throw(
16 | _("Please configure attachments for voucher type {}.").format(
17 | _(voucher_config.voucher_type)
18 | )
19 | )
20 |
21 |
22 | def send(doc, method):
23 | settings = frappe.get_single("DATEV Unternehmen Online Settings")
24 | if not settings.enabled:
25 | return
26 |
27 | voucher_config = get_voucher_config(settings, doc.doctype)
28 | if not voucher_config:
29 | return
30 |
31 | attachments = []
32 |
33 | if voucher_config.attach_print:
34 | document_language = doc.language if hasattr(doc, "language") else None
35 | print_language = document_language or settings.default_print_language or frappe.db.get_default("lang")
36 | filename = attach_print(
37 | doc.doctype,
38 | doc.name,
39 | print_language,
40 | voucher_config.print_format,
41 | )
42 | attachments.append(filename)
43 |
44 | if voucher_config.attach_files:
45 | attachments.extend(get_attached_files(doc.doctype, doc.name))
46 |
47 | if not attachments:
48 | frappe.msgprint(
49 | # fmt: off
50 | _("{} was not sent to DATEV because no attachments have been found.").format(_(doc.doctype))
51 | # fmt: on
52 | )
53 | return
54 |
55 | make_communication(
56 | doctype=doc.doctype,
57 | name=doc.name,
58 | content=_("New {0} {1} sent by the ERPNext-DATEV integration.").format(_(doc.doctype), doc.name),
59 | subject=f"{_(doc.doctype)}: {doc.name}",
60 | sender=frappe.get_value("Email Account", settings.sender, "email_id"),
61 | recipients=[voucher_config.recipient],
62 | communication_medium="Email",
63 | send_email=True,
64 | attachments=attachments,
65 | communication_type="Automated Message",
66 | )
67 |
68 |
69 | def attach_print(doctype, name, language, print_format):
70 | with print_language(language):
71 | data = frappe.get_print(doctype, name, print_format, as_pdf=True)
72 |
73 | if doctype == "Sales Invoice" and "eu_einvoice" in frappe.get_installed_apps():
74 | try:
75 | from eu_einvoice.european_e_invoice.custom.sales_invoice import attach_xml_to_pdf
76 |
77 | data = attach_xml_to_pdf(name, data)
78 | except Exception:
79 | msg = _("Failed to attach XML to Sales Invoice PDF for DATEV")
80 | frappe.log_error(title=msg, reference_doctype=doctype, reference_name=name)
81 | frappe.msgprint(msg, indicator="red", alert=True)
82 |
83 | file = frappe.new_doc("File")
84 | file.file_name = f"{name}.pdf"
85 | file.content = data
86 | file.attached_to_doctype = doctype
87 | file.attached_to_name = name
88 | file.is_private = 1
89 | file.save()
90 |
91 | return file.name
92 |
93 |
94 | def get_voucher_config(settings: DATEVUnternehmenOnlineSettings, doctype: str):
95 | voucher_config = settings.get("datev_voucher_config", filters={"voucher_type": doctype})
96 | if not voucher_config:
97 | return
98 |
99 | return voucher_config[0]
100 |
101 |
102 | def get_attached_files(doctype: str, docname: str):
103 | return frappe.get_all(
104 | "File",
105 | filters={
106 | "attached_to_doctype": doctype,
107 | "attached_to_name": docname,
108 | },
109 | pluck="name",
110 | )
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## ERPNext - DATEV Integration
2 |
3 | Integration between [ERPNext](https://github.com/frappe/erpnext) and DATEV.
4 |
5 | - [DATEV Unternehmen Online](https://www.datev.de/web/de/mydatev/online-anwendungen/datev-unternehmen-online/)
6 |
7 | When a voucher is submitted, it will be sent to DATEV Unternehmen Online by email. Either by converting the document to PDF first (outgoing vouchers) or by sending files attached to the document (incoming vouchers).
8 |
9 | - DATEV CSV Export
10 |
11 | Export raw **GL Entries** from ERPNext in the DATEV CSV format.
12 |
13 | ## Install on Frappe Cloud
14 |
15 | 1. Go to https://frappecloud.com/dashboard/#/sites and click the "New Site" button.
16 | 2. In Step 2 ("Select apps to install"), select "ERPNext" and "DATEV Unternehmen Online Integration".
17 | 3. Complete the new site wizard.
18 |
19 |
20 | ## Setup DATEV CSV Export
21 |
22 | 1. Datev Settings
23 |
24 | Configure you client number, you tax consultant's number and a temporary against account. We recommend keeping the default against account "9090" as described in the [DATEV Help Center](https://apps.datev.de/help-center/documents/1002764).
25 |
26 | 2. DATEV Report
27 |
28 | Now you can use the report "DATEV". This is a preview of the transactions data. It can be exported, along with the master data, as zip file via the report's menu. Your tax xonsultant can then import your GL Entries into his DATEV system.
29 |
30 | > [!IMPORTANT]
31 | > ERPNext does not have automatic VAT deduction ("Automatikkonten") on the GL Entry level. By using the default against account "9090", the automation is disabled.
32 | >
33 | > If you use a different against account, please ensure to only book to non-automatic accounts in ERPNext. Otherwise, the VAT deduction will be incorrect.
34 |
35 | ## Setup DATEV Unternehmen Online [en]
36 |
37 | 1. Open **DATEV Unternehmen Online Settings**
38 | 2. Enable the integration
39 | 3. Select the _Email Account_ that should be used to send receipts to DATEV Unternehmen Online
40 | 4. Add a row to the table
41 | 5. Select the _Voucher Type_ (**Sales Invoice**, **Purchase Invoice** or **Expense Claim**)
42 | 6. Paste the target email address provided by DATEV Unternehmen Online ([DATEV Help Center](https://apps.datev.de/help-center/documents/1007550))
43 | 8. Enable "Add Attachments" or "Add Print"
44 | 9. Save
45 |
46 | 
47 |
48 | ## Einrichtung DATEV Unternehmen Online [de]
49 |
50 | 1. Öffnen Sie **DATEV Unternehmen Online-Einstellungen** (engl. **DATEV Unternehmen Online Settings**)
51 | 2. Aktivieren Sie die Integration
52 | 3. Wählen Sie das _E-Mail-Konto_ (engl. _Email Account_) aus, das für den Versand von Belegen an DATEV Unternehmen Online verwendet werden soll
53 | 4. Fügen Sie der Tabelle eine Zeile hinzu
54 | 5. Wählen Sie die _Belegart_ (engl. _Voucher Type_)
55 | 6. Fügen Sie die für diese Belegart von DATEV Unternehmen Online bereitgestellte Ziel-E-Mail-Adresse ein (mehr dazu im [DATEV Help Center](https://apps.datev.de/help-center/documents/1007550))
56 | > **Achtung:** Die E-Mail-Adresse des Senders muss mit dem in Schritt 3 ausgewählten E-Mail-Konto übereinstimmen
57 | 8. Aktivieren Sie "Anhänge hinzufügen" oder "Druck hinzufügen".
58 | 9. Speichern Sie die **DATEV Unternehmen Online-Einstellungen**
59 |
60 | ## Kompatibilität mit _PDF on Submit_
61 |
62 | Falls Sie [PDF on Submit](https://github.com/alyf-de/erpnext_pdf-on-submit) für dieselbe Belegart verwenden, wählen Sie "Anhänge hinzufügen" statt "Druck hinzufügen". _PDF on Submit_ fügt dann die PDF-Datei als Anhang zum Beleg hinzu und die DATEV-Integration versendet diesen.
63 |
64 | ## Disclaimer
65 |
66 | "DATEV" and "DATEV Unternehmen Online" are trademarks of [DATEV eG](https://www.datev.de/). This integration is not approved or endorsed by DATEV eG.
67 |
68 | ## License
69 |
70 | GPLv3
71 |
--------------------------------------------------------------------------------
/erpnext_datev/hooks.py:
--------------------------------------------------------------------------------
1 | from . import __version__ as app_version
2 |
3 | app_name = "erpnext_datev"
4 | app_title = "ERPNext DATEV Integration"
5 | app_publisher = "ALYF GmbH"
6 | app_description = "DATEV integration for ERPNext"
7 | required_apps = ["frappe/erpnext"]
8 | app_icon = "octicon octicon-file-directory"
9 | app_color = "grey"
10 | app_email = "hallo@alyf.de"
11 | app_license = "GPLv3"
12 |
13 | # fixtures = []
14 |
15 | # Includes in
language field. If it doesn't have a language field or it is empty, the Default Print Language is used. If Default Print Language is empty, the language according to System Settings is used."
35 | msgstr ""
36 |
37 | #. Label of a Int field in DocType 'DATEV Settings'
38 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
39 | msgid "Account Number Length"
40 | msgstr ""
41 |
42 | #. Name of a role
43 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
44 | msgid "Accounts Manager"
45 | msgstr ""
46 |
47 | #. Name of a role
48 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
49 | msgid "Accounts User"
50 | msgstr ""
51 |
52 | #. Label of a Check field in DocType 'DATEV Voucher Config'
53 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
54 | msgid "Attach Files"
55 | msgstr ""
56 |
57 | #. Label of a Check field in DocType 'DATEV Voucher Config'
58 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
59 | msgid "Attach Print"
60 | msgstr ""
61 |
62 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:55
63 | msgid "Change DATEV Settings"
64 | msgstr ""
65 |
66 | #. Label of a Link field in DocType 'DATEV Settings'
67 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
68 | msgid "Client"
69 | msgstr ""
70 |
71 | #. Label of a Data field in DocType 'DATEV Settings'
72 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
73 | msgid "Client ID"
74 | msgstr ""
75 |
76 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:5
77 | msgid "Company"
78 | msgstr ""
79 |
80 | #. Label of a Link field in DocType 'DATEV Settings'
81 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
82 | msgid "Consultant"
83 | msgstr ""
84 |
85 | #. Label of a Data field in DocType 'DATEV Settings'
86 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
87 | msgid "Consultant ID"
88 | msgstr ""
89 |
90 | #. Name of a report
91 | #: erpnext_datev/erpnext_datev/report/datev/datev.json
92 | msgid "DATEV"
93 | msgstr ""
94 |
95 | #. Name of a DocType
96 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
97 | msgid "DATEV Settings"
98 | msgstr ""
99 |
100 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:40
101 | msgid "DATEV Settings for your Company are missing. Would you like to create them now?"
102 | msgstr ""
103 |
104 | #: erpnext_datev/erpnext_datev/report/datev/datev.py:187
105 | msgid "DATEV Settings missing"
106 | msgstr ""
107 |
108 | #. Name of a DocType
109 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
110 | msgid "DATEV Unternehmen Online Settings"
111 | msgstr ""
112 |
113 | #. Label of a Table field in DocType 'DATEV Unternehmen Online Settings'
114 | #. Name of a DocType
115 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
116 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
117 | msgid "DATEV Voucher Config"
118 | msgstr ""
119 |
120 | #: erpnext_datev/erpnext_datev/report/datev/datev.py:197
121 | msgid "Dates {} and {} are not in the same fiscal year."
122 | msgstr ""
123 |
124 | #. Label of a Link field in DocType 'DATEV Unternehmen Online Settings'
125 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
126 | msgid "Default Print Language"
127 | msgstr ""
128 |
129 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:48
130 | msgid "Download DATEV File"
131 | msgstr ""
132 |
133 | #: erpnext_datev/config/desktop.py:11
134 | msgid "ERPNext DATEV"
135 | msgstr ""
136 |
137 | #. Label of a Check field in DocType 'DATEV Unternehmen Online Settings'
138 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
139 | msgid "Enabled"
140 | msgstr ""
141 |
142 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:79
143 | msgid "Failed to attach XML to Sales Invoice PDF for DATEV"
144 | msgstr ""
145 |
146 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:15
147 | msgid "From Date"
148 | msgstr ""
149 |
150 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:58
151 | msgid "New {0} {1} sent by the ERPNext-DATEV integration."
152 | msgstr ""
153 |
154 | #. Label of a Data field in DocType 'DATEV Settings'
155 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
156 | msgid "Opening Against Account Number"
157 | msgstr ""
158 |
159 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.py:25
160 | msgid "Opening Against Account Number must be {0} digits long"
161 | msgstr ""
162 |
163 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:16
164 | msgid "Please configure attachments for voucher type {}."
165 | msgstr ""
166 |
167 | #: erpnext_datev/erpnext_datev/report/datev/datev.py:186
168 | msgid "Please create DATEV Settings for Company {}"
169 | msgstr ""
170 |
171 | #. Label of a Link field in DocType 'DATEV Voucher Config'
172 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
173 | msgid "Print Format"
174 | msgstr ""
175 |
176 | #. Label of a Data field in DocType 'DATEV Voucher Config'
177 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
178 | msgid "Recipient"
179 | msgstr ""
180 |
181 | #. Description of the 'Attach Files' (Check) field in DocType 'DATEV Voucher
182 | #. Config'
183 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
184 | msgid "Send files attached to the voucher"
185 | msgstr ""
186 |
187 | #. Description of the 'Attach Print' (Check) field in DocType 'DATEV Voucher
188 | #. Config'
189 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
190 | msgid "Send the voucher printed as PDF"
191 | msgstr ""
192 |
193 | #. Label of a Link field in DocType 'DATEV Unternehmen Online Settings'
194 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
195 | msgid "Sender"
196 | msgstr ""
197 |
198 | #. Name of a role
199 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
200 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
201 | msgid "System Manager"
202 | msgstr ""
203 |
204 | #. Label of a Data field in DocType 'DATEV Settings'
205 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
206 | msgid "Temporary Against Account Number"
207 | msgstr ""
208 |
209 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.py:15
210 | msgid "Temporary Against Account Number must be {0} digits long"
211 | msgstr ""
212 |
213 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:22
214 | msgid "To Date"
215 | msgstr ""
216 |
217 | #. Label of a Link field in DocType 'DATEV Voucher Config'
218 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
219 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:29
220 | msgid "Voucher Type"
221 | msgstr ""
222 |
223 | #. Description of the 'Temporary Against Account Number' (Data) field in
224 | #. DocType 'DATEV Settings'
225 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
226 | msgid "Will be used as against account for all normal ledger entries"
227 | msgstr ""
228 |
229 | #. Description of the 'Opening Against Account Number' (Data) field in DocType
230 | #. 'DATEV Settings'
231 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
232 | msgid "Will be used as against account for opening ledger entries"
233 | msgstr ""
234 |
235 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:50
236 | msgid "{} was not sent to DATEV because no attachments have been found."
237 | msgstr ""
238 |
239 |
--------------------------------------------------------------------------------
/erpnext_datev/locale/de.po:
--------------------------------------------------------------------------------
1 | # Translations template for ERPNext DATEV Integration.
2 | # Copyright (C) 2024 ALYF GmbH
3 | # This file is distributed under the same license as the ERPNext DATEV Integration project.
4 | # FIRST AUTHOR language field. If it doesn't have a language field or it is empty, the Default Print Language is used. If Default Print Language is empty, the language according to System Settings is used."
35 | msgstr "Ein Dokument wird entsprechend seinem Feld Sprache (language) gedruckt. Wenn das Feld Sprache nicht vorhanden oder leer ist, wird die Standarddrucksprache verwendet. Wenn das Feld Standarddrucksprache leer ist, wird die Sprache gemäß den Systemeinstellungen verwendet."
36 |
37 | #. Label of a Int field in DocType 'DATEV Settings'
38 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
39 | msgid "Account Number Length"
40 | msgstr "Kontonummerlänge"
41 |
42 | #. Name of a role
43 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
44 | msgid "Accounts Manager"
45 | msgstr ""
46 |
47 | #. Name of a role
48 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
49 | msgid "Accounts User"
50 | msgstr ""
51 |
52 | #. Label of a Check field in DocType 'DATEV Voucher Config'
53 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
54 | msgid "Attach Files"
55 | msgstr "Anhänge anfügen"
56 |
57 | #. Label of a Check field in DocType 'DATEV Voucher Config'
58 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
59 | msgid "Attach Print"
60 | msgstr "Druck anfügen"
61 |
62 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:55
63 | msgid "Change DATEV Settings"
64 | msgstr "DATEV-Einstellungen ändern"
65 |
66 | #. Label of a Link field in DocType 'DATEV Settings'
67 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
68 | msgid "Client"
69 | msgstr "Mandant"
70 |
71 | #. Label of a Data field in DocType 'DATEV Settings'
72 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
73 | msgid "Client ID"
74 | msgstr "Mandantennummer"
75 |
76 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:5
77 | msgid "Company"
78 | msgstr "Unternehmen"
79 |
80 | #. Label of a Link field in DocType 'DATEV Settings'
81 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
82 | msgid "Consultant"
83 | msgstr "Berater"
84 |
85 | #. Label of a Data field in DocType 'DATEV Settings'
86 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
87 | msgid "Consultant ID"
88 | msgstr "Beraternummer"
89 |
90 | #. Name of a report
91 | #: erpnext_datev/erpnext_datev/report/datev/datev.json
92 | msgid "DATEV"
93 | msgstr "DATEV"
94 |
95 | #. Name of a DocType
96 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
97 | msgid "DATEV Settings"
98 | msgstr "DATEV-Einstellungen"
99 |
100 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:40
101 | msgid "DATEV Settings for your Company are missing. Would you like to create them now?"
102 | msgstr "DATEV-Einstellungen für Ihr Unternehmen fehlen. Möchten Sie diese jetzt erstellen?"
103 |
104 | #: erpnext_datev/erpnext_datev/report/datev/datev.py:187
105 | msgid "DATEV Settings missing"
106 | msgstr "DATEV-Einstellungen fehlen"
107 |
108 | #. Name of a DocType
109 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
110 | msgid "DATEV Unternehmen Online Settings"
111 | msgstr "DATEV Unternehmen Online-Einstellungen"
112 |
113 | #. Label of a Table field in DocType 'DATEV Unternehmen Online Settings'
114 | #. Name of a DocType
115 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
116 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
117 | msgid "DATEV Voucher Config"
118 | msgstr "DATEV Belegkonfiguration"
119 |
120 | #: erpnext_datev/erpnext_datev/report/datev/datev.py:197
121 | msgid "Dates {} and {} are not in the same fiscal year."
122 | msgstr "Die Daten {} und {} liegen nicht im gleichen Geschäftsjahr."
123 |
124 | #. Label of a Link field in DocType 'DATEV Unternehmen Online Settings'
125 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
126 | msgid "Default Print Language"
127 | msgstr "Standarddrucksprache"
128 |
129 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:48
130 | msgid "Download DATEV File"
131 | msgstr "DATEV-Datei herunterladen"
132 |
133 | #: erpnext_datev/config/desktop.py:11
134 | msgid "ERPNext DATEV"
135 | msgstr "ERPNext-DATEV"
136 |
137 | #. Label of a Check field in DocType 'DATEV Unternehmen Online Settings'
138 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
139 | msgid "Enabled"
140 | msgstr "Aktiviert"
141 |
142 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:79
143 | msgid "Failed to attach XML to Sales Invoice PDF for DATEV"
144 | msgstr "Fehler beim Anfügen der XML-Datei zu einem PDF-Druck für DATEV"
145 |
146 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:15
147 | msgid "From Date"
148 | msgstr "Von Datum"
149 |
150 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:58
151 | msgid "New {0} {1} sent by the ERPNext-DATEV integration."
152 | msgstr "Neue {0} {1}, gesendet von der ERPNext-DATEV-Integration."
153 |
154 | #. Label of a Data field in DocType 'DATEV Settings'
155 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
156 | msgid "Opening Against Account Number"
157 | msgstr "Gegenkontonummer für Eröffnungsbuchungen"
158 |
159 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.py:25
160 | msgid "Opening Against Account Number must be {0} digits long"
161 | msgstr "Gegenkontonummer für Eröffnungsbuchungen muss {0} Ziffern lang sein"
162 |
163 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:16
164 | msgid "Please configure attachments for voucher type {}."
165 | msgstr "Bitte konfigurieren Sie Anhänge für Belegart {}."
166 |
167 | #: erpnext_datev/erpnext_datev/report/datev/datev.py:186
168 | msgid "Please create DATEV Settings for Company {}"
169 | msgstr "Bitte erstellen Sie DATEV-Einstellungen für Unternehmen {}"
170 |
171 | #. Label of a Link field in DocType 'DATEV Voucher Config'
172 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
173 | msgid "Print Format"
174 | msgstr ""
175 |
176 | #. Label of a Data field in DocType 'DATEV Voucher Config'
177 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
178 | msgid "Recipient"
179 | msgstr "Empfänger"
180 |
181 | #. Description of the 'Attach Files' (Check) field in DocType 'DATEV Voucher
182 | #. Config'
183 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
184 | msgid "Send files attached to the voucher"
185 | msgstr "Dateien anhängen, die zu dem Beleg gehören"
186 |
187 | #. Description of the 'Attach Print' (Check) field in DocType 'DATEV Voucher
188 | #. Config'
189 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
190 | msgid "Send the voucher printed as PDF"
191 | msgstr "Beleg als PDF senden"
192 |
193 | #. Label of a Link field in DocType 'DATEV Unternehmen Online Settings'
194 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
195 | msgid "Sender"
196 | msgstr "Absender"
197 |
198 | #. Name of a role
199 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
200 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.json
201 | msgid "System Manager"
202 | msgstr ""
203 |
204 | #. Label of a Data field in DocType 'DATEV Settings'
205 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
206 | msgid "Temporary Against Account Number"
207 | msgstr "Temporäre Gegenkontonummer"
208 |
209 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.py:15
210 | msgid "Temporary Against Account Number must be {0} digits long"
211 | msgstr "Temporäre Gegenkontonummer muss {0} Ziffern lang sein"
212 |
213 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:22
214 | msgid "To Date"
215 | msgstr "Bis Datum"
216 |
217 | #. Label of a Link field in DocType 'DATEV Voucher Config'
218 | #: erpnext_datev/erpnext_datev/doctype/datev_voucher_config/datev_voucher_config.json
219 | #: erpnext_datev/erpnext_datev/report/datev/datev.js:29
220 | msgid "Voucher Type"
221 | msgstr "Belegart"
222 |
223 | #. Description of the 'Temporary Against Account Number' (Data) field in
224 | #. DocType 'DATEV Settings'
225 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
226 | msgid "Will be used as against account for all normal ledger entries"
227 | msgstr "Wird als Gegenkontonummer für alle normalen Buchungen verwendet"
228 |
229 | #. Description of the 'Opening Against Account Number' (Data) field in DocType
230 | #. 'DATEV Settings'
231 | #: erpnext_datev/erpnext_datev/doctype/datev_settings/datev_settings.json
232 | msgid "Will be used as against account for opening ledger entries"
233 | msgstr "Wird als Gegenkontonummer für Eröffnungsbuchungen verwendet"
234 |
235 | #: erpnext_datev/erpnext_datev/doctype/datev_unternehmen_online_settings/datev_unternehmen_online_settings.py:50
236 | msgid "{} was not sent to DATEV because no attachments have been found."
237 | msgstr "{} wurde nicht an DATEV gesendet weil keine Anhänge gefunden wurden."
238 |
239 |
--------------------------------------------------------------------------------
/erpnext_datev/utils/datev_constants.py:
--------------------------------------------------------------------------------
1 | """Constants used in datev.py."""
2 |
3 | TRANSACTION_COLUMNS = [
4 | # All possible columns must tbe listed here, because DATEV requires them to
5 | # be present in the CSV.
6 | # ---
7 | # Umsatz
8 | "Umsatz (ohne Soll/Haben-Kz)",
9 | "Soll/Haben-Kennzeichen",
10 | "WKZ Umsatz",
11 | "Kurs",
12 | "Basis-Umsatz",
13 | "WKZ Basis-Umsatz",
14 | # Konto/Gegenkonto
15 | "Konto",
16 | "Gegenkonto (ohne BU-Schlüssel)",
17 | "BU-Schlüssel",
18 | # Datum
19 | "Belegdatum",
20 | # Rechnungs- / Belegnummer
21 | "Belegfeld 1",
22 | # z.B. Fälligkeitsdatum Format: TTMMJJ
23 | "Belegfeld 2",
24 | # Skonto-Betrag / -Abzug (Der Wert 0 ist unzulässig)
25 | "Skonto",
26 | # Beschreibung des Buchungssatzes
27 | "Buchungstext",
28 | # Mahn- / Zahl-Sperre (1 = Postensperre)
29 | "Postensperre",
30 | "Diverse Adressnummer",
31 | "Geschäftspartnerbank",
32 | "Sachverhalt",
33 | # Keine Mahnzinsen
34 | "Zinssperre",
35 | # Link auf den Buchungsbeleg (Programmkürzel + GUID)
36 | "Beleglink",
37 | # Beleginfo
38 | "Beleginfo - Art 1",
39 | "Beleginfo - Inhalt 1",
40 | "Beleginfo - Art 2",
41 | "Beleginfo - Inhalt 2",
42 | "Beleginfo - Art 3",
43 | "Beleginfo - Inhalt 3",
44 | "Beleginfo - Art 4",
45 | "Beleginfo - Inhalt 4",
46 | "Beleginfo - Art 5",
47 | "Beleginfo - Inhalt 5",
48 | "Beleginfo - Art 6",
49 | "Beleginfo - Inhalt 6",
50 | "Beleginfo - Art 7",
51 | "Beleginfo - Inhalt 7",
52 | "Beleginfo - Art 8",
53 | "Beleginfo - Inhalt 8",
54 | # Zuordnung des Geschäftsvorfalls für die Kostenrechnung
55 | "KOST1 - Kostenstelle",
56 | "KOST2 - Kostenstelle",
57 | "KOST-Menge",
58 | # USt-ID-Nummer (Beispiel: DE133546770)
59 | "EU-Mitgliedstaat u. USt-IdNr.",
60 | # Der im EU-Bestimmungsland gültige Steuersatz
61 | "EU-Steuersatz",
62 | # I = Ist-Versteuerung,
63 | # K = keine Umsatzsteuerrechnung
64 | # P = Pauschalierung (z. B. für Land- und Forstwirtschaft),
65 | # S = Soll-Versteuerung
66 | "Abw. Versteuerungsart",
67 | # Sachverhalte gem. § 13b Abs. 1 Satz 1 Nrn. 1.-5. UStG
68 | "Sachverhalt L+L",
69 | # Steuersatz / Funktion zum L+L-Sachverhalt (Beispiel: Wert 190 für 19%)
70 | "Funktionsergänzung L+L",
71 | # Bei Verwendung des BU-Schlüssels 49 für „andere Steuersätze“ muss der
72 | # steuerliche Sachverhalt mitgegeben werden
73 | "BU 49 Hauptfunktionstyp",
74 | "BU 49 Hauptfunktionsnummer",
75 | "BU 49 Funktionsergänzung",
76 | # Zusatzinformationen, besitzen den Charakter eines Notizzettels und können
77 | # frei erfasst werden.
78 | "Zusatzinformation - Art 1",
79 | "Zusatzinformation - Inhalt 1",
80 | "Zusatzinformation - Art 2",
81 | "Zusatzinformation - Inhalt 2",
82 | "Zusatzinformation - Art 3",
83 | "Zusatzinformation - Inhalt 3",
84 | "Zusatzinformation - Art 4",
85 | "Zusatzinformation - Inhalt 4",
86 | "Zusatzinformation - Art 5",
87 | "Zusatzinformation - Inhalt 5",
88 | "Zusatzinformation - Art 6",
89 | "Zusatzinformation - Inhalt 6",
90 | "Zusatzinformation - Art 7",
91 | "Zusatzinformation - Inhalt 7",
92 | "Zusatzinformation - Art 8",
93 | "Zusatzinformation - Inhalt 8",
94 | "Zusatzinformation - Art 9",
95 | "Zusatzinformation - Inhalt 9",
96 | "Zusatzinformation - Art 10",
97 | "Zusatzinformation - Inhalt 10",
98 | "Zusatzinformation - Art 11",
99 | "Zusatzinformation - Inhalt 11",
100 | "Zusatzinformation - Art 12",
101 | "Zusatzinformation - Inhalt 12",
102 | "Zusatzinformation - Art 13",
103 | "Zusatzinformation - Inhalt 13",
104 | "Zusatzinformation - Art 14",
105 | "Zusatzinformation - Inhalt 14",
106 | "Zusatzinformation - Art 15",
107 | "Zusatzinformation - Inhalt 15",
108 | "Zusatzinformation - Art 16",
109 | "Zusatzinformation - Inhalt 16",
110 | "Zusatzinformation - Art 17",
111 | "Zusatzinformation - Inhalt 17",
112 | "Zusatzinformation - Art 18",
113 | "Zusatzinformation - Inhalt 18",
114 | "Zusatzinformation - Art 19",
115 | "Zusatzinformation - Inhalt 19",
116 | "Zusatzinformation - Art 20",
117 | "Zusatzinformation - Inhalt 20",
118 | # Wirkt sich nur bei Sachverhalt mit SKR 14 Land- und Forstwirtschaft aus,
119 | # für andere SKR werden die Felder beim Import / Export überlesen bzw.
120 | # leer exportiert.
121 | "Stück",
122 | "Gewicht",
123 | # 1 = Lastschrift
124 | # 2 = Mahnung
125 | # 3 = Zahlung
126 | "Zahlweise",
127 | "Forderungsart",
128 | # JJJJ
129 | "Veranlagungsjahr",
130 | # TTMMJJJJ
131 | "Zugeordnete Fälligkeit",
132 | # 1 = Einkauf von Waren
133 | # 2 = Erwerb von Roh-Hilfs- und Betriebsstoffen
134 | "Skontotyp",
135 | # Allgemeine Bezeichnung, des Auftrags / Projekts.
136 | "Auftragsnummer",
137 | # AA = Angeforderte Anzahlung / Abschlagsrechnung
138 | # AG = Erhaltene Anzahlung (Geldeingang)
139 | # AV = Erhaltene Anzahlung (Verbindlichkeit)
140 | # SR = Schlussrechnung
141 | # SU = Schlussrechnung (Umbuchung)
142 | # SG = Schlussrechnung (Geldeingang)
143 | # SO = Sonstige
144 | "Buchungstyp",
145 | "USt-Schlüssel (Anzahlungen)",
146 | "EU-Mitgliedstaat (Anzahlungen)",
147 | "Sachverhalt L+L (Anzahlungen)",
148 | "EU-Steuersatz (Anzahlungen)",
149 | "Erlöskonto (Anzahlungen)",
150 | # Wird beim Import durch SV (Stapelverarbeitung) ersetzt.
151 | "Herkunft-Kz",
152 | # Wird von DATEV verwendet.
153 | "Leerfeld",
154 | # Format TTMMJJJJ
155 | "KOST-Datum",
156 | # Vom Zahlungsempfänger individuell vergebenes Kennzeichen eines Mandats
157 | # (z.B. Rechnungs- oder Kundennummer).
158 | "SEPA-Mandatsreferenz",
159 | # 1 = Skontosperre
160 | # 0 = Keine Skontosperre
161 | "Skontosperre",
162 | # Gesellschafter und Sonderbilanzsachverhalt
163 | "Gesellschaftername",
164 | # Amtliche Nummer aus der Feststellungserklärung
165 | "Beteiligtennummer",
166 | "Identifikationsnummer",
167 | "Zeichnernummer",
168 | # Format TTMMJJJJ
169 | "Postensperre bis",
170 | # Gesellschafter und Sonderbilanzsachverhalt
171 | "Bezeichnung SoBil-Sachverhalt",
172 | "Kennzeichen SoBil-Buchung",
173 | # 0 = keine Festschreibung
174 | # 1 = Festschreibung
175 | "Festschreibung",
176 | # Format TTMMJJJJ
177 | "Leistungsdatum",
178 | # Format TTMMJJJJ
179 | "Datum Zuord. Steuerperiode",
180 | # OPOS-Informationen, Format TTMMJJJJ
181 | "Fälligkeit",
182 | # G oder 1 = Generalumkehr
183 | # 0 = keine Generalumkehr
184 | "Generalumkehr (GU)",
185 | # Steuersatz für Steuerschlüssel
186 | "Steuersatz",
187 | # Beispiel: DE für Deutschland
188 | "Land",
189 | ]
190 |
191 | DEBTOR_CREDITOR_COLUMNS = [
192 | # All possible columns must tbe listed here, because DATEV requires them to
193 | # be present in the CSV.
194 | # Columns "Leerfeld" have been replaced with "Leerfeld #" to not confuse pandas
195 | # ---
196 | "Konto",
197 | "Name (Adressatentyp Unternehmen)",
198 | "Unternehmensgegenstand",
199 | "Name (Adressatentyp natürl. Person)",
200 | "Vorname (Adressatentyp natürl. Person)",
201 | "Name (Adressatentyp keine Angabe)",
202 | "Adressatentyp",
203 | "Kurzbezeichnung",
204 | "EU-Land",
205 | "EU-USt-IdNr.",
206 | "Anrede",
207 | "Titel/Akad. Grad",
208 | "Adelstitel",
209 | "Namensvorsatz",
210 | "Adressart",
211 | "Straße",
212 | "Postfach",
213 | "Postleitzahl",
214 | "Ort",
215 | "Land",
216 | "Versandzusatz",
217 | "Adresszusatz",
218 | "Abweichende Anrede",
219 | "Abw. Zustellbezeichnung 1",
220 | "Abw. Zustellbezeichnung 2",
221 | "Kennz. Korrespondenzadresse",
222 | "Adresse gültig von",
223 | "Adresse gültig bis",
224 | "Telefon",
225 | "Bemerkung (Telefon)",
226 | "Telefon Geschäftsleitung",
227 | "Bemerkung (Telefon GL)",
228 | "E-Mail",
229 | "Bemerkung (E-Mail)",
230 | "Internet",
231 | "Bemerkung (Internet)",
232 | "Fax",
233 | "Bemerkung (Fax)",
234 | "Sonstige",
235 | "Bemerkung (Sonstige)",
236 | "Bankleitzahl 1",
237 | "Bankbezeichnung 1",
238 | "Bankkonto-Nummer 1",
239 | "Länderkennzeichen 1",
240 | "IBAN 1",
241 | "Leerfeld 1",
242 | "SWIFT-Code 1",
243 | "Abw. Kontoinhaber 1",
244 | "Kennz. Haupt-Bankverb. 1",
245 | "Bankverb. 1 Gültig von",
246 | "Bankverb. 1 Gültig bis",
247 | "Bankleitzahl 2",
248 | "Bankbezeichnung 2",
249 | "Bankkonto-Nummer 2",
250 | "Länderkennzeichen 2",
251 | "IBAN 2",
252 | "Leerfeld 2",
253 | "SWIFT-Code 2",
254 | "Abw. Kontoinhaber 2",
255 | "Kennz. Haupt-Bankverb. 2",
256 | "Bankverb. 2 gültig von",
257 | "Bankverb. 2 gültig bis",
258 | "Bankleitzahl 3",
259 | "Bankbezeichnung 3",
260 | "Bankkonto-Nummer 3",
261 | "Länderkennzeichen 3",
262 | "IBAN 3",
263 | "Leerfeld 3",
264 | "SWIFT-Code 3",
265 | "Abw. Kontoinhaber 3",
266 | "Kennz. Haupt-Bankverb. 3",
267 | "Bankverb. 3 gültig von",
268 | "Bankverb. 3 gültig bis",
269 | "Bankleitzahl 4",
270 | "Bankbezeichnung 4",
271 | "Bankkonto-Nummer 4",
272 | "Länderkennzeichen 4",
273 | "IBAN 4",
274 | "Leerfeld 4",
275 | "SWIFT-Code 4",
276 | "Abw. Kontoinhaber 4",
277 | "Kennz. Haupt-Bankverb. 4",
278 | "Bankverb. 4 Gültig von",
279 | "Bankverb. 4 Gültig bis",
280 | "Bankleitzahl 5",
281 | "Bankbezeichnung 5",
282 | "Bankkonto-Nummer 5",
283 | "Länderkennzeichen 5",
284 | "IBAN 5",
285 | "Leerfeld 5",
286 | "SWIFT-Code 5",
287 | "Abw. Kontoinhaber 5",
288 | "Kennz. Haupt-Bankverb. 5",
289 | "Bankverb. 5 gültig von",
290 | "Bankverb. 5 gültig bis",
291 | "Leerfeld 6",
292 | "Briefanrede",
293 | "Grußformel",
294 | "Kundennummer",
295 | "Steuernummer",
296 | "Sprache",
297 | "Ansprechpartner",
298 | "Vertreter",
299 | "Sachbearbeiter",
300 | "Diverse-Konto",
301 | "Ausgabeziel",
302 | "Währungssteuerung",
303 | "Kreditlimit (Debitor)",
304 | "Zahlungsbedingung",
305 | "Fälligkeit in Tagen (Debitor)",
306 | "Skonto in Prozent (Debitor)",
307 | "Kreditoren-Ziel 1 (Tage)",
308 | "Kreditoren-Skonto 1 (%)",
309 | "Kreditoren-Ziel 2 (Tage)",
310 | "Kreditoren-Skonto 2 (%)",
311 | "Kreditoren-Ziel 3 Brutto (Tage)",
312 | "Kreditoren-Ziel 4 (Tage)",
313 | "Kreditoren-Skonto 4 (%)",
314 | "Kreditoren-Ziel 5 (Tage)",
315 | "Kreditoren-Skonto 5 (%)",
316 | "Mahnung",
317 | "Kontoauszug",
318 | "Mahntext 1",
319 | "Mahntext 2",
320 | "Mahntext 3",
321 | "Kontoauszugstext",
322 | "Mahnlimit Betrag",
323 | "Mahnlimit %",
324 | "Zinsberechnung",
325 | "Mahnzinssatz 1",
326 | "Mahnzinssatz 2",
327 | "Mahnzinssatz 3",
328 | "Lastschrift",
329 | "Verfahren",
330 | "Mandantenbank",
331 | "Zahlungsträger",
332 | "Indiv. Feld 1",
333 | "Indiv. Feld 2",
334 | "Indiv. Feld 3",
335 | "Indiv. Feld 4",
336 | "Indiv. Feld 5",
337 | "Indiv. Feld 6",
338 | "Indiv. Feld 7",
339 | "Indiv. Feld 8",
340 | "Indiv. Feld 9",
341 | "Indiv. Feld 10",
342 | "Indiv. Feld 11",
343 | "Indiv. Feld 12",
344 | "Indiv. Feld 13",
345 | "Indiv. Feld 14",
346 | "Indiv. Feld 15",
347 | "Abweichende Anrede (Rechnungsadresse)",
348 | "Adressart (Rechnungsadresse)",
349 | "Straße (Rechnungsadresse)",
350 | "Postfach (Rechnungsadresse)",
351 | "Postleitzahl (Rechnungsadresse)",
352 | "Ort (Rechnungsadresse)",
353 | "Land (Rechnungsadresse)",
354 | "Versandzusatz (Rechnungsadresse)",
355 | "Adresszusatz (Rechnungsadresse)",
356 | "Abw. Zustellbezeichnung 1 (Rechnungsadresse)",
357 | "Abw. Zustellbezeichnung 2 (Rechnungsadresse)",
358 | "Adresse Gültig von (Rechnungsadresse)",
359 | "Adresse Gültig bis (Rechnungsadresse)",
360 | "Bankleitzahl 6",
361 | "Bankbezeichnung 6",
362 | "Bankkonto-Nummer 6",
363 | "Länderkennzeichen 6",
364 | "IBAN 6",
365 | "Leerfeld 7",
366 | "SWIFT-Code 6",
367 | "Abw. Kontoinhaber 6",
368 | "Kennz. Haupt-Bankverb. 6",
369 | "Bankverb 6 gültig von",
370 | "Bankverb 6 gültig bis",
371 | "Bankleitzahl 7",
372 | "Bankbezeichnung 7",
373 | "Bankkonto-Nummer 7",
374 | "Länderkennzeichen 7",
375 | "IBAN 7",
376 | "Leerfeld 8",
377 | "SWIFT-Code 7",
378 | "Abw. Kontoinhaber 7",
379 | "Kennz. Haupt-Bankverb. 7",
380 | "Bankverb 7 gültig von",
381 | "Bankverb 7 gültig bis",
382 | "Bankleitzahl 8",
383 | "Bankbezeichnung 8",
384 | "Bankkonto-Nummer 8",
385 | "Länderkennzeichen 8",
386 | "IBAN 8",
387 | "Leerfeld 9",
388 | "SWIFT-Code 8",
389 | "Abw. Kontoinhaber 8",
390 | "Kennz. Haupt-Bankverb. 8",
391 | "Bankverb 8 gültig von",
392 | "Bankverb 8 gültig bis",
393 | "Bankleitzahl 9",
394 | "Bankbezeichnung 9",
395 | "Bankkonto-Nummer 9",
396 | "Länderkennzeichen 9",
397 | "IBAN 9",
398 | "Leerfeld 10",
399 | "SWIFT-Code 9",
400 | "Abw. Kontoinhaber 9",
401 | "Kennz. Haupt-Bankverb. 9",
402 | "Bankverb 9 gültig von",
403 | "Bankverb 9 gültig bis",
404 | "Bankleitzahl 10",
405 | "Bankbezeichnung 10",
406 | "Bankkonto-Nummer 10",
407 | "Länderkennzeichen 10",
408 | "IBAN 10",
409 | "Leerfeld 11",
410 | "SWIFT-Code 10",
411 | "Abw. Kontoinhaber 10",
412 | "Kennz. Haupt-Bankverb. 10",
413 | "Bankverb 10 gültig von",
414 | "Bankverb 10 gültig bis",
415 | "Nummer Fremdsystem",
416 | "Insolvent",
417 | "SEPA-Mandatsreferenz 1",
418 | "SEPA-Mandatsreferenz 2",
419 | "SEPA-Mandatsreferenz 3",
420 | "SEPA-Mandatsreferenz 4",
421 | "SEPA-Mandatsreferenz 5",
422 | "SEPA-Mandatsreferenz 6",
423 | "SEPA-Mandatsreferenz 7",
424 | "SEPA-Mandatsreferenz 8",
425 | "SEPA-Mandatsreferenz 9",
426 | "SEPA-Mandatsreferenz 10",
427 | "Verknüpftes OPOS-Konto",
428 | "Mahnsperre bis",
429 | "Lastschriftsperre bis",
430 | "Zahlungssperre bis",
431 | "Gebührenberechnung",
432 | "Mahngebühr 1",
433 | "Mahngebühr 2",
434 | "Mahngebühr 3",
435 | "Pauschalberechnung",
436 | "Verzugspauschale 1",
437 | "Verzugspauschale 2",
438 | "Verzugspauschale 3",
439 | "Alternativer Suchname",
440 | "Status",
441 | "Anschrift manuell geändert (Korrespondenzadresse)",
442 | "Anschrift individuell (Korrespondenzadresse)",
443 | "Anschrift manuell geändert (Rechnungsadresse)",
444 | "Anschrift individuell (Rechnungsadresse)",
445 | "Fristberechnung bei Debitor",
446 | "Mahnfrist 1",
447 | "Mahnfrist 2",
448 | "Mahnfrist 3",
449 | "Letzte Frist",
450 | ]
451 |
452 | ACCOUNT_NAME_COLUMNS = [
453 | # Account number
454 | "Konto",
455 | # Account name
456 | "Kontenbeschriftung",
457 | # Language of the account name
458 | # "de-DE" or "en-GB"
459 | "Sprach-ID",
460 | ]
461 |
462 |
463 | class DataCategory:
464 | """Field of the CSV Header."""
465 |
466 | DEBTORS_CREDITORS = "16"
467 | ACCOUNT_NAMES = "20"
468 | TRANSACTIONS = "21"
469 | POSTING_TEXT_CONSTANTS = "67"
470 |
471 |
472 | class FormatName:
473 | """Field of the CSV Header, corresponds to DataCategory."""
474 |
475 | DEBTORS_CREDITORS = "Debitoren/Kreditoren"
476 | ACCOUNT_NAMES = "Kontenbeschriftungen"
477 | TRANSACTIONS = "Buchungsstapel"
478 | POSTING_TEXT_CONSTANTS = "Buchungstextkonstanten"
479 |
480 |
481 | class Transactions:
482 | DATA_CATEGORY = DataCategory.TRANSACTIONS
483 | FORMAT_NAME = FormatName.TRANSACTIONS
484 | FORMAT_VERSION = "9"
485 | COLUMNS = TRANSACTION_COLUMNS
486 |
487 |
488 | class DebtorsCreditors:
489 | DATA_CATEGORY = DataCategory.DEBTORS_CREDITORS
490 | FORMAT_NAME = FormatName.DEBTORS_CREDITORS
491 | FORMAT_VERSION = "5"
492 | COLUMNS = DEBTOR_CREDITOR_COLUMNS
493 |
494 |
495 | class AccountNames:
496 | DATA_CATEGORY = DataCategory.ACCOUNT_NAMES
497 | FORMAT_NAME = FormatName.ACCOUNT_NAMES
498 | FORMAT_VERSION = "2"
499 | COLUMNS = ACCOUNT_NAME_COLUMNS
500 |
--------------------------------------------------------------------------------
/erpnext_datev/erpnext_datev/report/datev/datev.py:
--------------------------------------------------------------------------------
1 | """
2 | Provide a report and downloadable CSV according to the German DATEV format.
3 |
4 | - Query report showing only the columns that contain data, formatted nicely for
5 | dispay to the user.
6 | - CSV download functionality `download_datev_csv` that provides a CSV file with
7 | all required columns. Used to import the data into the DATEV Software.
8 | """
9 |
10 | import json
11 |
12 | import frappe
13 | from erpnext.accounts.utils import get_fiscal_year
14 | from frappe import _
15 |
16 | from erpnext_datev.utils.datev_constants import (
17 | AccountNames,
18 | DebtorsCreditors,
19 | Transactions,
20 | )
21 | from erpnext_datev.utils.datev_csv import get_datev_csv, zip_and_download
22 |
23 | COLUMNS = [
24 | {
25 | "label": "Umsatz (ohne Soll/Haben-Kz)",
26 | "fieldname": "Umsatz (ohne Soll/Haben-Kz)",
27 | "fieldtype": "Currency",
28 | "width": 100,
29 | },
30 | {
31 | "label": "Soll/Haben-Kennzeichen",
32 | "fieldname": "Soll/Haben-Kennzeichen",
33 | "fieldtype": "Data",
34 | "width": 100,
35 | },
36 | {"label": "Konto", "fieldname": "Konto", "fieldtype": "Data", "width": 100},
37 | {
38 | "label": "Gegenkonto (ohne BU-Schlüssel)",
39 | "fieldname": "Gegenkonto (ohne BU-Schlüssel)",
40 | "fieldtype": "Data",
41 | "width": 100,
42 | },
43 | {
44 | "label": "BU-Schlüssel",
45 | "fieldname": "BU-Schlüssel",
46 | "fieldtype": "Data",
47 | "width": 100,
48 | },
49 | {
50 | "label": "Belegdatum",
51 | "fieldname": "Belegdatum",
52 | "fieldtype": "Date",
53 | "width": 100,
54 | },
55 | {
56 | "label": "Belegfeld 1",
57 | "fieldname": "Belegfeld 1",
58 | "fieldtype": "Data",
59 | "width": 150,
60 | },
61 | {
62 | "label": "Buchungstext",
63 | "fieldname": "Buchungstext",
64 | "fieldtype": "Text",
65 | "width": 300,
66 | },
67 | {
68 | "label": "Beleginfo - Art 1",
69 | "fieldname": "Beleginfo - Art 1",
70 | "fieldtype": "Link",
71 | "options": "DocType",
72 | "width": 100,
73 | },
74 | {
75 | "label": "Beleginfo - Inhalt 1",
76 | "fieldname": "Beleginfo - Inhalt 1",
77 | "fieldtype": "Dynamic Link",
78 | "options": "Beleginfo - Art 1",
79 | "width": 150,
80 | },
81 | {
82 | "label": "Beleginfo - Art 2",
83 | "fieldname": "Beleginfo - Art 2",
84 | "fieldtype": "Link",
85 | "options": "DocType",
86 | "width": 100,
87 | },
88 | {
89 | "label": "Beleginfo - Inhalt 2",
90 | "fieldname": "Beleginfo - Inhalt 2",
91 | "fieldtype": "Dynamic Link",
92 | "options": "Beleginfo - Art 2",
93 | "width": 150,
94 | },
95 | {
96 | "label": "Beleginfo - Art 3",
97 | "fieldname": "Beleginfo - Art 3",
98 | "fieldtype": "Link",
99 | "options": "DocType",
100 | "width": 100,
101 | },
102 | {
103 | "label": "Beleginfo - Inhalt 3",
104 | "fieldname": "Beleginfo - Inhalt 3",
105 | "fieldtype": "Dynamic Link",
106 | "options": "Beleginfo - Art 3",
107 | "width": 150,
108 | },
109 | {
110 | "label": "Beleginfo - Art 4",
111 | "fieldname": "Beleginfo - Art 4",
112 | "fieldtype": "Data",
113 | "width": 100,
114 | },
115 | {
116 | "label": "Beleginfo - Inhalt 4",
117 | "fieldname": "Beleginfo - Inhalt 4",
118 | "fieldtype": "Data",
119 | "width": 150,
120 | },
121 | {
122 | "label": "Beleginfo - Art 5",
123 | "fieldname": "Beleginfo - Art 5",
124 | "fieldtype": "Data",
125 | "width": 150,
126 | },
127 | {
128 | "label": "Beleginfo - Inhalt 5",
129 | "fieldname": "Beleginfo - Inhalt 5",
130 | "fieldtype": "Data",
131 | "width": 100,
132 | },
133 | {
134 | "label": "Beleginfo - Art 6",
135 | "fieldname": "Beleginfo - Art 6",
136 | "fieldtype": "Data",
137 | "width": 150,
138 | },
139 | {
140 | "label": "Beleginfo - Inhalt 6",
141 | "fieldname": "Beleginfo - Inhalt 6",
142 | "fieldtype": "Date",
143 | "width": 100,
144 | },
145 | {
146 | "label": "Fälligkeit",
147 | "fieldname": "Fälligkeit",
148 | "fieldtype": "Date",
149 | "width": 100,
150 | },
151 | ]
152 |
153 |
154 | def execute(filters=None):
155 | """Entry point for frappe."""
156 | data = []
157 | if filters and validate(filters):
158 | temp, opening = frappe.get_value(
159 | "DATEV Settings",
160 | filters.get("company"),
161 | ["temporary_against_account_number", "opening_against_account_number"],
162 | )
163 | filters.update({"against_account": temp, "opening_account": opening or temp})
164 | data = get_transactions(filters, as_dict=0)
165 |
166 | return COLUMNS, data
167 |
168 |
169 | def validate(filters):
170 | """Make sure all mandatory filters and settings are present."""
171 | company = filters.get("company")
172 | if not company:
173 | frappe.throw(_("Company is a mandatory filter."))
174 |
175 | from_date = filters.get("from_date")
176 | if not from_date:
177 | frappe.throw(_("From Date is a mandatory filter."))
178 |
179 | to_date = filters.get("to_date")
180 | if not to_date:
181 | frappe.throw(_("To Date is a mandatory filter."))
182 |
183 | validate_fiscal_year(from_date, to_date, company)
184 |
185 | if not frappe.db.exists("DATEV Settings", filters.get("company")):
186 | msg = _("Please create DATEV Settings for Company {}").format(filters.get("company"))
187 | frappe.log_error(message=msg, title=_("DATEV Settings missing"))
188 | return False
189 |
190 | return True
191 |
192 |
193 | def validate_fiscal_year(from_date, to_date, company):
194 | from_fiscal_year = get_fiscal_year(date=from_date, company=company)
195 | to_fiscal_year = get_fiscal_year(date=to_date, company=company)
196 | if from_fiscal_year != to_fiscal_year:
197 | frappe.throw(_("Dates {} and {} are not in the same fiscal year.").format(from_date, to_date))
198 |
199 |
200 | def get_transactions(filters, as_dict=1):
201 | def run(params_method, filters):
202 | extra_fields, extra_joins, extra_filters = params_method(filters)
203 | return run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=as_dict)
204 |
205 | def sort_by(row):
206 | # "Belegdatum" is in the fifth column when list format is used
207 | return row["Belegdatum" if as_dict else 5]
208 |
209 | type_map = {
210 | # specific query methods for some voucher types
211 | "Payment Entry": get_payment_entry_params,
212 | "Sales Invoice": get_sales_invoice_params,
213 | "Purchase Invoice": get_purchase_invoice_params,
214 | }
215 |
216 | only_voucher_type = filters.get("voucher_type")
217 | transactions = []
218 |
219 | for voucher_type, get_voucher_params in type_map.items():
220 | if only_voucher_type and only_voucher_type != voucher_type:
221 | continue
222 |
223 | transactions.extend(run(params_method=get_voucher_params, filters=filters))
224 |
225 | if not only_voucher_type or only_voucher_type not in type_map:
226 | # generic query method for all other voucher types
227 | filters["exclude_voucher_types"] = type_map.keys()
228 | transactions.extend(run(params_method=get_generic_params, filters=filters))
229 |
230 | return sorted(transactions, key=sort_by)
231 |
232 |
233 | def get_payment_entry_params(filters):
234 | extra_fields = """
235 | , 'Zahlungsreferenz' as 'Beleginfo - Art 5'
236 | , pe.reference_no as 'Beleginfo - Inhalt 5'
237 | , 'Buchungstag' as 'Beleginfo - Art 6'
238 | , pe.reference_date as 'Beleginfo - Inhalt 6'
239 | , '' as 'Fälligkeit'
240 | """
241 |
242 | extra_joins = """
243 | LEFT JOIN `tabPayment Entry` pe
244 | ON gl.voucher_no = pe.name
245 | """
246 |
247 | extra_filters = """
248 | AND gl.voucher_type = 'Payment Entry'
249 | """
250 |
251 | return extra_fields, extra_joins, extra_filters
252 |
253 |
254 | def get_sales_invoice_params(filters):
255 | extra_fields = """
256 | , '' as 'Beleginfo - Art 5'
257 | , '' as 'Beleginfo - Inhalt 5'
258 | , '' as 'Beleginfo - Art 6'
259 | , '' as 'Beleginfo - Inhalt 6'
260 | , si.due_date as 'Fälligkeit'
261 | """
262 |
263 | extra_joins = """
264 | LEFT JOIN `tabSales Invoice` si
265 | ON gl.voucher_no = si.name
266 | """
267 |
268 | extra_filters = """
269 | AND gl.voucher_type = 'Sales Invoice'
270 | """
271 |
272 | return extra_fields, extra_joins, extra_filters
273 |
274 |
275 | def get_purchase_invoice_params(filters):
276 | extra_fields = """
277 | , 'Lieferanten-Rechnungsnummer' as 'Beleginfo - Art 5'
278 | , pi.bill_no as 'Beleginfo - Inhalt 5'
279 | , 'Lieferanten-Rechnungsdatum' as 'Beleginfo - Art 6'
280 | , pi.bill_date as 'Beleginfo - Inhalt 6'
281 | , pi.due_date as 'Fälligkeit'
282 | """
283 |
284 | extra_joins = """
285 | LEFT JOIN `tabPurchase Invoice` pi
286 | ON gl.voucher_no = pi.name
287 | """
288 |
289 | extra_filters = """
290 | AND gl.voucher_type = 'Purchase Invoice'
291 | """
292 |
293 | return extra_fields, extra_joins, extra_filters
294 |
295 |
296 | def get_generic_params(filters):
297 | # produce empty fields so all rows will have the same length
298 | extra_fields = """
299 | , '' as 'Beleginfo - Art 5'
300 | , '' as 'Beleginfo - Inhalt 5'
301 | , '' as 'Beleginfo - Art 6'
302 | , '' as 'Beleginfo - Inhalt 6'
303 | , '' as 'Fälligkeit'
304 | """
305 | extra_joins = ""
306 |
307 | if filters.get("exclude_voucher_types"):
308 | # exclude voucher types that are queried by a dedicated method
309 | exclude = "({})".format(", ".join("'{}'".format(key) for key in filters.get("exclude_voucher_types")))
310 | extra_filters = "AND gl.voucher_type NOT IN {}".format(exclude)
311 |
312 | # if voucher type filter is set, allow only this type
313 | if filters.get("voucher_type"):
314 | extra_filters += " AND gl.voucher_type = %(voucher_type)s"
315 |
316 | return extra_fields, extra_joins, extra_filters
317 |
318 |
319 | def run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=1):
320 | """
321 | Get a list of accounting entries.
322 |
323 | Select GL Entries joined with Account and Party Account in order to get the
324 | account numbers. Returns a list of accounting entries.
325 |
326 | Arguments:
327 | filters -- dict of filters to be passed to the sql query
328 | as_dict -- return as list of dicts [0,1]
329 | """
330 | query = """
331 | SELECT
332 |
333 | /* either debit or credit amount; always positive */
334 | case ROUND(gl.debit, 2) when 0 then ROUND(gl.credit, 2) else ROUND(gl.debit, 2) end as 'Umsatz (ohne Soll/Haben-Kz)',
335 |
336 | /* 'H' when credit, 'S' when debit */
337 | case ROUND(gl.debit, 2) when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
338 |
339 | /* account number or, if empty, party account number */
340 | acc.account_number as 'Konto',
341 |
342 | /* against number or, if empty, party against number */
343 | CASE gl.is_opening when 'Yes' then %(opening_account)s else %(against_account)s end as 'Gegenkonto (ohne BU-Schlüssel)',
344 |
345 | /* disable automatic VAT deduction */
346 | '' as 'BU-Schlüssel',
347 |
348 | gl.posting_date as 'Belegdatum',
349 | gl.voucher_no as 'Belegfeld 1',
350 | REPLACE(LEFT(gl.remarks, 60), '\n', ' ') as 'Buchungstext',
351 | gl.voucher_type as 'Beleginfo - Art 1',
352 | gl.voucher_no as 'Beleginfo - Inhalt 1',
353 | gl.against_voucher_type as 'Beleginfo - Art 2',
354 | gl.against_voucher as 'Beleginfo - Inhalt 2',
355 | gl.party_type as 'Beleginfo - Art 3',
356 | gl.party as 'Beleginfo - Inhalt 3',
357 | case gl.party_type when 'Customer' then 'Debitorennummer' when 'Supplier' then 'Kreditorennummer' else NULL end as 'Beleginfo - Art 4',
358 | par.debtor_creditor_number as 'Beleginfo - Inhalt 4'
359 |
360 | {extra_fields}
361 |
362 | FROM `tabGL Entry` gl
363 |
364 | /* Kontonummer */
365 | LEFT JOIN `tabAccount` acc
366 | ON gl.account = acc.name
367 |
368 | LEFT JOIN `tabParty Account` par
369 | ON par.parent = gl.party
370 | AND par.parenttype = gl.party_type
371 | AND par.company = %(company)s
372 |
373 | {extra_joins}
374 |
375 | WHERE gl.company = %(company)s
376 | AND DATE(gl.posting_date) >= %(from_date)s
377 | AND DATE(gl.posting_date) <= %(to_date)s
378 |
379 | {extra_filters}
380 |
381 | ORDER BY 'Belegdatum', gl.voucher_no""".format(
382 | extra_fields=extra_fields, extra_joins=extra_joins, extra_filters=extra_filters
383 | )
384 |
385 | gl_entries = frappe.db.sql(query, filters, as_dict=as_dict)
386 |
387 | return gl_entries
388 |
389 |
390 | def get_customers(filters):
391 | """
392 | Get a list of Customers.
393 |
394 | Arguments:
395 | filters -- dict of filters to be passed to the sql query
396 | """
397 | return frappe.db.sql(
398 | """
399 | SELECT
400 |
401 | par.debtor_creditor_number as 'Konto',
402 | CASE cus.customer_type
403 | WHEN 'Company' THEN cus.customer_name
404 | ELSE null
405 | END as 'Name (Adressatentyp Unternehmen)',
406 | CASE cus.customer_type
407 | WHEN 'Individual' THEN TRIM(SUBSTR(cus.customer_name, LOCATE(' ', cus.customer_name)))
408 | ELSE null
409 | END as 'Name (Adressatentyp natürl. Person)',
410 | CASE cus.customer_type
411 | WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(cus.customer_name, ' ', 1), ' ', -1)
412 | ELSE null
413 | END as 'Vorname (Adressatentyp natürl. Person)',
414 | CASE cus.customer_type
415 | WHEN 'Individual' THEN '1'
416 | WHEN 'Company' THEN '2'
417 | ELSE '0'
418 | END as 'Adressatentyp',
419 | adr.address_line1 as 'Straße',
420 | adr.pincode as 'Postleitzahl',
421 | adr.city as 'Ort',
422 | UPPER(country.code) as 'Land',
423 | adr.address_line2 as 'Adresszusatz',
424 | adr.email_id as 'E-Mail',
425 | adr.phone as 'Telefon',
426 | adr.fax as 'Fax',
427 | cus.website as 'Internet',
428 | cus.tax_id as 'Steuernummer'
429 |
430 | FROM `tabCustomer` cus
431 |
432 | left join `tabParty Account` par
433 | on par.parent = cus.name
434 | and par.parenttype = 'Customer'
435 | and par.company = %(company)s
436 |
437 | left join `tabDynamic Link` dyn_adr
438 | on dyn_adr.link_name = cus.name
439 | and dyn_adr.link_doctype = 'Customer'
440 | and dyn_adr.parenttype = 'Address'
441 |
442 | left join `tabAddress` adr
443 | on adr.name = dyn_adr.parent
444 | and adr.is_primary_address = '1'
445 |
446 | left join `tabCountry` country
447 | on country.name = adr.country
448 |
449 | WHERE adr.is_primary_address = '1'
450 | """,
451 | filters,
452 | as_dict=1,
453 | )
454 |
455 |
456 | def get_suppliers(filters):
457 | """
458 | Get a list of Suppliers.
459 |
460 | Arguments:
461 | filters -- dict of filters to be passed to the sql query
462 | """
463 | return frappe.db.sql(
464 | """
465 | SELECT
466 |
467 | par.debtor_creditor_number as 'Konto',
468 | CASE sup.supplier_type
469 | WHEN 'Company' THEN sup.supplier_name
470 | ELSE null
471 | END as 'Name (Adressatentyp Unternehmen)',
472 | CASE sup.supplier_type
473 | WHEN 'Individual' THEN TRIM(SUBSTR(sup.supplier_name, LOCATE(' ', sup.supplier_name)))
474 | ELSE null
475 | END as 'Name (Adressatentyp natürl. Person)',
476 | CASE sup.supplier_type
477 | WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(sup.supplier_name, ' ', 1), ' ', -1)
478 | ELSE null
479 | END as 'Vorname (Adressatentyp natürl. Person)',
480 | CASE sup.supplier_type
481 | WHEN 'Individual' THEN '1'
482 | WHEN 'Company' THEN '2'
483 | ELSE '0'
484 | END as 'Adressatentyp',
485 | adr.address_line1 as 'Straße',
486 | adr.pincode as 'Postleitzahl',
487 | adr.city as 'Ort',
488 | UPPER(country.code) as 'Land',
489 | adr.address_line2 as 'Adresszusatz',
490 | adr.email_id as 'E-Mail',
491 | adr.phone as 'Telefon',
492 | adr.fax as 'Fax',
493 | sup.website as 'Internet',
494 | sup.tax_id as 'Steuernummer',
495 | case sup.on_hold when 1 then sup.release_date else null end as 'Zahlungssperre bis'
496 |
497 | FROM `tabSupplier` sup
498 |
499 | left join `tabParty Account` par
500 | on par.parent = sup.name
501 | and par.parenttype = 'Supplier'
502 | and par.company = %(company)s
503 |
504 | left join `tabDynamic Link` dyn_adr
505 | on dyn_adr.link_name = sup.name
506 | and dyn_adr.link_doctype = 'Supplier'
507 | and dyn_adr.parenttype = 'Address'
508 |
509 | left join `tabAddress` adr
510 | on adr.name = dyn_adr.parent
511 | and adr.is_primary_address = '1'
512 |
513 | left join `tabCountry` country
514 | on country.name = adr.country
515 |
516 | WHERE adr.is_primary_address = '1'
517 | """,
518 | filters,
519 | as_dict=1,
520 | )
521 |
522 |
523 | def get_account_names(filters):
524 | return frappe.db.sql(
525 | """
526 | SELECT
527 |
528 | account_number as 'Konto',
529 | LEFT(account_name, 40) as 'Kontenbeschriftung',
530 | 'de-DE' as 'Sprach-ID'
531 |
532 | FROM `tabAccount`
533 | WHERE company = %(company)s
534 | AND is_group = 0
535 | AND account_number != ''
536 | """,
537 | filters,
538 | as_dict=1,
539 | )
540 |
541 |
542 | @frappe.whitelist()
543 | def download_datev_csv(filters):
544 | """
545 | Provide accounting entries for download in DATEV format.
546 |
547 | Validate the filters, get the data, produce the CSV file and provide it for
548 | download. Can be called like this:
549 |
550 | GET /api/method/erpnext_datev.erpnext_datev.report.datev.datev.download_datev_csv
551 |
552 | Arguments / Params:
553 | filters -- dict of filters to be passed to the sql query
554 | """
555 | frappe.only_for(["Accounts User", "Accounts Manager"])
556 |
557 | if isinstance(filters, str):
558 | filters = json.loads(filters)
559 |
560 | validate(filters)
561 |
562 | company = filters.get("company")
563 | fiscal_year = get_fiscal_year(date=filters.get("from_date"), company=company)
564 | coa = frappe.get_value("Company", company, "chart_of_accounts")
565 | datev_settings = frappe.get_doc("DATEV Settings", company)
566 |
567 | filters.update(
568 | {
569 | "fiscal_year_start": fiscal_year[1],
570 | "skr": "04" if "SKR04" in coa else ("03" if "SKR03" in coa else ""),
571 | "account_number_length": datev_settings.account_number_length,
572 | "against_account": datev_settings.temporary_against_account_number,
573 | "opening_account": datev_settings.opening_against_account_number
574 | or datev_settings.temporary_against_account_number,
575 | }
576 | )
577 |
578 | transactions = get_transactions(filters)
579 | account_names = get_account_names(filters)
580 | customers = get_customers(filters)
581 | suppliers = get_suppliers(filters)
582 |
583 | zip_name = "{} DATEV.zip".format(frappe.utils.datetime.date.today())
584 | zip_and_download(
585 | zip_name,
586 | [
587 | {
588 | "file_name": "EXTF_Buchungsstapel.csv",
589 | "csv_data": get_datev_csv(transactions, filters, csv_class=Transactions),
590 | },
591 | {
592 | "file_name": "EXTF_Kontenbeschriftungen.csv",
593 | "csv_data": get_datev_csv(account_names, filters, csv_class=AccountNames),
594 | },
595 | {
596 | "file_name": "EXTF_Kunden.csv",
597 | "csv_data": get_datev_csv(customers, filters, csv_class=DebtorsCreditors),
598 | },
599 | {
600 | "file_name": "EXTF_Lieferanten.csv",
601 | "csv_data": get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors),
602 | },
603 | ],
604 | )
605 |
--------------------------------------------------------------------------------