.
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include MANIFEST.in
2 | include requirements.txt
3 | include *.json
4 | include *.md
5 | include *.py
6 | include *.txt
7 | recursive-include erpnext_germany *.css
8 | recursive-include erpnext_germany *.csv
9 | recursive-include erpnext_germany *.html
10 | recursive-include erpnext_germany *.ico
11 | recursive-include erpnext_germany *.js
12 | recursive-include erpnext_germany *.json
13 | recursive-include erpnext_germany *.md
14 | recursive-include erpnext_germany *.png
15 | recursive-include erpnext_germany *.py
16 | recursive-include erpnext_germany *.svg
17 | recursive-include erpnext_germany *.txt
18 | recursive-exclude erpnext_germany *.pyc
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## ERPNext Germany
2 |
3 | App to hold regional code for Germany, built on top of ERPNext.
4 |
5 | ### Features
6 |
7 | - German accounting reports
8 |
9 | - _Summen- und Saldenliste_
10 |
11 | - Section for Register Information (Registerart, -gericht und nummer) in **Company**, **Customer** and **Supplier**
12 |
13 | 
14 |
15 | - Validation of EU VAT IDs
16 |
17 | Automatically checks the validity of EU VAT IDs of all your customers every three months, or manually whenever you want. Check out the [intro on Youtube](https://youtu.be/hsFMn2Y85zA) (german).
18 |
19 | 
20 |
21 | - Allow deletion of the most recent sales transaction only
22 |
23 | This ensures consecutive numbering of transactions. Applies to **Quotation**, **Sales Order**, **Sales Invoice**.
24 |
25 | - Restrict deletion of attachments to submitted transactions
26 | - Custom fields in **Employee** (tax information, etc.)
27 | - List of religios denominations ("Konfessionen")
28 | - List of German health insurance providers (depends on HRMS)
29 | - Create **Business Letters** from a template and print or email them to your customers or suppliers
30 | - Record **Business Trips** and pay out allowances to your employees (dt. Reisekostenabrechnung) (depends on HRMS)
31 |
32 | ## Installation
33 |
34 | > [!NOTE]
35 | > Some features of this app depend on the [HRMS](https://github.com/frappe/hrms) app. If you want to use them, you need to install the HRMS app before installing this app.
36 |
37 | ### On Frappe Cloud
38 |
39 | You can find ERPNext Germany in the [Frappe Cloud Marketplace](https://frappecloud.com/marketplace/apps/erpnext_germany).
40 | Please refer to the [Frappe Cloud documentation](https://frappecloud.com/docs/installing-an-app) on how to install an app.
41 |
42 | ### Local
43 |
44 | Using bench, [install ERPNext](https://github.com/frappe/bench#installation) as mentioned here.
45 |
46 | Once ERPNext is installed, add the ERPNext Germany app to your bench by running
47 |
48 | ```bash
49 | bench get-app https://github.com/alyf-de/erpnext_germany.git
50 | ```
51 |
52 | After that, you can install the app on required site (let's say demo.com ) by running
53 |
54 | ```bash
55 | bench --site demo.com install-app erpnext_germany
56 | ```
57 |
58 | ## Business Trip
59 |
60 | Before an employee can create a **Business Trip (Dienstreise)**, you should configure the available regions and their travel allowances in the **Business Trip Region** list. A basic list based on the German tax law is imported on installation, but not further updated later. In the **Business Trip Settings (Dienstreise-Einstellungen)** you can set the value for the mileage allowance and select **Expense Claim Type** for mileage allowance and **Additional meal expenses**. Both Expense Claim Type are preset to **Additional meal expenses**. If you have HRMS installed, it allows you to book expenses as Expense Claims.
61 |
62 | When a **Business Trip** is submitted, it creates a draft **Expense Claim** for the employee's travel allowances including the Additional meal expenses as well as mileage allowance for "Car (private)". One business trip will create on **Expense Claim** which can be approved and submitted as usual.
63 |
64 | Mileage allowance is calculated based on the distance entered in the details of the journey when "Car (private)" is selected.
65 |
66 | The Additional meal expenses depend on the selected region. Select the "From" and "To" dates of the trip and with the click on "Add Row" all days of the trip a pre-created.
67 |
68 | The receipts for transport and accommodation can be attached, but are not processed automatically. You can check them, create a **Purchase Invoice** and pay the respective amount to the employee.
69 |
70 | You can use our [Banking app](https://github.com/alyf-de/banking) to reconcile the **Expense Claims** and **Purchase Invoices** with the respective **Bank Transactions**.
71 |
72 | ### License
73 |
74 | GNU GPL V3. See the `LICENSE` file for more information.
75 |
--------------------------------------------------------------------------------
/docs/register_information.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/docs/register_information.png
--------------------------------------------------------------------------------
/docs/vat_check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/docs/vat_check.png
--------------------------------------------------------------------------------
/erpnext_germany/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = "15.14.3"
2 |
--------------------------------------------------------------------------------
/erpnext_germany/api.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 | from .utils.eu_vat import check_vat, parse_vat_id
4 |
5 |
6 | @frappe.whitelist()
7 | def validate_vat_id(vat_id: str) -> bool:
8 | """Use the EU VAT checker to validate a VAT ID."""
9 | is_valid = frappe.cache().hget("eu_vat_validation", vat_id, shared=True)
10 | if is_valid is not None:
11 | return is_valid
12 |
13 | try:
14 | country_code, vat_number = parse_vat_id(vat_id)
15 | result = check_vat(country_code, vat_number)
16 | is_valid = result.valid
17 | frappe.cache().hset("eu_vat_validation", vat_id, is_valid, shared=True)
18 | except Exception:
19 | frappe.response["status_code"] = 501
20 | is_valid = None
21 |
22 | return is_valid
23 |
--------------------------------------------------------------------------------
/erpnext_germany/config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/config/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/config/desktop.py:
--------------------------------------------------------------------------------
1 | from frappe import _
2 |
3 |
4 | def get_data():
5 | return [
6 | {
7 | "module_name": "ERPNext Germany",
8 | "color": "grey",
9 | "icon": "octicon octicon-file-directory",
10 | "type": "module",
11 | "label": _("ERPNext Germany"),
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/erpnext_germany/config/docs.py:
--------------------------------------------------------------------------------
1 | """
2 | Configuration for docs
3 | """
4 |
5 | # source_link = "https://github.com/[org_name]/erpnext_germany"
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 Germany"
12 |
--------------------------------------------------------------------------------
/erpnext_germany/constants.py:
--------------------------------------------------------------------------------
1 | # German company register courts
2 | REGISTER_COURTS = [
3 | "Aachen",
4 | "Altenburg",
5 | "Amberg",
6 | "Ansbach",
7 | "Apolda",
8 | "Arnsberg",
9 | "Arnstadt",
10 | "Arnstadt Zweigstelle Ilmenau",
11 | "Aschaffenburg",
12 | "Augsburg",
13 | "Aurich",
14 | "Bad Hersfeld",
15 | "Bad Homburg v.d.H.",
16 | "Bad Kreuznach",
17 | "Bad Oeynhausen",
18 | "Bad Salzungen",
19 | "Bamberg",
20 | "Bayreuth",
21 | "Berlin (Charlottenburg)",
22 | "Bielefeld",
23 | "Bochum",
24 | "Bonn",
25 | "Braunschweig",
26 | "Bremen",
27 | "Chemnitz",
28 | "Coburg",
29 | "Coesfeld",
30 | "Cottbus",
31 | "Darmstadt",
32 | "Deggendorf",
33 | "Dortmund",
34 | "Dresden",
35 | "Duisburg",
36 | "Düren",
37 | "Düsseldorf",
38 | "Eisenach",
39 | "Erfurt",
40 | "Eschwege",
41 | "Essen",
42 | "Flensburg",
43 | "Frankfurt am Main",
44 | "Frankfurt/Oder",
45 | "Freiburg",
46 | "Friedberg",
47 | "Fritzlar",
48 | "Fulda",
49 | "Fürth",
50 | "Gelsenkirchen",
51 | "Gera",
52 | "Gießen",
53 | "Gotha",
54 | "Göttingen",
55 | "Greiz",
56 | "Gütersloh",
57 | "Hagen",
58 | "Hamburg",
59 | "Hamm",
60 | "Hanau",
61 | "Hannover",
62 | "Heilbad Heiligenstadt",
63 | "Hildburghausen",
64 | "Hildesheim",
65 | "Hof",
66 | "Homburg",
67 | "Ingolstadt",
68 | "Iserlohn",
69 | "Jena",
70 | "Kaiserslautern",
71 | "Kassel",
72 | "Kempten (Allgäu)",
73 | "Kiel",
74 | "Kleve",
75 | "Koblenz",
76 | "Köln",
77 | "Königstein",
78 | "Korbach",
79 | "Krefeld",
80 | "Landau",
81 | "Landshut",
82 | "Langenfeld",
83 | "Lebach",
84 | "Leipzig",
85 | "Lemgo",
86 | "Limburg",
87 | "Lübeck",
88 | "Ludwigshafen a. Rhein",
89 | "Lüneburg",
90 | "Mainz",
91 | "Mannheim",
92 | "Marburg",
93 | "Meiningen",
94 | "Memmingen",
95 | "Merzig",
96 | "Mönchengladbach",
97 | "Montabaur",
98 | "Mühlhausen",
99 | "München",
100 | "Münster",
101 | "Neubrandenburg",
102 | "Neunkirchen",
103 | "Neuruppin",
104 | "Neuss",
105 | "Nordhausen",
106 | "Nürnberg",
107 | "Offenbach am Main",
108 | "Oldenburg",
109 | "Osnabrück",
110 | "Ottweiler",
111 | "Paderborn",
112 | "Passau",
113 | "Pinneberg",
114 | "Pößneck",
115 | "Pößneck Zweigstelle Bad Lobenstein",
116 | "Potsdam",
117 | "Recklinghausen",
118 | "Regensburg",
119 | "Rostock",
120 | "Rudolstadt",
121 | "Saarbrücken",
122 | "Saarlouis",
123 | "Schweinfurt",
124 | "Schwerin",
125 | "Siegburg",
126 | "Siegen",
127 | "Sömmerda",
128 | "Sondershausen",
129 | "Sonneberg",
130 | "Stadthagen",
131 | "Stadtroda",
132 | "Steinfurt",
133 | "Stendal",
134 | "St. Ingbert",
135 | "Stralsund",
136 | "Straubing",
137 | "Stuttgart",
138 | "St. Wendel",
139 | "Suhl",
140 | "Tostedt",
141 | "Traunstein",
142 | "Ulm",
143 | "Völklingen",
144 | "Walsrode",
145 | "Weiden i. d. OPf.",
146 | "Weimar",
147 | "Wetzlar",
148 | "Wiesbaden",
149 | "Wittlich",
150 | "Wuppertal",
151 | "Würzburg",
152 | "Zweibrücken",
153 | ]
154 |
--------------------------------------------------------------------------------
/erpnext_germany/custom/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/custom/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/custom/sales.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from erpnext.controllers.selling_controller import SellingController
3 | from frappe import _
4 |
5 |
6 | def on_trash(doc: SellingController, event: str | None = None) -> None:
7 | if doc.flags.ignore_validate:
8 | return
9 |
10 | if not frappe.db.get_single_value("ERPNext Germany Settings", "prevent_gaps_in_transaction_naming"):
11 | return
12 |
13 | if is_not_latest(doc.doctype, doc.name, doc.creation, doc.company):
14 | frappe.throw(
15 | msg=_("Only the most recent {0} can be deleted in order to avoid gaps in numbering.").format(
16 | _(doc.doctype)
17 | ),
18 | title=_("Cannot delete this transaction"),
19 | )
20 |
21 |
22 | def is_not_latest(doctype, name, creation, company):
23 | return frappe.db.exists(
24 | doctype,
25 | {
26 | "creation": (">", creation),
27 | "name": ("!=", name),
28 | "company": company,
29 | },
30 | )
31 |
--------------------------------------------------------------------------------
/erpnext_germany/custom_fields.py:
--------------------------------------------------------------------------------
1 | from frappe import get_installed_apps
2 |
3 | from .constants import REGISTER_COURTS
4 |
5 |
6 | def _(message: str) -> str:
7 | return message
8 |
9 |
10 | def get_register_fields(insert_after: str):
11 | return [
12 | {
13 | "fieldtype": "Section Break",
14 | "fieldname": "register_sb_1",
15 | "label": _("Register Information"),
16 | "insert_after": insert_after,
17 | "collapsible": 1,
18 | },
19 | {
20 | "fieldtype": "Select",
21 | "fieldname": "register_type",
22 | "label": _("Register Type"),
23 | "insert_after": "register_sb_1",
24 | "options": "\nHRA\nHRB\nGnR\nPR\nVR",
25 | "translatable": 0,
26 | },
27 | {
28 | "fieldtype": "Column Break",
29 | "fieldname": "register_cb_1",
30 | "insert_after": "register_type",
31 | },
32 | {
33 | "fieldtype": "Data",
34 | "fieldname": "register_number",
35 | "label": _("Register Number"),
36 | "insert_after": "register_cb_1",
37 | "translatable": 0,
38 | },
39 | {
40 | "fieldtype": "Column Break",
41 | "fieldname": "register_cb_2",
42 | "insert_after": "register_number",
43 | },
44 | {
45 | "fieldtype": "Select",
46 | "fieldname": "register_court",
47 | "label": _("Register Court"),
48 | "insert_after": "register_cb_2",
49 | "options": "\n".join(["", *REGISTER_COURTS]), # empty string to be able to select nothing
50 | "translatable": 0,
51 | },
52 | ]
53 |
54 |
55 | def get_custom_fields():
56 | custom_fields = {
57 | "Company": get_register_fields(insert_after="address_html"),
58 | "Customer": get_register_fields(insert_after="companies"),
59 | "Supplier": get_register_fields(insert_after="companies"),
60 | "Employee": [
61 | {
62 | "fieldtype": "Link",
63 | "fieldname": "nationality",
64 | "label": _("Nationality"),
65 | "options": "Country",
66 | "insert_after": "date_of_joining",
67 | },
68 | {
69 | "fieldtype": "Check",
70 | "fieldname": "is_severely_disabled",
71 | "label": _("Is Severely Disabled"),
72 | "insert_after": "nationality",
73 | },
74 | {
75 | "fieldtype": "Float",
76 | "fieldname": "working_hours_per_week",
77 | "label": _("Working Hours Per Week"),
78 | "insert_after": "attendance_device_id",
79 | },
80 | # -- BEGIN TAXES SECTION --
81 | {
82 | "fieldtype": "Section Break",
83 | "fieldname": "employee_taxes_sb",
84 | "label": _("Taxes"),
85 | "insert_after": "default_shift",
86 | "collapsible": 1,
87 | },
88 | {
89 | "fieldtype": "Data",
90 | "fieldname": "tax_id",
91 | "label": _("Tax ID"),
92 | "insert_after": "employee_taxes_sb",
93 | "translatable": 0,
94 | },
95 | {
96 | "fieldtype": "Data",
97 | "fieldname": "tax_office",
98 | "label": _("Tax Office"),
99 | "insert_after": "tax_id",
100 | "translatable": 0,
101 | },
102 | {
103 | "fieldtype": "Data",
104 | "fieldname": "tax_office_number",
105 | "label": _("Tax Office Number"),
106 | "insert_after": "tax_office",
107 | "translatable": 0,
108 | },
109 | {
110 | "fieldtype": "Column Break",
111 | "fieldname": "employee_taxes_cb",
112 | "insert_after": "tax_office_number",
113 | },
114 | {
115 | "fieldtype": "Select",
116 | "fieldname": "tax_bracket",
117 | "label": _("Tax Bracket"),
118 | "options": "\nI\nII\nIII\nIV\nV\nVI",
119 | "insert_after": "employee_taxes_cb",
120 | "translatable": 0,
121 | },
122 | {
123 | "fieldtype": "Int",
124 | "fieldname": "children_eligible_for_tax_credits",
125 | "label": _("Children Eligible for Tax Credits"),
126 | "insert_after": "tax_bracket",
127 | },
128 | {
129 | "fieldtype": "Link",
130 | "fieldname": "religious_denomination",
131 | "label": _("Religious Denomination"),
132 | "options": "Religious Denomination",
133 | "insert_after": "children_eligible_for_tax_credits",
134 | },
135 | # -- END TAXES SECTION --
136 | {
137 | "fieldtype": "Check",
138 | "fieldname": "has_children",
139 | "label": _("Has Children"),
140 | "insert_after": "health_insurance_no",
141 | },
142 | {
143 | "fieldtype": "Check",
144 | "fieldname": "has_other_employments",
145 | "label": _("Has Other Employments"),
146 | "insert_after": "external_work_history",
147 | },
148 | {
149 | "fieldtype": "Select",
150 | "fieldname": "highest_school_qualification",
151 | "label": _("Highest School Qualification"),
152 | "options": "\n".join(
153 | [
154 | "",
155 | "Ohne Schulabschluss",
156 | "Haupt-/Volksschulabschluss",
157 | "Mitttlere Reife",
158 | "(Fach-)Abitur",
159 | ]
160 | ),
161 | "insert_after": "education",
162 | "translatable": 0,
163 | },
164 | ],
165 | ("Quotation", "Sales Order", "Sales Invoice"): [
166 | {
167 | "label": _("Tax Exemption Reason"),
168 | "fieldtype": "Small Text",
169 | "fieldname": "tax_exemption_reason",
170 | "fetch_from": "taxes_and_charges.tax_exemption_reason",
171 | "depends_on": "tax_exemption_reason",
172 | "insert_after": "taxes_and_charges",
173 | "translatable": 0,
174 | }
175 | ],
176 | "Sales Taxes and Charges Template": [
177 | {
178 | "label": _("Tax Exemption Reason"),
179 | "fieldtype": "Small Text",
180 | "fieldname": "tax_exemption_reason",
181 | "insert_after": "tax_category",
182 | "translatable": 0,
183 | }
184 | ],
185 | }
186 |
187 | if "hrms" in get_installed_apps():
188 | custom_fields["Expense Claim"] = [
189 | {
190 | "fieldtype": "Link",
191 | "fieldname": "business_trip",
192 | "label": _("Business Trip"),
193 | "options": "Business Trip",
194 | "insert_after": "company",
195 | },
196 | ]
197 |
198 | return custom_fields
199 |
--------------------------------------------------------------------------------
/erpnext_germany/data/business_trip_region.csv:
--------------------------------------------------------------------------------
1 | title,whole_day,arrival_or_departure,accommodation,valid_from
2 | Afghanistan,30,20,95,2024-01-01
3 | Ägypten,50,33,112,2024-01-01
4 | Äthiopien,39,26,130,2024-01-01
5 | Äquatorialguinea,42,28,166,2024-01-01
6 | Albanien,27,18,112,2024-01-01
7 | Algerien,47,32,120,2024-01-01
8 | Andorra,41,28,91,2024-01-01
9 | Angola,52,35,299,2024-01-01
10 | Argentinien,35,24,113,2024-01-01
11 | Armenien,24,16,59,2024-01-01
12 | Aserbaidschan,44,29,88,2024-01-01
13 | Australien: Canberra,74,49,186,2024-01-01
14 | Australien: Sydney,57,38,173,2024-01-01
15 | Australien,57,38,173,2024-01-01
16 | Bahrain,48,32,153,2024-01-01
17 | Bangladesch,50,33,165,2024-01-01
18 | Barbados,54,36,206,2024-01-01
19 | Belgien,59,40,141,2024-01-01
20 | Benin,52,35,115,2024-01-01
21 | Bolivien,46,31,108,2024-01-01
22 | Bosnien und Herzegowina,23,16,75,2024-01-01
23 | Botsuana,46,31,176,2024-01-01
24 | Brasilien: Brasilia,51,34,88,2024-01-01
25 | Brasilien: Rio de Janeiro,69,46,140,2024-01-01
26 | Brasilien: Sao Paulo,46,31,151,2024-01-01
27 | Brasilien,46,31,88,2024-01-01
28 | Brunei,52,35,106,2024-01-01
29 | Bulgarien,22,15,115,2024-01-01
30 | Burkina Faso,38,25,174,2024-01-01
31 | Burundi,36,24,138,2024-01-01
32 | Chile,44,29,154,2024-01-01
33 | China: Chengdu,41,28,131,2024-01-01
34 | China: Hongkong,71,48,169,2024-01-01
35 | China: Kanton,36,24,150,2024-01-01
36 | China: Peking,30,20,185,2024-01-01
37 | China: Shanghai,58,39,217,2024-01-01
38 | China,48,32,112,2024-01-01
39 | Costa Rica,47,32,93,2024-01-01
40 | Côte d’Ivoire,59,40,166,2024-01-01
41 | Dänemark,75,50,183,2024-01-01
42 | Deutschland,28,14,0,2024-01-01
43 | Dominikanische Republik,50,33,167,2024-01-01
44 | Dschibuti,77,52,255,2024-01-01
45 | Ecuador,27,18,103,2024-01-01
46 | El Salvador,65,44,161,2024-01-01
47 | Eritrea,50,33,91,2024-01-01
48 | Estland,29,20,85,2024-01-01
49 | Fidschi,32,21,183,2024-01-01
50 | Finnland,54,36,171,2024-01-01
51 | "Frankreich: Paris sowie die Departments 77, 78, 91 bis 95",58,39,159,2024-01-01
52 | Frankreich,53,36,105,2024-01-01
53 | Gabun,52,35,183,2024-01-01
54 | Gambia,40,27,161,2024-01-01
55 | Georgien,45,30,87,2024-01-01
56 | Ghana,46,31,148,2024-01-01
57 | Griechenland: Athen,40,27,139,2024-01-01
58 | Griechenland,36,24,150,2024-01-01
59 | Guatemala,34,23,90,2024-01-01
60 | Guinea,59,40,140,2024-01-01
61 | Guinea-Bissau,32,21,113,2024-01-01
62 | Haiti,58,39,130,2024-01-01
63 | Honduras,57,38,198,2024-01-01
64 | Indien: Chennai,32,21,85,2024-01-01
65 | Indien: Kalkutta,35,24,145,2024-01-01
66 | Indien: Mumbai,50,33,146,2024-01-01
67 | Indien: Neu Delhi,38,25,185,2024-01-01
68 | Indien,32,21,85,2024-01-01
69 | Indonesien,36,24,134,2024-01-01
70 | Iran,33,22,196,2024-01-01
71 | Irland,58,39,129,2024-01-01
72 | Island,62,41,187,2024-01-01
73 | Israel,66,44,190,2024-01-01
74 | Italien: Mailand,42,28,191,2024-01-01
75 | Italien: Rom,48,32,150,2024-01-01
76 | Italien,42,28,150,2024-01-01
77 | Jamaika,39,26,171,2024-01-01
78 | Japan: Tokio,50,33,285,2024-01-01
79 | Japan,52,35,190,2024-01-01
80 | Jemen,24,16,95,2024-01-01
81 | Jordanien,57,38,134,2024-01-01
82 | Kambodscha,38,25,94,2024-01-01
83 | Kamerun,56,37,275,2024-01-01
84 | Kanada: Ottawa,62,41,214,2024-01-01
85 | Kanada: Toronto,54,36,392,2024-01-01
86 | Kanada: Vancouver,63,42,304,2024-01-01
87 | Kanada,54,36,214,2024-01-01
88 | Kap Verde,38,25,90,2024-01-01
89 | Kasachstan,45,30,111,2024-01-01
90 | Katar,56,37,149,2024-01-01
91 | Kenia,51,34,219,2024-01-01
92 | Kirgisistan,27,18,74,2024-01-01
93 | Kolumbien,46,31,115,2024-01-01
94 | "Kongo, Republik",62,41,215,2024-01-01
95 | "Kongo, Demokratische Republik",70,47,190,2024-01-01
96 | "Korea, Demokratische Volksrepublik",28,19,92,2024-01-01
97 | "Korea, Republik",48,32,108,2024-01-01
98 | Kosovo,24,16,71,2024-01-01
99 | Kroatien,35,24,107,2024-01-01
100 | Kuba,51,34,170,2024-01-01
101 | Kuwait,56,37,241,2024-01-01
102 | Laos,35,24,71,2024-01-01
103 | Lesotho,28,19,104,2024-01-01
104 | Lettland,35,24,76,2024-01-01
105 | Libanon,69,46,146,2024-01-01
106 | Libyen,63,42,135,2024-01-01
107 | Liechtenstein,56,37,190,2024-01-01
108 | Litauen,26,17,109,2024-01-01
109 | Luxemburg,63,42,139,2024-01-01
110 | Madagaskar,33,22,116,2024-01-01
111 | Malawi,41,28,109,2024-01-01
112 | Malaysia,36,24,86,2024-01-01
113 | Malediven,52,35,170,2024-01-01
114 | Mali,38,25,120,2024-01-01
115 | Malta,46,31,114,2024-01-01
116 | Marokko,41,28,87,2024-01-01
117 | Marshall Inseln,63,42,102,2024-01-01
118 | Mauretanien,35,24,86,2024-01-01
119 | Mauritius,44,29,172,2024-01-01
120 | Mexiko,48,32,177,2024-01-01
121 | "Moldau, Republik",26,17,73,2024-01-01
122 | Monaco,52,35,187,2024-01-01
123 | Mongolei,23,16,92,2024-01-01
124 | Montenegro,32,21,85,2024-01-01
125 | Mosambik,51,34,208,2024-01-01
126 | Myanmar,35,24,155,2024-01-01
127 | Namibia,30,20,112,2024-01-01
128 | Nepal,36,24,126,2024-01-01
129 | Neuseeland,58,39,148,2024-01-01
130 | Nicaragua,46,31,105,2024-01-01
131 | Niederlande,47,32,122,2024-01-01
132 | Niger,42,28,131,2024-01-01
133 | Nigeria,46,31,182,2024-01-01
134 | Nordmazedonien,27,18,89,2024-01-01
135 | Norwegen,75,50,139,2024-01-01
136 | Österreich,50,33,117,2024-01-01
137 | Oman,64,43,141,2024-01-01
138 | Pakistan: Islamabad,23,16,238,2024-01-01
139 | Pakistan,34,23,122,2024-01-01
140 | Palau,51,34,179,2024-01-01
141 | Panama,41,28,82,2024-01-01
142 | Papua-Neuguinea,59,40,159,2024-01-01
143 | Paraguay,39,26,124,2024-01-01
144 | Peru,34,23,143,2024-01-01
145 | Philippinen,41,28,140,2024-01-01
146 | Polen: Breslau,33,22,117,2024-01-01
147 | Polen: Danzig,30,20,84,2024-01-01
148 | Polen: Krakau,27,18,86,2024-01-01
149 | Polen: Warschau,29,20,109,2024-01-01
150 | Polen,29,20,60,2024-01-01
151 | Portugal,32,21,111,2024-01-01
152 | Ruanda,44,29,117,2024-01-01
153 | Rumänien: Bukarest,32,21,92,2024-01-01
154 | Rumänien,27,18,89,2024-01-01
155 | Russische Föderation: Jekaterinburg,28,19,84,2024-01-01
156 | Russische Föderation: Moskau,30,20,110,2024-01-01
157 | Russische Föderation: St. Petersburg,26,17,114,2024-01-01
158 | Russische Föderation,24,16,58,2024-01-01
159 | Sambia,38,25,105,2024-01-01
160 | Samoa,39,26,105,2024-01-01
161 | San Marino,34,23,79,2024-01-01
162 | São Tomé – Príncipe,47,32,80,2024-01-01
163 | Saudi-Arabien: Djidda,57,38,181,2024-01-01
164 | Saudi-Arabien: Riad,56,37,186,2024-01-01
165 | Saudi-Arabien,56,37,181,2024-01-01
166 | Schweden,66,44,140,2024-01-01
167 | Schweiz: Genf,66,44,186,2024-01-01
168 | Schweiz,64,43,180,2024-01-01
169 | Senegal,42,28,190,2024-01-01
170 | Serbien,27,18,97,2024-01-01
171 | Sierra Leone,57,38,145,2024-01-01
172 | Simbabwe,45,30,140,2024-01-01
173 | Singapur,54,36,197,2024-01-01
174 | Slowakische Republik,33,22,121,2024-01-01
175 | Slowenien,38,25,126,2024-01-01
176 | Spanien: Barcelona,34,23,144,2024-01-01
177 | Spanien: Kanarische Inseln,36,24,103,2024-01-01
178 | Spanien: Madrid,42,28,131,2024-01-01
179 | Spanien: Palma de Mallorca,44,29,142,2024-01-01
180 | Spanien,34,23,103,2024-01-01
181 | Sri Lanka,36,24,112,2024-01-01
182 | Sudan,33,22,195,2024-01-01
183 | Südafrika: Kapstadt,33,22,130,2024-01-01
184 | Südafrika: Johannesburg,36,24,129,2024-01-01
185 | Südafrika,29,20,109,2024-01-01
186 | Südsudan,51,34,159,2024-01-01
187 | Syrien,38,25,140,2024-01-01
188 | Tadschikistan,27,18,118,2024-01-01
189 | Taiwan,46,31,143,2024-01-01
190 | Tansania,44,29,97,2024-01-01
191 | Thailand,38,25,110,2024-01-01
192 | Togo,39,26,118,2024-01-01
193 | Tonga,39,26,94,2024-01-01
194 | Trinidad und Tobago,66,44,203,2024-01-01
195 | Tschad,42,28,155,2024-01-01
196 | Tschechische Republik,32,21,77,2024-01-01
197 | Türkei: Istanbul,26,17,120,2024-01-01
198 | Türkei: Izmir,29,20,55,2024-01-01
199 | Türkei,17,12,95,2024-01-01
200 | Tunesien,40,27,144,2024-01-01
201 | Turkmenistan,33,22,108,2024-01-01
202 | Uganda,41,28,143,2024-01-01
203 | Ukraine,26,17,98,2024-01-01
204 | Ungarn,32,21,85,2024-01-01
205 | Uruguay,48,32,90,2024-01-01
206 | Usbekistan,34,23,104,2024-01-01
207 | Vatikanstaat,48,32,150,2024-01-01
208 | Venezuela,45,30,127,2024-01-01
209 | Vereinigte Arabische Emirate,65,44,156,2024-01-01
210 | Vereinigte Staaten von Amerika: Atlanta,77,52,182,2024-01-01
211 | Vereinigte Staaten von Amerika: Boston,63,42,333,2024-01-01
212 | Vereinigte Staaten von Amerika: Chicago,65,44,233,2024-01-01
213 | Vereinigte Staaten von Amerika: Houston,62,41,204,2024-01-01
214 | Vereinigte Staaten von Amerika: Los Angeles,64,43,262,2024-01-01
215 | Vereinigte Staaten von Amerika: Miami,65,44,256,2024-01-01
216 | Vereinigte Staaten von Amerika: New York City,66,44,308,2024-01-01
217 | Vereinigte Staaten von Amerika: San Francisco,59,40,327,2024-01-01
218 | "Vereinigte Staaten von Amerika: Washington, D. C.",66,44,203,2024-01-01
219 | Vereinigte Staaten von Amerika,59,40,182,2024-01-01
220 | Vereinigtes Königreich von Großbritannien und Nordirland: London,66,44,163,2024-01-01
221 | Vereinigtes Königreich von Großbritannien und Nordirland,52,35,99,2024-01-01
222 | Vietnam,41,28,86,2024-01-01
223 | Weißrussland,20,13,98,2024-01-01
224 | Zentralafrikanische Republik,53,36,210,2024-01-01
225 | Zypern,42,28,125,2024-01-01
--------------------------------------------------------------------------------
/erpnext_germany/data/employee_health_insurance.csv:
--------------------------------------------------------------------------------
1 | health_insurance_name
2 | AOK Niedersachsen
3 | AOK Hessen
4 | AOK Baden-Württemberg
5 | AOK Bayern
6 | AOK Bremen / Bremerhaven
7 | AOK Nordost
8 | AOK NordWest
9 | AOK PLUS
10 | AOK Rheinland/Hamburg
11 | AOK Rheinland-Pfalz/Saarland
12 | AOK Sachsen-Anhalt
13 | Audi BKK
14 | BAHN-BKK
15 | BARMER
16 | BERGISCHE KRANKENKASSE
17 | Bertelsmann BKK
18 | Betriebskrankenkasse Mobil
19 | BIG direkt gesund
20 | BKK Akzo Nobel Bayern
21 | BKK Diakonie
22 | BKK EUREGIO
23 | BKK exklusiv
24 | BKK Faber-Castell & Partner
25 | BKK firmus
26 | BKK Freudenberg
27 | BKK GILDEMEISTER SEIDENSTICKER
28 | BKK Herkules
29 | BKK Linde
30 | bkk melitta hmr
31 | BKK PFAFF
32 | BKK Pfalz
33 | BKK ProVita
34 | BKK Public
35 | BKK Scheufelen
36 | BKK Schwarzwald-Baar-Heuberg
37 | BKK Technoform
38 | BKK Textilgruppe Hof
39 | BKK VDN
40 | BKK VerbundPlus
41 | BKK Verkehrsbau Union (BKK VBU)
42 | BKK Werra-Meissner
43 | BKK Wirtschaft & Finanzen
44 | BKK ZF & Partner
45 | BKK_DürkoppAdler
46 | BKK24
47 | Bosch BKK
48 | Continentale Betriebskrankenkasse
49 | DAK-Gesundheit
50 | Debeka BKK
51 | energie-Betriebskrankenkasse
52 | Handelskrankenkasse (hkk)
53 | Heimat Krankenkasse
54 | HEK - Hanseatische Krankenkasse
55 | IKK - Die Innovationskasse
56 | IKK Brandenburg und Berlin
57 | IKK classic
58 | IKK gesund plus
59 | IKK Südwest
60 | Kaufmännische Krankenkasse - KKH
61 | KNAPPSCHAFT
62 | mhplus Betriebskrankenkasse
63 | Novitas BKK
64 | pronova BKK
65 | R+V Betriebskrankenkasse
66 | Salus BKK
67 | SECURVITA BKK
68 | Siemens-Betriebskrankenkasse (SBK)
69 | SKD BKK
70 | "Sozialversicherung für Landwirtschaft, Forsten und Gartenbau (SVLFG)"
71 | Techniker Krankenkasse
72 | TUI BKK
73 | VIACTIV Krankenkasse
74 | vivida bkk
75 | WMF Betriebskrankenkasse
--------------------------------------------------------------------------------
/erpnext_germany/data/expense_claim_type.csv:
--------------------------------------------------------------------------------
1 | expense_type,description
2 | Additional meal expenses,Used for creating expense claims from Business Trips
3 |
--------------------------------------------------------------------------------
/erpnext_germany/data/religious_denomination.csv:
--------------------------------------------------------------------------------
1 | name,title
2 | rk,Römisch-Katholische Kirchensteuer
3 | ev,Evangelische Kirchensteuer
4 | lt,Evangelisch lutherisch
5 | rf,Evangelisch reformiert
6 | ak,Altkatholische Kirchensteuer
7 | is,Israelitische/Jüdische Kultussteuer
8 | fb,Freireligiöse Landesgemeinde Baden
9 | ib,Israelitische Religionsgemeinschaft Baden
10 | fs,Freireligiöse Gemeinde Offenbach/Main
11 | fg,Freireligiöse Landesgemeinde Pfalz
12 | fm,Freireligiöse Gemeinde Mainz
13 | jh,Jüdische Kultussteuer
14 | jd,Jüdische Kultussteuer
15 | ih,Jüdische Kultussteuer
16 | iw,Israelitische Religionsgemeinschaft Württembergs
17 | il,Israelitische Kultussteuer der kultusberechtigten Gemeinden
18 | fr,Französisch reformiert
19 | fa,Freie Religionsgemeinschaft Alzey
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_letter/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("Business Letter", {
5 | setup(frm) {
6 | frm.set_query("department", function (doc) {
7 | return {
8 | filters: {
9 | company: doc.company,
10 | },
11 | };
12 | });
13 |
14 | frm.set_query("employee", function (doc) {
15 | return {
16 | filters: {
17 | company: doc.company,
18 | status: "Active",
19 | ...(doc.department && { department: doc.department }),
20 | },
21 | };
22 | });
23 | },
24 | onload(frm) {
25 | set_html_data(frm);
26 | },
27 | template: function (frm) {
28 | if (frm.doc.template) {
29 | frappe.call({
30 | method: "get_template",
31 | doc: frm.doc,
32 | callback: function (r) {
33 | if (r.message) {
34 | frm.set_value("subject", r.message.subject);
35 | frm.set_value("content", r.message.content);
36 | }
37 | },
38 | });
39 | }
40 | },
41 | address: function (frm) {
42 | erpnext.utils.get_address_display(frm, "address");
43 | },
44 | });
45 |
46 | function set_html_data(frm) {
47 | const html_notice = ``;
50 |
51 | frm.get_field("html_notice").$wrapper.html(html_notice);
52 | frm.get_field("html_help").$wrapper.html(
53 | `${erpnext_germany.business_letter.get_help_text()}
`
54 | );
55 | }
56 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2024-02-02 11:10:16.599009",
5 | "doctype": "DocType",
6 | "engine": "InnoDB",
7 | "field_order": [
8 | "amended_from",
9 | "section_break_ikyq",
10 | "recipient_column",
11 | "address",
12 | "contact",
13 | "email_id",
14 | "address_display",
15 | "column_break_ybiq",
16 | "date",
17 | "company",
18 | "department",
19 | "employee",
20 | "employee_name",
21 | "section_break_bsox",
22 | "template",
23 | "subject",
24 | "content",
25 | "html_help",
26 | "reference_section",
27 | "link_document_type",
28 | "column_break_xyam",
29 | "link_name",
30 | "column_break_yebn",
31 | "link_title",
32 | "preview_tab",
33 | "sdf_section",
34 | "subject_preview",
35 | "content_preview",
36 | "html_notice"
37 | ],
38 | "fields": [
39 | {
40 | "fieldname": "amended_from",
41 | "fieldtype": "Link",
42 | "label": "Amended From",
43 | "no_copy": 1,
44 | "options": "Business Letter",
45 | "print_hide": 1,
46 | "read_only": 1,
47 | "search_index": 1
48 | },
49 | {
50 | "fieldname": "address",
51 | "fieldtype": "Link",
52 | "label": "Address",
53 | "options": "Address",
54 | "print_hide": 1
55 | },
56 | {
57 | "fieldname": "contact",
58 | "fieldtype": "Link",
59 | "label": "Contact",
60 | "options": "Contact"
61 | },
62 | {
63 | "fieldname": "subject",
64 | "fieldtype": "Data",
65 | "label": "Subject",
66 | "print_hide": 1
67 | },
68 | {
69 | "fieldname": "template",
70 | "fieldtype": "Link",
71 | "label": "Load From Template",
72 | "options": "Business Letter Template",
73 | "print_hide": 1,
74 | "report_hide": 1
75 | },
76 | {
77 | "fieldname": "content",
78 | "fieldtype": "Text Editor",
79 | "label": "Content",
80 | "print_hide": 1
81 | },
82 | {
83 | "fieldname": "link_document_type",
84 | "fieldtype": "Link",
85 | "label": "Link Document Type",
86 | "options": "DocType",
87 | "print_hide": 1
88 | },
89 | {
90 | "fieldname": "link_title",
91 | "fieldtype": "Data",
92 | "label": "Link Title",
93 | "no_copy": 1,
94 | "print_hide": 1,
95 | "read_only": 1
96 | },
97 | {
98 | "fieldname": "link_name",
99 | "fieldtype": "Dynamic Link",
100 | "label": "Link Name",
101 | "options": "link_document_type",
102 | "print_hide": 1
103 | },
104 | {
105 | "fieldname": "reference_section",
106 | "fieldtype": "Section Break",
107 | "label": "Reference"
108 | },
109 | {
110 | "fieldname": "section_break_ikyq",
111 | "fieldtype": "Section Break"
112 | },
113 | {
114 | "fieldname": "column_break_ybiq",
115 | "fieldtype": "Column Break",
116 | "label": "Sender"
117 | },
118 | {
119 | "fieldname": "preview_tab",
120 | "fieldtype": "Tab Break",
121 | "label": "Preview"
122 | },
123 | {
124 | "depends_on": "eval:!cur_frm.doc.__unsaved",
125 | "fieldname": "subject_preview",
126 | "fieldtype": "Data",
127 | "label": "Subject (Preview)",
128 | "no_copy": 1,
129 | "read_only": 1
130 | },
131 | {
132 | "depends_on": "eval:!cur_frm.doc.__unsaved",
133 | "fieldname": "content_preview",
134 | "fieldtype": "Text Editor",
135 | "label": "Content (Preview)",
136 | "no_copy": 1,
137 | "read_only": 1
138 | },
139 | {
140 | "fieldname": "section_break_bsox",
141 | "fieldtype": "Section Break"
142 | },
143 | {
144 | "fieldname": "column_break_xyam",
145 | "fieldtype": "Column Break"
146 | },
147 | {
148 | "fieldname": "column_break_yebn",
149 | "fieldtype": "Column Break"
150 | },
151 | {
152 | "depends_on": "eval:cur_frm.doc.__unsaved",
153 | "fieldname": "html_notice",
154 | "fieldtype": "HTML",
155 | "no_copy": 1,
156 | "print_hide": 1,
157 | "report_hide": 1
158 | },
159 | {
160 | "fieldname": "html_help",
161 | "fieldtype": "HTML",
162 | "no_copy": 1,
163 | "print_hide": 1,
164 | "report_hide": 1
165 | },
166 | {
167 | "fieldname": "address_display",
168 | "fieldtype": "Small Text",
169 | "label": "Address",
170 | "no_copy": 1,
171 | "read_only": 1
172 | },
173 | {
174 | "fetch_from": "contact.email_id",
175 | "fieldname": "email_id",
176 | "fieldtype": "Data",
177 | "label": "Email",
178 | "no_copy": 1,
179 | "print_hide": 1,
180 | "read_only": 1
181 | },
182 | {
183 | "fieldname": "sdf_section",
184 | "fieldtype": "Section Break"
185 | },
186 | {
187 | "default": "Today",
188 | "fieldname": "date",
189 | "fieldtype": "Date",
190 | "label": "Date"
191 | },
192 | {
193 | "fieldname": "company",
194 | "fieldtype": "Link",
195 | "label": "Company",
196 | "options": "Company",
197 | "print_hide": 1,
198 | "remember_last_selected_value": 1
199 | },
200 | {
201 | "fieldname": "recipient_column",
202 | "fieldtype": "Column Break",
203 | "label": "Recipient"
204 | },
205 | {
206 | "fieldname": "department",
207 | "fieldtype": "Link",
208 | "label": "Department",
209 | "options": "Department"
210 | },
211 | {
212 | "fieldname": "employee",
213 | "fieldtype": "Link",
214 | "in_list_view": 1,
215 | "label": "Employee",
216 | "options": "Employee",
217 | "print_hide": 1
218 | },
219 | {
220 | "fetch_from": "employee.employee_name",
221 | "fieldname": "employee_name",
222 | "fieldtype": "Data",
223 | "label": "Employee Name",
224 | "read_only": 1
225 | }
226 | ],
227 | "index_web_pages_for_search": 1,
228 | "is_submittable": 1,
229 | "links": [],
230 | "modified": "2025-02-19 22:06:05.863438",
231 | "modified_by": "Administrator",
232 | "module": "ERPNext Germany",
233 | "name": "Business Letter",
234 | "owner": "Administrator",
235 | "permissions": [
236 | {
237 | "amend": 1,
238 | "cancel": 1,
239 | "create": 1,
240 | "delete": 1,
241 | "email": 1,
242 | "export": 1,
243 | "print": 1,
244 | "read": 1,
245 | "report": 1,
246 | "role": "System Manager",
247 | "share": 1,
248 | "submit": 1,
249 | "write": 1
250 | },
251 | {
252 | "create": 1,
253 | "delete": 1,
254 | "email": 1,
255 | "export": 1,
256 | "print": 1,
257 | "read": 1,
258 | "report": 1,
259 | "role": "Employee",
260 | "share": 1,
261 | "write": 1
262 | },
263 | {
264 | "amend": 1,
265 | "cancel": 1,
266 | "create": 1,
267 | "delete": 1,
268 | "email": 1,
269 | "export": 1,
270 | "if_owner": 1,
271 | "print": 1,
272 | "read": 1,
273 | "report": 1,
274 | "role": "Employee",
275 | "share": 1,
276 | "submit": 1,
277 | "write": 1
278 | }
279 | ],
280 | "show_title_field_in_link": 1,
281 | "sort_field": "modified",
282 | "sort_order": "DESC",
283 | "states": [],
284 | "title_field": "subject_preview"
285 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe import _
6 | from frappe.desk.doctype.notification_log.notification_log import get_title
7 | from frappe.model.document import Document
8 |
9 |
10 | class BusinessLetter(Document):
11 | def before_validate(self):
12 | self.set_subject_preview()
13 | self.set_content_preview()
14 | self.set_link_title()
15 |
16 | def set_subject_preview(self):
17 | self.subject_preview = (
18 | frappe.render_template(self.subject, self.get_context()) if self.subject else " "
19 | )
20 |
21 | def set_content_preview(self):
22 | self.content_preview = (
23 | frappe.render_template(self.content, self.get_context()) if self.content else " "
24 | )
25 |
26 | def set_link_title(self):
27 | self.link_title = get_title(self.link_document_type, self.link_name) if self.link_name else None
28 |
29 | def get_context(self):
30 | address = frappe.get_doc("Address", self.address) if self.address else None
31 | contact = frappe.get_doc("Contact", self.contact) if self.contact else None
32 | reference = frappe.get_doc(self.link_document_type, self.link_name) if self.link_name else None
33 |
34 | return {
35 | "address": address,
36 | "contact": contact,
37 | "reference": reference,
38 | }
39 |
40 | def on_submit(self):
41 | self.add_comments(
42 | _("submitted Business Letter {0}").format(
43 | f"{self.subject_preview}"
44 | )
45 | )
46 |
47 | def on_cancel(self):
48 | self.add_comments(
49 | _("cancelled Business Letter {0}").format(
50 | f"{self.subject_preview}"
51 | )
52 | )
53 |
54 | def add_comments(self, msg):
55 | if self.contact:
56 | frappe.get_doc("Contact", self.contact).add_comment("Info", msg)
57 |
58 | if self.address:
59 | frappe.get_doc("Address", self.address).add_comment("Info", msg)
60 |
61 | if self.link_name:
62 | frappe.get_doc(self.link_document_type, self.link_name).add_comment("Info", msg)
63 |
64 | @frappe.whitelist()
65 | def get_template(self):
66 | subject = frappe.get_value("Business Letter Template", self.template, "subject")
67 | content = frappe.get_value("Business Letter Template", self.template, "content")
68 |
69 | return {"subject": subject, "content": content}
70 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter/test_business_letter.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestBusinessLetter(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter_template/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_letter_template/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("Business Letter Template", {
5 | onload(frm) {
6 | set_html_data(frm);
7 | },
8 | });
9 |
10 | function set_html_data(frm) {
11 | frm.get_field("html_help").$wrapper.html(
12 | `${erpnext_germany.business_letter.get_help_text()}
`
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "autoname": "prompt",
5 | "creation": "2024-02-02 10:57:28.464825",
6 | "doctype": "DocType",
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "subject",
10 | "content",
11 | "html_help"
12 | ],
13 | "fields": [
14 | {
15 | "fieldname": "content",
16 | "fieldtype": "Text Editor",
17 | "label": "Content"
18 | },
19 | {
20 | "fieldname": "subject",
21 | "fieldtype": "Data",
22 | "label": "Subject"
23 | },
24 | {
25 | "fieldname": "html_help",
26 | "fieldtype": "HTML"
27 | }
28 | ],
29 | "links": [
30 | {
31 | "link_doctype": "Business Letter",
32 | "link_fieldname": "template"
33 | }
34 | ],
35 | "modified": "2024-03-12 15:04:47.387792",
36 | "modified_by": "Administrator",
37 | "module": "ERPNext Germany",
38 | "name": "Business Letter Template",
39 | "naming_rule": "Set by user",
40 | "owner": "Administrator",
41 | "permissions": [
42 | {
43 | "create": 1,
44 | "delete": 1,
45 | "email": 1,
46 | "export": 1,
47 | "print": 1,
48 | "read": 1,
49 | "report": 1,
50 | "role": "System Manager",
51 | "share": 1,
52 | "write": 1
53 | }
54 | ],
55 | "sort_field": "modified",
56 | "sort_order": "DESC",
57 | "states": []
58 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH 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 BusinessLetterTemplate(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_letter_template/test_business_letter_template.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestBusinessLetterTemplate(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_trip/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("Business Trip", {
5 | setup(frm) {
6 | frm.set_query("employee", erpnext.queries.employee);
7 | frm.set_query("region", (doc) => {
8 | return {
9 | filters: {
10 | valid_from: ["<=", doc.from_date],
11 | },
12 | };
13 | });
14 | },
15 |
16 | from_date: function (frm) {
17 | if (!frm.doc.to_date) {
18 | frm.set_value("to_date", frm.doc.from_date);
19 | }
20 |
21 | frm.fields_dict.to_date.datepicker.update({
22 | minDate: frm.doc.from_date ? new Date(frm.doc.from_date) : null,
23 | });
24 | },
25 |
26 | to_date: function (frm) {
27 | frm.fields_dict.from_date.datepicker.update({
28 | maxDate: frm.doc.to_date ? new Date(frm.doc.to_date) : null,
29 | });
30 | },
31 | });
32 |
33 | frappe.ui.form.on("Business Trip Journey", {
34 | journeys_add(frm, cdt, cdn) {
35 | frappe.model.set_value(cdt, cdn, "date", frm.doc.from_date);
36 | },
37 | });
38 |
39 | frappe.ui.form.on("Business Trip Accommodation", {
40 | accommodations_add(frm, cdt, cdn) {
41 | frappe.model.set_value(cdt, cdn, "from_date", frm.doc.from_date);
42 | frappe.model.set_value(cdt, cdn, "to_date", frm.doc.to_date);
43 | },
44 | });
45 |
46 | frappe.ui.form.on("Business Trip Allowance", {
47 | allowances_add(frm) {
48 | if (!frm.doc.from_date || !frm.doc.to_date) {
49 | frappe.msgprint(__("Please enter a start and end date of the trip!"));
50 | return;
51 | }
52 |
53 | let start = new Date(frm.doc.from_date);
54 | let end = new Date(frm.doc.to_date);
55 |
56 | if (end < start) {
57 | frappe.msgprint(__("The end date should not be before the start date!"));
58 | return;
59 | }
60 |
61 | if (frm.doc.allowances && frm.doc.allowances.length == 1 && !frm.doc.allowances[0].date) {
62 | frm.clear_table("allowances");
63 |
64 | for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
65 | let child = frm.add_child("allowances");
66 | let d_string = d.toISOString().slice(0, 10);
67 |
68 | frappe.model.set_value(child.doctype, child.name, "date", d_string);
69 |
70 | if (d_string == frm.doc.from_date && d_string == frm.doc.to_date) {
71 | continue;
72 | } else if (d_string == frm.doc.from_date) {
73 | frappe.model.set_value(child.doctype, child.name, "to_time", "23:59");
74 | } else if (d_string == frm.doc.to_date) {
75 | frappe.model.set_value(child.doctype, child.name, "from_time", "00:00");
76 | } else {
77 | frappe.model.set_value(child.doctype, child.name, "whole_day", true);
78 | frappe.model.set_value(child.doctype, child.name, "from_time", "00:00");
79 | frappe.model.set_value(child.doctype, child.name, "to_time", "23:59");
80 | }
81 | }
82 |
83 | frm.refresh_field("allowances");
84 | }
85 | },
86 | });
87 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "autoname": "BT-.####",
4 | "creation": "2022-10-12 21:52:42.547420",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "employee",
10 | "company",
11 | "currency",
12 | "title",
13 | "column_break_2",
14 | "employee_name",
15 | "status",
16 | "section_break_4",
17 | "from_date",
18 | "column_break_6",
19 | "to_date",
20 | "section_break_8",
21 | "project",
22 | "cost_center",
23 | "column_break_10",
24 | "customer",
25 | "section_break_12",
26 | "journeys",
27 | "total_mileage_allowance",
28 | "section_break_14",
29 | "accommodations",
30 | "section_break_16",
31 | "region",
32 | "allowances",
33 | "total_allowance",
34 | "amended_from"
35 | ],
36 | "fields": [
37 | {
38 | "fieldname": "employee",
39 | "fieldtype": "Link",
40 | "in_standard_filter": 1,
41 | "label": "Employee",
42 | "options": "Employee",
43 | "reqd": 1
44 | },
45 | {
46 | "fetch_from": "employee.company",
47 | "fieldname": "company",
48 | "fieldtype": "Link",
49 | "label": "Company",
50 | "options": "Company"
51 | },
52 | {
53 | "fetch_from": "company.default_currency",
54 | "fieldname": "currency",
55 | "fieldtype": "Link",
56 | "label": "Currency",
57 | "options": "Currency"
58 | },
59 | {
60 | "fieldname": "title",
61 | "fieldtype": "Data",
62 | "label": "Title",
63 | "reqd": 1
64 | },
65 | {
66 | "fieldname": "column_break_2",
67 | "fieldtype": "Column Break"
68 | },
69 | {
70 | "fetch_from": "employee.employee_name",
71 | "fieldname": "employee_name",
72 | "fieldtype": "Data",
73 | "in_list_view": 1,
74 | "label": "Employee Name",
75 | "read_only": 1
76 | },
77 | {
78 | "allow_on_submit": 1,
79 | "depends_on": "eval:!doc.__islocal",
80 | "fieldname": "status",
81 | "fieldtype": "Select",
82 | "label": "Status",
83 | "options": "\nSubmitted\nApproved\nRejected\nPaid\nBilled"
84 | },
85 | {
86 | "fieldname": "section_break_4",
87 | "fieldtype": "Section Break"
88 | },
89 | {
90 | "fieldname": "from_date",
91 | "fieldtype": "Date",
92 | "in_list_view": 1,
93 | "label": "From Date",
94 | "reqd": 1
95 | },
96 | {
97 | "fieldname": "column_break_6",
98 | "fieldtype": "Column Break"
99 | },
100 | {
101 | "fieldname": "to_date",
102 | "fieldtype": "Date",
103 | "in_list_view": 1,
104 | "label": "To Date",
105 | "reqd": 1
106 | },
107 | {
108 | "fieldname": "section_break_8",
109 | "fieldtype": "Section Break"
110 | },
111 | {
112 | "fieldname": "project",
113 | "fieldtype": "Link",
114 | "in_standard_filter": 1,
115 | "label": "Project",
116 | "options": "Project"
117 | },
118 | {
119 | "fieldname": "column_break_10",
120 | "fieldtype": "Column Break"
121 | },
122 | {
123 | "fetch_from": "project.customer",
124 | "fieldname": "customer",
125 | "fieldtype": "Link",
126 | "label": "Customer",
127 | "options": "Customer"
128 | },
129 | {
130 | "fieldname": "section_break_12",
131 | "fieldtype": "Section Break"
132 | },
133 | {
134 | "fieldname": "journeys",
135 | "fieldtype": "Table",
136 | "label": "Journeys",
137 | "options": "Business Trip Journey"
138 | },
139 | {
140 | "fieldname": "section_break_14",
141 | "fieldtype": "Section Break"
142 | },
143 | {
144 | "fieldname": "accommodations",
145 | "fieldtype": "Table",
146 | "label": "Accommodations",
147 | "options": "Business Trip Accommodation"
148 | },
149 | {
150 | "fieldname": "section_break_16",
151 | "fieldtype": "Section Break"
152 | },
153 | {
154 | "fieldname": "region",
155 | "fieldtype": "Link",
156 | "label": "Region",
157 | "options": "Business Trip Region",
158 | "reqd": 1
159 | },
160 | {
161 | "fieldname": "allowances",
162 | "fieldtype": "Table",
163 | "label": "Allowances",
164 | "options": "Business Trip Allowance"
165 | },
166 | {
167 | "fieldname": "total_allowance",
168 | "fieldtype": "Currency",
169 | "label": "Total Allowance",
170 | "read_only": 1
171 | },
172 | {
173 | "fieldname": "amended_from",
174 | "fieldtype": "Link",
175 | "label": "Amended From",
176 | "no_copy": 1,
177 | "options": "Business Trip",
178 | "print_hide": 1,
179 | "read_only": 1
180 | },
181 | {
182 | "fieldname": "cost_center",
183 | "fieldtype": "Link",
184 | "in_standard_filter": 1,
185 | "label": "Cost Center",
186 | "options": "Cost Center"
187 | },
188 | {
189 | "description": "Paid only for Car (private)",
190 | "fieldname": "total_mileage_allowance",
191 | "fieldtype": "Currency",
192 | "label": "Total Mileage Allowance",
193 | "read_only": 1
194 | }
195 | ],
196 | "index_web_pages_for_search": 1,
197 | "is_submittable": 1,
198 | "links": [
199 | {
200 | "link_doctype": "Expense Claim",
201 | "link_fieldname": "business_trip"
202 | }
203 | ],
204 | "modified": "2025-02-20 10:58:45.064314",
205 | "modified_by": "Administrator",
206 | "module": "ERPNext Germany",
207 | "name": "Business Trip",
208 | "naming_rule": "Expression (old style)",
209 | "owner": "Administrator",
210 | "permissions": [
211 | {
212 | "amend": 1,
213 | "cancel": 1,
214 | "create": 1,
215 | "delete": 1,
216 | "email": 1,
217 | "export": 1,
218 | "print": 1,
219 | "read": 1,
220 | "report": 1,
221 | "role": "System Manager",
222 | "share": 1,
223 | "submit": 1,
224 | "write": 1
225 | },
226 | {
227 | "amend": 1,
228 | "cancel": 1,
229 | "create": 1,
230 | "delete": 1,
231 | "email": 1,
232 | "export": 1,
233 | "print": 1,
234 | "read": 1,
235 | "report": 1,
236 | "role": "Projects Manager",
237 | "share": 1,
238 | "submit": 1,
239 | "write": 1
240 | },
241 | {
242 | "create": 1,
243 | "delete": 1,
244 | "email": 1,
245 | "export": 1,
246 | "if_owner": 1,
247 | "print": 1,
248 | "read": 1,
249 | "report": 1,
250 | "role": "Employee",
251 | "share": 1,
252 | "write": 1
253 | },
254 | {
255 | "email": 1,
256 | "export": 1,
257 | "print": 1,
258 | "read": 1,
259 | "report": 1,
260 | "role": "Accounts User"
261 | }
262 | ],
263 | "sort_field": "modified",
264 | "sort_order": "DESC",
265 | "states": [],
266 | "title_field": "title"
267 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe import get_installed_apps
6 | from frappe.model.document import Document
7 | from frappe.utils.data import fmt_money
8 |
9 | DEFAULT_EXPENSE_CLAIM_TYPE = "Additional meal expenses"
10 |
11 |
12 | class BusinessTrip(Document):
13 | def before_save(self):
14 | self.reset_distance()
15 | self.set_regional_amount()
16 | self.set_whole_day_time()
17 | self.calculate_total()
18 | self.calculate_total_mileage_allowance()
19 |
20 | def validate(self):
21 | self.validate_from_to_dates("from_date", "to_date")
22 |
23 | def set_regional_amount(self):
24 | if not self.region:
25 | return
26 |
27 | region = frappe.get_doc("Business Trip Region", self.region)
28 | whole_day = region.get("whole_day", 0.0)
29 | arrival_or_departure = region.get("arrival_or_departure", 0.0)
30 | accomodation = region.get("accomodation", 0.0)
31 |
32 | for allowance in self.allowances:
33 | amount = whole_day if allowance.whole_day else arrival_or_departure
34 | if allowance.breakfast_was_provided:
35 | amount -= whole_day * 0.2
36 |
37 | if allowance.lunch_was_provided:
38 | amount -= whole_day * 0.4
39 |
40 | if allowance.dinner_was_provided:
41 | amount -= whole_day * 0.4
42 |
43 | if not allowance.accommodation_was_provided:
44 | amount += accomodation
45 |
46 | allowance.amount = max(amount, 0.0)
47 |
48 | def reset_distance(self):
49 | for journey in self.journeys:
50 | if journey.mode_of_transport != "Car (private)":
51 | journey.distance = 0
52 |
53 | def set_whole_day_time(self):
54 | for allowance in self.allowances:
55 | if allowance.whole_day:
56 | allowance.from_time = "00:00"
57 | allowance.to_time = "23:59"
58 |
59 | def calculate_total(self):
60 | self.total_allowance = sum(allowance.amount for allowance in self.allowances)
61 |
62 | def calculate_total_mileage_allowance(self):
63 | mileage_allowance = frappe.db.get_single_value("Business Trip Settings", "mileage_allowance") or 0
64 | self.total_mileage_allowance = sum(journey.distance for journey in self.journeys) * mileage_allowance
65 |
66 | def before_submit(self):
67 | self.status = "Submitted"
68 |
69 | def on_submit(self):
70 | if not self.allowances and not self.journeys:
71 | return
72 |
73 | if "hrms" not in get_installed_apps():
74 | return
75 |
76 | settings = frappe.get_single("Business Trip Settings")
77 | expenses = get_mileage_allowances(
78 | self,
79 | expense_claim_type=settings.expense_claim_type_car or DEFAULT_EXPENSE_CLAIM_TYPE,
80 | mileage_allowance=settings.mileage_allowance or 0.0,
81 | )
82 | expenses.extend(get_meal_expenses(self, settings.expense_claim_type or DEFAULT_EXPENSE_CLAIM_TYPE))
83 |
84 | if not expenses:
85 | return
86 |
87 | # Create Expense Claim for Car (private) and Allowance
88 | expense_claim = frappe.new_doc("Expense Claim")
89 | expense_claim.update(
90 | {
91 | "employee": self.employee,
92 | "company": self.company,
93 | "posting_date": frappe.utils.today(),
94 | "business_trip": self.name,
95 | "project": self.project,
96 | "cost_center": self.cost_center,
97 | }
98 | )
99 | expense_claim.extend("expenses", expenses)
100 | expense_claim.save()
101 |
102 |
103 | def get_mileage_allowances(
104 | business_trip: BusinessTrip, expense_claim_type: str, mileage_allowance: float
105 | ) -> list[dict]:
106 | """Return a list of expense claim rows for mileage allowances."""
107 | expenses = []
108 | for journey in business_trip.journeys:
109 | if journey.mode_of_transport != "Car (private)":
110 | continue
111 |
112 | description = (
113 | "{distance} * {mileage_allowance} von {from_place} nach {to_place} (Fahrt mit Privatauto)".format(
114 | distance=journey.get_formatted("distance"),
115 | mileage_allowance=fmt_money(mileage_allowance),
116 | from_place=getattr(journey, "from"),
117 | to_place=journey.to,
118 | )
119 | )
120 | mileage_amount = journey.distance * mileage_allowance
121 | expenses.append(
122 | {
123 | "expense_date": journey.date,
124 | "expense_type": expense_claim_type,
125 | "description": description,
126 | "amount": mileage_amount,
127 | "sanctioned_amount": mileage_amount,
128 | "project": business_trip.project,
129 | "cost_center": business_trip.cost_center,
130 | },
131 | )
132 |
133 | return expenses
134 |
135 |
136 | def get_meal_expenses(business_trip: BusinessTrip, expense_claim_type: str) -> list[dict]:
137 | """Return a list of expense claim rows for meal expenses"""
138 | expenses = []
139 | for allowance in business_trip.allowances:
140 | description = "Ganztägig" if allowance.whole_day else "An-/Abreise"
141 | if not allowance.accommodation_was_provided and frappe.db.get_value(
142 | "Business Trip Region", business_trip.region, "accommodation"
143 | ):
144 | description += ", zzgl. Hotel"
145 |
146 | if allowance.breakfast_was_provided:
147 | description += ", abzügl. Frühstück"
148 |
149 | if allowance.lunch_was_provided:
150 | description += ", abzügl. Mittagessen"
151 |
152 | if allowance.dinner_was_provided:
153 | description += ", abzügl. Abendessen"
154 |
155 | expenses.append(
156 | {
157 | "expense_date": allowance.date,
158 | "expense_type": expense_claim_type,
159 | "description": description,
160 | "amount": allowance.amount,
161 | "sanctioned_amount": allowance.amount,
162 | "project": business_trip.project,
163 | "cost_center": business_trip.cost_center,
164 | },
165 | )
166 |
167 | return expenses
168 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip/business_trip_list.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.listview_settings["Business Trip"] = {
5 | get_indicator: function (doc) {
6 | const colors = {
7 | Draft: "red",
8 | Approved: "yellow",
9 | Paid: "blue",
10 | Billed: "green",
11 | };
12 | return [__(doc.status), colors[doc.status], "status,=," + doc.status];
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip/test_business_trip.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestBusinessTrip(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/business_trip_accommodation.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2022-10-12 22:02:25.775886",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "from_date",
10 | "to_date",
11 | "city",
12 | "receipt"
13 | ],
14 | "fields": [
15 | {
16 | "fieldname": "from_date",
17 | "fieldtype": "Date",
18 | "in_list_view": 1,
19 | "label": "From Date",
20 | "reqd": 1
21 | },
22 | {
23 | "fieldname": "to_date",
24 | "fieldtype": "Date",
25 | "in_list_view": 1,
26 | "label": "To Date",
27 | "reqd": 1
28 | },
29 | {
30 | "fieldname": "city",
31 | "fieldtype": "Data",
32 | "in_list_view": 1,
33 | "label": "City",
34 | "reqd": 1
35 | },
36 | {
37 | "fieldname": "receipt",
38 | "fieldtype": "Attach",
39 | "in_list_view": 1,
40 | "label": "Receipt"
41 | }
42 | ],
43 | "index_web_pages_for_search": 1,
44 | "istable": 1,
45 | "links": [],
46 | "modified": "2024-09-04 22:59:37.891096",
47 | "modified_by": "Administrator",
48 | "module": "ERPNext Germany",
49 | "name": "Business Trip Accommodation",
50 | "owner": "Administrator",
51 | "permissions": [],
52 | "sort_field": "modified",
53 | "sort_order": "DESC",
54 | "states": []
55 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/business_trip_accommodation.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH 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 BusinessTripAccommodation(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_allowance/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_trip_allowance/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2022-11-01 20:32:56.339187",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "date",
10 | "whole_day",
11 | "from_time",
12 | "to_time",
13 | "breakfast_was_provided",
14 | "lunch_was_provided",
15 | "dinner_was_provided",
16 | "accommodation_was_provided",
17 | "amount"
18 | ],
19 | "fields": [
20 | {
21 | "columns": 2,
22 | "fieldname": "date",
23 | "fieldtype": "Date",
24 | "in_list_view": 1,
25 | "label": "Date",
26 | "reqd": 1
27 | },
28 | {
29 | "columns": 1,
30 | "default": "0",
31 | "fieldname": "whole_day",
32 | "fieldtype": "Check",
33 | "in_list_view": 1,
34 | "label": "Whole Day"
35 | },
36 | {
37 | "columns": 1,
38 | "fieldname": "from_time",
39 | "fieldtype": "Time",
40 | "label": "From Time",
41 | "mandatory_depends_on": "eval:!doc.whole_day",
42 | "read_only_depends_on": "eval:doc.whole_day"
43 | },
44 | {
45 | "fieldname": "to_time",
46 | "fieldtype": "Time",
47 | "label": "To Time",
48 | "mandatory_depends_on": "eval:!doc.whole_day",
49 | "read_only_depends_on": "eval:doc.whole_day"
50 | },
51 | {
52 | "columns": 2,
53 | "default": "1",
54 | "fieldname": "breakfast_was_provided",
55 | "fieldtype": "Check",
56 | "in_list_view": 1,
57 | "label": "Breakfast was Provided"
58 | },
59 | {
60 | "columns": 2,
61 | "default": "0",
62 | "fieldname": "lunch_was_provided",
63 | "fieldtype": "Check",
64 | "in_list_view": 1,
65 | "label": "Lunch was Provided"
66 | },
67 | {
68 | "columns": 2,
69 | "default": "0",
70 | "fieldname": "dinner_was_provided",
71 | "fieldtype": "Check",
72 | "in_list_view": 1,
73 | "label": "Dinner was Provided"
74 | },
75 | {
76 | "default": "1",
77 | "fieldname": "accommodation_was_provided",
78 | "fieldtype": "Check",
79 | "label": "Accommodation was Provided"
80 | },
81 | {
82 | "columns": 1,
83 | "fieldname": "amount",
84 | "fieldtype": "Currency",
85 | "in_list_view": 1,
86 | "label": "Amount",
87 | "read_only": 1
88 | }
89 | ],
90 | "index_web_pages_for_search": 1,
91 | "istable": 1,
92 | "links": [],
93 | "modified": "2025-02-20 15:25:04.594624",
94 | "modified_by": "Administrator",
95 | "module": "ERPNext Germany",
96 | "name": "Business Trip Allowance",
97 | "owner": "Administrator",
98 | "permissions": [],
99 | "sort_field": "modified",
100 | "sort_order": "DESC",
101 | "states": []
102 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH 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 BusinessTripAllowance(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_journey/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_trip_journey/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "creation": "2022-10-12 21:57:22.305645",
4 | "doctype": "DocType",
5 | "editable_grid": 1,
6 | "engine": "InnoDB",
7 | "field_order": [
8 | "date",
9 | "mode_of_transport",
10 | "from",
11 | "to",
12 | "distance",
13 | "receipt"
14 | ],
15 | "fields": [
16 | {
17 | "fieldname": "date",
18 | "fieldtype": "Date",
19 | "in_list_view": 1,
20 | "label": "Date",
21 | "reqd": 1
22 | },
23 | {
24 | "fieldname": "mode_of_transport",
25 | "fieldtype": "Select",
26 | "in_list_view": 1,
27 | "label": "Mode of Transport",
28 | "options": "Car\nCar (private)\nCar (rental)\nTaxi\nBus\nTrain\nAirplane\nPublic Transport",
29 | "reqd": 1
30 | },
31 | {
32 | "fieldname": "from",
33 | "fieldtype": "Data",
34 | "in_list_view": 1,
35 | "label": "From",
36 | "reqd": 1
37 | },
38 | {
39 | "fieldname": "to",
40 | "fieldtype": "Data",
41 | "in_list_view": 1,
42 | "label": "To",
43 | "reqd": 1
44 | },
45 | {
46 | "fieldname": "receipt",
47 | "fieldtype": "Attach",
48 | "in_list_view": 1,
49 | "label": "Receipt"
50 | },
51 | {
52 | "depends_on": "eval:doc.mode_of_transport===\"Car (private)\"",
53 | "description": "Used to calculate the total Mileage Allowance",
54 | "fieldname": "distance",
55 | "fieldtype": "Int",
56 | "label": "Distance",
57 | "non_negative": 1
58 | }
59 | ],
60 | "index_web_pages_for_search": 1,
61 | "istable": 1,
62 | "links": [],
63 | "modified": "2025-02-20 11:01:39.706560",
64 | "modified_by": "Administrator",
65 | "module": "ERPNext Germany",
66 | "name": "Business Trip Journey",
67 | "owner": "Administrator",
68 | "permissions": [],
69 | "sort_field": "modified",
70 | "sort_order": "DESC",
71 | "states": []
72 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH 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 BusinessTripJourney(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_region/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_trip_region/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("Business Trip Region", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "autoname": "hash",
4 | "creation": "2022-11-01 20:24:28.877901",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "title",
10 | "whole_day",
11 | "arrival_or_departure",
12 | "accommodation",
13 | "column_break_ajul",
14 | "valid_from",
15 | "disabled"
16 | ],
17 | "fields": [
18 | {
19 | "fieldname": "title",
20 | "fieldtype": "Data",
21 | "label": "Title"
22 | },
23 | {
24 | "description": "For an absence of at least 24 hours per calendar day",
25 | "fieldname": "whole_day",
26 | "fieldtype": "Currency",
27 | "in_list_view": 1,
28 | "label": "Whole Day"
29 | },
30 | {
31 | "description": "For the day of arrival and departure and for an absence of more than 8 hours per calendar day",
32 | "fieldname": "arrival_or_departure",
33 | "fieldtype": "Currency",
34 | "in_list_view": 1,
35 | "label": "Arrival or Departure"
36 | },
37 | {
38 | "description": "Lump sum for accommodation expenses",
39 | "fieldname": "accommodation",
40 | "fieldtype": "Currency",
41 | "in_list_view": 1,
42 | "label": "Accommodation"
43 | },
44 | {
45 | "fieldname": "valid_from",
46 | "fieldtype": "Date",
47 | "in_list_view": 1,
48 | "label": "Valid From"
49 | },
50 | {
51 | "fieldname": "column_break_ajul",
52 | "fieldtype": "Column Break"
53 | },
54 | {
55 | "default": "0",
56 | "fieldname": "disabled",
57 | "fieldtype": "Check",
58 | "label": "Disabled"
59 | }
60 | ],
61 | "index_web_pages_for_search": 1,
62 | "links": [
63 | {
64 | "link_doctype": "Business Trip",
65 | "link_fieldname": "region"
66 | }
67 | ],
68 | "modified": "2024-10-24 22:42:37.127349",
69 | "modified_by": "Administrator",
70 | "module": "ERPNext Germany",
71 | "name": "Business Trip Region",
72 | "naming_rule": "Random",
73 | "owner": "Administrator",
74 | "permissions": [
75 | {
76 | "create": 1,
77 | "delete": 1,
78 | "email": 1,
79 | "export": 1,
80 | "print": 1,
81 | "read": 1,
82 | "report": 1,
83 | "role": "System Manager",
84 | "share": 1,
85 | "write": 1
86 | },
87 | {
88 | "role": "Employee",
89 | "select": 1
90 | },
91 | {
92 | "create": 1,
93 | "read": 1,
94 | "role": "Accounts User",
95 | "write": 1
96 | }
97 | ],
98 | "search_fields": "valid_from",
99 | "show_title_field_in_link": 1,
100 | "sort_field": "modified",
101 | "sort_order": "DESC",
102 | "states": [],
103 | "title_field": "title"
104 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH 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 BusinessTripRegion(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region_list.js:
--------------------------------------------------------------------------------
1 | frappe.listview_settings["Business Trip Region"] = {
2 | hide_name_column: true,
3 | };
4 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_region/test_business_trip_region.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestBusinessTripRegion(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/business_trip_settings/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("Business Trip Settings", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2025-02-20 11:02:49.284472",
5 | "doctype": "DocType",
6 | "engine": "InnoDB",
7 | "field_order": [
8 | "expense_claim_type",
9 | "expense_claim_type_car",
10 | "mileage_allowance"
11 | ],
12 | "fields": [
13 | {
14 | "description": "value / distance: e.g. 0.30\u20ac/km",
15 | "fieldname": "mileage_allowance",
16 | "fieldtype": "Currency",
17 | "in_list_view": 1,
18 | "label": "Mileage Allowance",
19 | "non_negative": 1,
20 | "precision": "2",
21 | "reqd": 1
22 | },
23 | {
24 | "fieldname": "expense_claim_type",
25 | "fieldtype": "Link",
26 | "label": "Expense Claim Type for Additional Meal Expenses",
27 | "options": "Expense Claim Type",
28 | "reqd": 1
29 | },
30 | {
31 | "fieldname": "expense_claim_type_car",
32 | "fieldtype": "Link",
33 | "label": "Expense Claim Type for Mileage Allowance",
34 | "options": "Expense Claim Type",
35 | "reqd": 1
36 | }
37 | ],
38 | "index_web_pages_for_search": 1,
39 | "issingle": 1,
40 | "links": [],
41 | "modified": "2025-02-26 12:23:22.874639",
42 | "modified_by": "Administrator",
43 | "module": "ERPNext Germany",
44 | "name": "Business Trip Settings",
45 | "owner": "Administrator",
46 | "permissions": [
47 | {
48 | "create": 1,
49 | "delete": 1,
50 | "email": 1,
51 | "print": 1,
52 | "read": 1,
53 | "role": "System Manager",
54 | "share": 1,
55 | "write": 1
56 | }
57 | ],
58 | "sort_field": "modified",
59 | "sort_order": "DESC",
60 | "states": []
61 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, ALYF GmbH 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 BusinessTripSettings(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/business_trip_settings/test_business_trip_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestBusinessTripSettings(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/erpnext_germany_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/erpnext_germany_settings/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/erpnext_germany_settings/erpnext_germany_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("ERPNext Germany Settings", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/erpnext_germany_settings/erpnext_germany_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2025-02-23 17:36:40.481091",
5 | "doctype": "DocType",
6 | "engine": "InnoDB",
7 | "field_order": [
8 | "prevent_gaps_in_transaction_naming"
9 | ],
10 | "fields": [
11 | {
12 | "default": "1",
13 | "description": "Allow deletion of the most recent record only. Applies to Quotation, Sales Order and Sales Invoice.",
14 | "fieldname": "prevent_gaps_in_transaction_naming",
15 | "fieldtype": "Check",
16 | "label": "Prevent Gaps In Transaction Naming"
17 | }
18 | ],
19 | "index_web_pages_for_search": 1,
20 | "issingle": 1,
21 | "links": [],
22 | "modified": "2025-02-23 17:38:08.351007",
23 | "modified_by": "Administrator",
24 | "module": "ERPNext Germany",
25 | "name": "ERPNext Germany Settings",
26 | "owner": "Administrator",
27 | "permissions": [
28 | {
29 | "create": 1,
30 | "delete": 1,
31 | "email": 1,
32 | "print": 1,
33 | "read": 1,
34 | "role": "System Manager",
35 | "share": 1,
36 | "write": 1
37 | }
38 | ],
39 | "sort_field": "modified",
40 | "sort_order": "DESC",
41 | "states": []
42 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/erpnext_germany_settings/erpnext_germany_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, ALYF GmbH 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 ERPNextGermanySettings(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/erpnext_germany_settings/test_erpnext_germany_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestERPNextGermanySettings(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/religious_denomination/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/religious_denomination/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2022, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("Religious Denomination", {});
5 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_import": 1,
4 | "allow_rename": 1,
5 | "autoname": "prompt",
6 | "creation": "2022-06-23 23:04:39.683289",
7 | "doctype": "DocType",
8 | "editable_grid": 1,
9 | "engine": "InnoDB",
10 | "field_order": [
11 | "title"
12 | ],
13 | "fields": [
14 | {
15 | "fieldname": "title",
16 | "fieldtype": "Data",
17 | "in_list_view": 1,
18 | "label": "Title",
19 | "reqd": 1
20 | }
21 | ],
22 | "links": [],
23 | "modified": "2022-06-23 23:09:54.546740",
24 | "modified_by": "Administrator",
25 | "module": "ERPNext Germany",
26 | "name": "Religious Denomination",
27 | "naming_rule": "Set by user",
28 | "owner": "Administrator",
29 | "permissions": [
30 | {
31 | "create": 1,
32 | "delete": 1,
33 | "email": 1,
34 | "export": 1,
35 | "print": 1,
36 | "read": 1,
37 | "report": 1,
38 | "role": "System Manager",
39 | "share": 1,
40 | "write": 1
41 | },
42 | {
43 | "create": 1,
44 | "delete": 1,
45 | "email": 1,
46 | "export": 1,
47 | "import": 1,
48 | "print": 1,
49 | "read": 1,
50 | "report": 1,
51 | "role": "HR Manager",
52 | "share": 1,
53 | "write": 1
54 | },
55 | {
56 | "read": 1,
57 | "role": "HR User"
58 | }
59 | ],
60 | "search_fields": "title",
61 | "show_title_field_in_link": 1,
62 | "sort_field": "name",
63 | "sort_order": "ASC",
64 | "states": [],
65 | "title_field": "title"
66 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2022, ALYF GmbH 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 ReligiousDenomination(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/religious_denomination/test_religious_denomination.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2022, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestReligiousDenomination(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/vat_id_check/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/doctype/vat_id_check/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/vat_id_check/test_vat_id_check.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, ALYF GmbH and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests.utils import FrappeTestCase
6 |
7 |
8 | class TestVATIDCheck(FrappeTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("VAT ID Check", {
5 | setup: function (frm) {
6 | frm.add_fetch("party", "tax_id", "party_vat_id");
7 |
8 | frm.set_query("party_type", function (doc) {
9 | return {
10 | filters: {
11 | name: ["in", ["Customer", "Supplier"]],
12 | },
13 | };
14 | });
15 |
16 | frm.set_query("party_address", function (doc) {
17 | return {
18 | query: "frappe.contacts.doctype.address.address.address_query",
19 | filters: {
20 | link_doctype: doc.party_type,
21 | link_name: doc.party,
22 | },
23 | };
24 | });
25 | },
26 | });
27 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "creation": "2023-03-26 18:28:14.770174",
4 | "default_view": "List",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "party_type",
10 | "party",
11 | "party_vat_id",
12 | "party_address",
13 | "column_break_hmgxr",
14 | "status",
15 | "section_break_6ctcl",
16 | "trader_name",
17 | "trader_street",
18 | "trader_postcode",
19 | "trader_city",
20 | "column_break_cxvts",
21 | "trader_name_match",
22 | "trader_street_match",
23 | "trader_postcode_match",
24 | "trader_city_match",
25 | "column_break_cjvyk",
26 | "actual_trader_name",
27 | "actual_trader_address",
28 | "section_break_xljfy",
29 | "company",
30 | "column_break_scjlu",
31 | "requester_vat_id",
32 | "section_break_dowxn",
33 | "is_valid",
34 | "request_id"
35 | ],
36 | "fields": [
37 | {
38 | "fieldname": "column_break_hmgxr",
39 | "fieldtype": "Column Break"
40 | },
41 | {
42 | "fieldname": "request_id",
43 | "fieldtype": "Data",
44 | "in_list_view": 1,
45 | "label": "Request ID",
46 | "no_copy": 1,
47 | "read_only": 1
48 | },
49 | {
50 | "default": "0",
51 | "fieldname": "is_valid",
52 | "fieldtype": "Check",
53 | "label": "Is Valid",
54 | "no_copy": 1,
55 | "read_only": 1
56 | },
57 | {
58 | "fieldname": "company",
59 | "fieldtype": "Link",
60 | "label": "Company",
61 | "options": "Company",
62 | "set_only_once": 1
63 | },
64 | {
65 | "fieldname": "section_break_xljfy",
66 | "fieldtype": "Section Break",
67 | "label": "Requester"
68 | },
69 | {
70 | "fieldname": "column_break_scjlu",
71 | "fieldtype": "Column Break"
72 | },
73 | {
74 | "depends_on": "eval:!doc.__islocal",
75 | "fieldname": "section_break_dowxn",
76 | "fieldtype": "Section Break",
77 | "label": "Result"
78 | },
79 | {
80 | "fetch_from": "company.tax_id",
81 | "fetch_if_empty": 1,
82 | "fieldname": "requester_vat_id",
83 | "fieldtype": "Data",
84 | "label": "Requester VAT ID",
85 | "set_only_once": 1
86 | },
87 | {
88 | "default": "Planned",
89 | "fieldname": "status",
90 | "fieldtype": "Select",
91 | "label": "Status",
92 | "options": "Planned\nRunning\nCompleted\nService Unavailable\nInvalid Input\nError",
93 | "read_only": 1
94 | },
95 | {
96 | "fetch_from": "customer.customer_name",
97 | "fetch_if_empty": 1,
98 | "fieldname": "trader_name",
99 | "fieldtype": "Data",
100 | "in_list_view": 1,
101 | "label": "Trader Name",
102 | "set_only_once": 1
103 | },
104 | {
105 | "fetch_from": "customer_address.address_line1",
106 | "fetch_if_empty": 1,
107 | "fieldname": "trader_street",
108 | "fieldtype": "Data",
109 | "label": "Trader Street",
110 | "set_only_once": 1
111 | },
112 | {
113 | "fetch_from": "customer_address.pincode",
114 | "fetch_if_empty": 1,
115 | "fieldname": "trader_postcode",
116 | "fieldtype": "Data",
117 | "label": "Trader Postcode",
118 | "set_only_once": 1
119 | },
120 | {
121 | "fetch_from": "customer_address.city",
122 | "fetch_if_empty": 1,
123 | "fieldname": "trader_city",
124 | "fieldtype": "Data",
125 | "label": "Trader City",
126 | "set_only_once": 1
127 | },
128 | {
129 | "default": "0",
130 | "fieldname": "trader_name_match",
131 | "fieldtype": "Check",
132 | "label": "Trader Name Match",
133 | "no_copy": 1,
134 | "read_only": 1
135 | },
136 | {
137 | "default": "0",
138 | "fieldname": "trader_street_match",
139 | "fieldtype": "Check",
140 | "label": "Trader Street Match",
141 | "no_copy": 1,
142 | "read_only": 1
143 | },
144 | {
145 | "default": "0",
146 | "fieldname": "trader_postcode_match",
147 | "fieldtype": "Check",
148 | "label": "Trader Postcode Match",
149 | "no_copy": 1,
150 | "read_only": 1
151 | },
152 | {
153 | "default": "0",
154 | "fieldname": "trader_city_match",
155 | "fieldtype": "Check",
156 | "label": "Trader City Match",
157 | "no_copy": 1,
158 | "read_only": 1
159 | },
160 | {
161 | "fieldname": "actual_trader_name",
162 | "fieldtype": "Data",
163 | "label": "Actual Trader Name",
164 | "no_copy": 1,
165 | "read_only": 1
166 | },
167 | {
168 | "fieldname": "actual_trader_address",
169 | "fieldtype": "Small Text",
170 | "label": "Actual Trader Address",
171 | "no_copy": 1,
172 | "read_only": 1
173 | },
174 | {
175 | "fieldname": "section_break_6ctcl",
176 | "fieldtype": "Section Break"
177 | },
178 | {
179 | "fieldname": "column_break_cxvts",
180 | "fieldtype": "Column Break"
181 | },
182 | {
183 | "fieldname": "column_break_cjvyk",
184 | "fieldtype": "Column Break"
185 | },
186 | {
187 | "fieldname": "party_type",
188 | "fieldtype": "Link",
189 | "label": "Party Type",
190 | "options": "DocType"
191 | },
192 | {
193 | "fieldname": "party",
194 | "fieldtype": "Dynamic Link",
195 | "in_standard_filter": 1,
196 | "label": "Party",
197 | "options": "party_type",
198 | "read_only_depends_on": "eval: !doc.party_type",
199 | "set_only_once": 1
200 | },
201 | {
202 | "fieldname": "party_address",
203 | "fieldtype": "Link",
204 | "label": "Party Address",
205 | "options": "Address",
206 | "read_only_depends_on": "eval: !doc.party",
207 | "set_only_once": 1
208 | },
209 | {
210 | "fetch_from": "customer.tax_id",
211 | "fetch_if_empty": 1,
212 | "fieldname": "party_vat_id",
213 | "fieldtype": "Data",
214 | "in_list_view": 1,
215 | "in_standard_filter": 1,
216 | "label": "Party VAT ID",
217 | "reqd": 1,
218 | "set_only_once": 1
219 | }
220 | ],
221 | "links": [],
222 | "modified": "2024-06-11 10:14:55.846253",
223 | "modified_by": "Administrator",
224 | "module": "ERPNext Germany",
225 | "name": "VAT ID Check",
226 | "owner": "Administrator",
227 | "permissions": [
228 | {
229 | "create": 1,
230 | "delete": 1,
231 | "email": 1,
232 | "export": 1,
233 | "print": 1,
234 | "read": 1,
235 | "report": 1,
236 | "role": "System Manager",
237 | "share": 1,
238 | "write": 1
239 | },
240 | {
241 | "create": 1,
242 | "delete": 1,
243 | "email": 1,
244 | "export": 1,
245 | "print": 1,
246 | "read": 1,
247 | "report": 1,
248 | "role": "Sales Manager",
249 | "share": 1,
250 | "write": 1
251 | },
252 | {
253 | "create": 1,
254 | "delete": 1,
255 | "email": 1,
256 | "export": 1,
257 | "print": 1,
258 | "read": 1,
259 | "report": 1,
260 | "role": "Sales Master Manager",
261 | "share": 1,
262 | "write": 1
263 | },
264 | {
265 | "create": 1,
266 | "email": 1,
267 | "export": 1,
268 | "print": 1,
269 | "read": 1,
270 | "report": 1,
271 | "role": "Sales User",
272 | "write": 1
273 | }
274 | ],
275 | "sort_field": "creation",
276 | "sort_order": "DESC",
277 | "states": [],
278 | "title_field": "party_vat_id"
279 | }
280 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, ALYF GmbH and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe.model.document import Document
6 | from tenacity import RetryError
7 |
8 | from erpnext_germany.utils.eu_vat import check_vat_approx, parse_vat_id
9 |
10 |
11 | class VATIDCheck(Document):
12 | def before_insert(self):
13 | if self.requester_vat_id:
14 | requester_country_code, requester_vat_number = parse_vat_id(self.requester_vat_id)
15 | self.requester_vat_id = f"{requester_country_code}{requester_vat_number}"
16 |
17 | country_code, vat_number = parse_vat_id(self.party_vat_id)
18 | self.party_vat_id = f"{country_code}{vat_number}"
19 |
20 | def after_insert(self):
21 | frappe.enqueue(
22 | run_check,
23 | doc=self,
24 | queue="long",
25 | now=frappe.conf.developer_mode or frappe.flags.in_test,
26 | )
27 |
28 |
29 | def run_check(doc: VATIDCheck):
30 | doc.db_set("status", "Running", notify=True)
31 | requester_country_code, requester_vat_number = None, None
32 | if doc.requester_vat_id:
33 | try:
34 | requester_country_code, requester_vat_number = parse_vat_id(doc.requester_vat_id)
35 | except ValueError:
36 | doc.db_set({"status": "Invalid Input", "is_valid": False}, notify=True)
37 | return
38 |
39 | try:
40 | country_code, vat_number = parse_vat_id(doc.party_vat_id)
41 | except ValueError:
42 | doc.db_set({"status": "Invalid Input", "is_valid": False}, notify=True)
43 | return
44 |
45 | try:
46 | result = check_vat_approx(
47 | country_code=country_code,
48 | vat_number=vat_number,
49 | trader_name=doc.trader_name,
50 | trader_street=doc.trader_street,
51 | trader_postcode=doc.trader_postcode,
52 | trader_city=doc.trader_city,
53 | requester_country_code=requester_country_code,
54 | requester_vat_number=requester_vat_number,
55 | )
56 | except (RetryError, ConnectionError):
57 | doc.db_set("status", "Service Unavailable", notify=True)
58 | return
59 | except Exception as e:
60 | if hasattr(e, "message") and e.message.upper() == "INVALID_INPUT":
61 | doc.db_set({"status": "Invalid Input", "is_valid": False}, notify=True)
62 | else:
63 | doc.db_set({"status": "Error"}, notify=True)
64 | frappe.log_error("VAT ID Check Error", frappe.get_traceback())
65 |
66 | return
67 |
68 | doc.db_set(
69 | {
70 | "status": "Completed",
71 | "is_valid": result.valid,
72 | "trader_name_match": bool(result.traderNameMatch),
73 | "trader_street_match": bool(result.traderStreetMatch),
74 | "trader_postcode_match": bool(result.traderPostcodeMatch),
75 | "trader_city_match": bool(result.traderCityMatch),
76 | "request_id": result.requestIdentifier,
77 | "actual_trader_name": result.traderName,
78 | "actual_trader_address": result.traderAddress,
79 | },
80 | notify=True,
81 | )
82 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check_list.js:
--------------------------------------------------------------------------------
1 | frappe.listview_settings["VAT ID Check"] = {
2 | add_fields: ["is_valid"],
3 | hide_name_column: true,
4 | get_indicator: function (doc) {
5 | if (doc.status === "Completed") {
6 | return doc.is_valid === 1
7 | ? [__("Valid"), "green", "is_valid,=,Yes"]
8 | : [__("Invalid"), "red", "is_valid,=,No"];
9 | } else if (doc.status === "Planned") {
10 | return [__("Planned"), "blue", "status,=,Planned"];
11 | } else if (doc.status === "Running") {
12 | return [__("Running"), "yellow", "status,=,Running"];
13 | } else if (["Service Unavailable", "Invalid Input"].includes(doc.status)) {
14 | return [__(doc.status), "gray", `status,=,${doc.status}`];
15 | }
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/report/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/report/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, ALYF GmbH and contributors
2 | // For license information, please see license.txt
3 | /* eslint-disable */
4 |
5 | frappe.query_reports["Summen- und Saldenliste"] = {
6 | filters: [
7 | {
8 | fieldname: "company",
9 | label: __("Company"),
10 | fieldtype: "Link",
11 | options: "Company",
12 | default: frappe.defaults.get_user_default("Company"),
13 | reqd: 1,
14 | },
15 | {
16 | fieldname: "fiscal_year",
17 | label: __("Fiscal Year"),
18 | fieldtype: "Link",
19 | options: "Fiscal Year",
20 | default: erpnext.utils.get_fiscal_year(),
21 | reqd: 1,
22 | },
23 | {
24 | fieldname: "month",
25 | label: __("Month"),
26 | fieldtype: "Select",
27 | default: get_previous_month(),
28 | options: [
29 | { value: 1, label: __("January") },
30 | { value: 2, label: __("February") },
31 | { value: 3, label: __("March") },
32 | { value: 4, label: __("April") },
33 | { value: 5, label: __("May") },
34 | { value: 6, label: __("June") },
35 | { value: 7, label: __("July") },
36 | { value: 8, label: __("August") },
37 | { value: 9, label: __("September") },
38 | { value: 10, label: __("October") },
39 | { value: 11, label: __("November") },
40 | { value: 12, label: __("December") },
41 | ],
42 | reqd: 1,
43 | },
44 | ],
45 | };
46 |
47 | function get_previous_month() {
48 | /* Return the 1-indexed number of the previous month */
49 | const date = new Date();
50 | date.setDate(0); // set to last day of previous month
51 | return date.getMonth() + 1; // getMonth() is 0-indexed
52 | }
53 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.json:
--------------------------------------------------------------------------------
1 | {
2 | "add_total_row": 0,
3 | "columns": [],
4 | "creation": "2023-12-28 20:21:45.244186",
5 | "disable_prepared_report": 1,
6 | "disabled": 0,
7 | "docstatus": 0,
8 | "doctype": "Report",
9 | "filters": [],
10 | "idx": 0,
11 | "is_standard": "Yes",
12 | "letter_head": "ALYF GmbH",
13 | "modified": "2023-12-28 20:21:50.528134",
14 | "modified_by": "Administrator",
15 | "module": "ERPNext Germany",
16 | "name": "Summen- und Saldenliste",
17 | "owner": "Administrator",
18 | "prepared_report": 0,
19 | "ref_doctype": "GL Entry",
20 | "report_name": "Summen- und Saldenliste",
21 | "report_type": "Script Report",
22 | "roles": [
23 | {
24 | "role": "Accounts User"
25 | },
26 | {
27 | "role": "Accounts Manager"
28 | },
29 | {
30 | "role": "Auditor"
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, ALYF GmbH and contributors
2 | # For license information, please see license.txt
3 |
4 | from calendar import monthrange
5 | from datetime import date
6 |
7 | import frappe
8 | from babel.dates import format_date
9 | from frappe import _
10 | from frappe.query_builder.functions import Cast, Coalesce, Sum
11 | from pypika.terms import Case
12 |
13 |
14 | def execute(filters=None):
15 | fy_start, month_start, month_end = get_dates(int(filters.month), filters.fiscal_year)
16 | current_month_name = format_date(month_start, format="MMMM", locale=frappe.local.lang)
17 | return get_columns(current_month_name), get_data(filters.company, fy_start, month_start, month_end)
18 |
19 |
20 | def get_columns(current_month_name: str):
21 | return [
22 | {
23 | "fieldname": "account",
24 | "label": _("Account"),
25 | "fieldtype": "Link",
26 | "options": "Account",
27 | "width": 400,
28 | },
29 | {
30 | "fieldname": "account_currency",
31 | "label": _("Currency"),
32 | "fieldtype": "Link",
33 | "options": "Currency",
34 | "width": 100,
35 | },
36 | {
37 | "fieldname": "debit_opening_balance",
38 | "label": _("Debit Opening Balance"),
39 | "fieldtype": "Currency",
40 | "width": 170,
41 | "options": "account_currency",
42 | },
43 | {
44 | "fieldname": "credit_opening_balance",
45 | "label": _("Credit Opening Balance"),
46 | "fieldtype": "Currency",
47 | "width": 170,
48 | "options": "account_currency",
49 | },
50 | {
51 | "fieldname": "debit_until_evaluation_period",
52 | "label": _("Debit until {0}").format(current_month_name),
53 | "fieldtype": "Currency",
54 | "width": 170,
55 | "options": "account_currency",
56 | },
57 | {
58 | "fieldname": "credit_until_evaluation_period",
59 | "label": _("Credit until {0}").format(current_month_name),
60 | "fieldtype": "Currency",
61 | "width": 170,
62 | "options": "account_currency",
63 | },
64 | {
65 | "fieldname": "debit_in_evaluation_period",
66 | "label": _("Debit in {0}").format(current_month_name),
67 | "fieldtype": "Currency",
68 | "width": 170,
69 | "options": "account_currency",
70 | },
71 | {
72 | "fieldname": "credit_in_evaluation_period",
73 | "label": _("Credit in {0}").format(current_month_name),
74 | "fieldtype": "Currency",
75 | "width": 170,
76 | "options": "account_currency",
77 | },
78 | {
79 | "fieldname": "debit_closing_balance",
80 | "label": _("Debit Closing Balance"),
81 | "fieldtype": "Currency",
82 | "width": 170,
83 | "options": "account_currency",
84 | },
85 | {
86 | "fieldname": "credit_closing_balance",
87 | "label": _("Credit Closing Balance"),
88 | "fieldtype": "Currency",
89 | "width": 170,
90 | "options": "account_currency",
91 | },
92 | ]
93 |
94 |
95 | def get_data(company: str, fy_start, month_start, month_end):
96 | gl_entry = frappe.qb.DocType("GL Entry")
97 | account = frappe.qb.DocType("Account")
98 |
99 | opening_balance = (
100 | frappe.qb.from_(gl_entry)
101 | .left_join(account)
102 | .on(gl_entry.account == account.name)
103 | .select(
104 | gl_entry.account,
105 | Case()
106 | .when(
107 | account.root_type == "Asset",
108 | Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency),
109 | )
110 | .else_(None)
111 | .as_("debit"),
112 | Case()
113 | .when(
114 | account.root_type.isin(("Liability", "Equity")),
115 | Sum(gl_entry.credit_in_account_currency) - Sum(gl_entry.debit_in_account_currency),
116 | )
117 | .else_(None)
118 | .as_("credit"),
119 | )
120 | .where(
121 | (gl_entry.company == company) & (gl_entry.is_cancelled == 0) & (gl_entry.posting_date < fy_start)
122 | )
123 | .groupby(gl_entry.account)
124 | )
125 |
126 | sum_until_month = (
127 | frappe.qb.from_(gl_entry)
128 | .select(
129 | gl_entry.account,
130 | Sum(gl_entry.debit_in_account_currency).as_("debit"),
131 | Sum(gl_entry.credit_in_account_currency).as_("credit"),
132 | )
133 | .where(
134 | (gl_entry.company == company)
135 | & (gl_entry.is_cancelled == 0)
136 | & (gl_entry.posting_date >= fy_start)
137 | & (gl_entry.posting_date < month_start)
138 | & (gl_entry.voucher_type != "Period Closing Voucher")
139 | )
140 | .groupby(gl_entry.account)
141 | )
142 |
143 | sum_in_month = (
144 | frappe.qb.from_(gl_entry)
145 | .left_join(account)
146 | .on(gl_entry.account == account.name)
147 | .select(
148 | gl_entry.account,
149 | gl_entry.account_currency,
150 | Sum(gl_entry.debit_in_account_currency).as_("debit"),
151 | Sum(gl_entry.credit_in_account_currency).as_("credit"),
152 | )
153 | .where(
154 | (gl_entry.company == company)
155 | & (gl_entry.is_cancelled == 0)
156 | & (gl_entry.posting_date >= month_start)
157 | & (gl_entry.posting_date <= month_end)
158 | & (gl_entry.voucher_type != "Period Closing Voucher")
159 | )
160 | .orderby(Cast(account.account_number, "int"))
161 | .groupby(gl_entry.account, gl_entry.account_currency)
162 | )
163 |
164 | query = (
165 | frappe.qb.from_(sum_in_month)
166 | .left_join(sum_until_month)
167 | .on(sum_until_month.account == sum_in_month.account)
168 | .left_join(opening_balance)
169 | .on(opening_balance.account == sum_in_month.account)
170 | .left_join(account)
171 | .on(sum_in_month.account == account.name)
172 | .select(
173 | sum_in_month.account,
174 | sum_in_month.account_currency,
175 | opening_balance.debit,
176 | opening_balance.credit,
177 | sum_until_month.debit,
178 | sum_until_month.credit,
179 | sum_in_month.debit,
180 | sum_in_month.credit,
181 | Case()
182 | .when(
183 | account.root_type.isin(("Asset", "Expense")),
184 | (
185 | Coalesce(opening_balance.debit, 0)
186 | + Coalesce(sum_until_month.debit, 0)
187 | + Coalesce(sum_in_month.debit, 0)
188 | )
189 | - (
190 | Coalesce(opening_balance.credit, 0)
191 | + Coalesce(sum_until_month.credit, 0)
192 | + Coalesce(sum_in_month.credit, 0)
193 | ),
194 | )
195 | .else_(None),
196 | Case()
197 | .when(
198 | account.root_type.isin(("Liability", "Equity", "Income")),
199 | (
200 | Coalesce(opening_balance.credit, 0)
201 | + Coalesce(sum_until_month.credit, 0)
202 | + Coalesce(sum_in_month.credit, 0)
203 | )
204 | - (
205 | Coalesce(opening_balance.debit, 0)
206 | + Coalesce(sum_until_month.debit, 0)
207 | + Coalesce(sum_in_month.debit, 0)
208 | ),
209 | )
210 | .else_(None),
211 | )
212 | )
213 |
214 | return query.run()
215 |
216 |
217 | def get_dates(month: int, fiscal_year: str):
218 | """Returns the start and end date for the given month."""
219 | fy_start: date = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")
220 | month_start = fy_start.replace(month=month, day=1)
221 | last_day_of_month = monthrange(month_start.year, month_start.month)[1]
222 | month_end = month_start.replace(day=last_day_of_month)
223 | return fy_start, month_start, month_end
224 |
--------------------------------------------------------------------------------
/erpnext_germany/erpnext_germany/workspace/germany/germany.json:
--------------------------------------------------------------------------------
1 | {
2 | "charts": [],
3 | "content": "[{\"id\":\"foL5O8DnUP\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Business Letter\",\"col\":4}},{\"id\":\"oJkHa86NBg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Business Trip\",\"col\":4}},{\"id\":\"VYs-C5iICV\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Vat Id Check\",\"col\":3}},{\"id\":\"kJjc0XvpWD\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"8IxzobqofO\",\"type\":\"card\",\"data\":{\"card_name\":\"Setup\",\"col\":4}},{\"id\":\"G5Fnz7UZYk\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
4 | "creation": "2024-10-24 17:31:50.222160",
5 | "custom_blocks": [],
6 | "docstatus": 0,
7 | "doctype": "Workspace",
8 | "for_user": "",
9 | "hide_custom": 0,
10 | "idx": 0,
11 | "indicator_color": "green",
12 | "is_hidden": 0,
13 | "label": "Germany",
14 | "links": [
15 | {
16 | "hidden": 0,
17 | "is_query_report": 0,
18 | "label": "Reports",
19 | "link_count": 1,
20 | "link_type": "DocType",
21 | "onboard": 0,
22 | "type": "Card Break"
23 | },
24 | {
25 | "hidden": 0,
26 | "is_query_report": 1,
27 | "label": "Summen- und Saldenliste",
28 | "link_count": 0,
29 | "link_to": "Summen- und Saldenliste",
30 | "link_type": "Report",
31 | "onboard": 0,
32 | "type": "Link"
33 | },
34 | {
35 | "hidden": 0,
36 | "is_query_report": 0,
37 | "label": "Setup",
38 | "link_count": 4,
39 | "link_type": "DocType",
40 | "onboard": 0,
41 | "type": "Card Break"
42 | },
43 | {
44 | "hidden": 0,
45 | "is_query_report": 0,
46 | "label": "Religious Denomination",
47 | "link_count": 0,
48 | "link_to": "Religious Denomination",
49 | "link_type": "DocType",
50 | "onboard": 0,
51 | "type": "Link"
52 | },
53 | {
54 | "hidden": 0,
55 | "is_query_report": 0,
56 | "label": "Business Trip Region",
57 | "link_count": 0,
58 | "link_to": "Business Trip Region",
59 | "link_type": "DocType",
60 | "onboard": 0,
61 | "type": "Link"
62 | },
63 | {
64 | "hidden": 0,
65 | "is_query_report": 0,
66 | "label": "Business Trip Settings",
67 | "link_count": 0,
68 | "link_to": "Business Trip Settings",
69 | "link_type": "DocType",
70 | "onboard": 0,
71 | "type": "Link"
72 | },
73 | {
74 | "hidden": 0,
75 | "is_query_report": 0,
76 | "label": "ERPNext Germany Settings",
77 | "link_count": 0,
78 | "link_to": "ERPNext Germany Settings",
79 | "link_type": "DocType",
80 | "onboard": 0,
81 | "type": "Link"
82 | }
83 | ],
84 | "modified": "2025-02-26 12:31:44.539466",
85 | "modified_by": "Administrator",
86 | "module": "ERPNext Germany",
87 | "name": "Germany",
88 | "number_cards": [],
89 | "owner": "Administrator",
90 | "parent_page": "",
91 | "public": 1,
92 | "quick_lists": [],
93 | "roles": [],
94 | "sequence_id": 53.0,
95 | "shortcuts": [
96 | {
97 | "color": "Grey",
98 | "doc_view": "List",
99 | "label": "Business Letter",
100 | "link_to": "Business Letter",
101 | "stats_filter": "[]",
102 | "type": "DocType"
103 | },
104 | {
105 | "color": "Red",
106 | "doc_view": "List",
107 | "format": "{} Invalid",
108 | "label": "Vat Id Check",
109 | "link_to": "VAT ID Check",
110 | "stats_filter": "[[\"VAT ID Check\",\"status\",\"=\",\"Completed\",false],[\"VAT ID Check\",\"is_valid\",\"=\",0,false]]",
111 | "type": "DocType"
112 | },
113 | {
114 | "color": "Yellow",
115 | "doc_view": "List",
116 | "format": "{} Open",
117 | "label": "Business Trip",
118 | "link_to": "Business Trip",
119 | "stats_filter": "[[\"Business Trip\",\"status\",\"=\",\"Submitted\",false]]",
120 | "type": "DocType"
121 | }
122 | ],
123 | "title": "Germany"
124 | }
--------------------------------------------------------------------------------
/erpnext_germany/hooks.py:
--------------------------------------------------------------------------------
1 | app_name = "erpnext_germany"
2 | app_title = "ERPNext Germany"
3 | app_publisher = "ALYF GmbH"
4 | app_description = "App to hold regional code for Germany, built on top of ERPNext."
5 | required_apps = ["erpnext"]
6 | app_icon = "octicon octicon-file-directory"
7 | app_color = "grey"
8 | app_email = "hallo@alyf.de"
9 | app_license = "GPLv3"
10 |
11 | # Includes in
12 | # ------------------
13 |
14 | # include js, css files in header of desk.html
15 | # app_include_css = "/assets/erpnext_germany/css/erpnext_germany.css"
16 | # app_include_js = "/assets/erpnext_germany/js/erpnext_germany.js"
17 | app_include_js = [
18 | "erpnext_germany.bundle.js",
19 | ]
20 |
21 | # include js, css files in header of web template
22 | # web_include_css = "/assets/erpnext_germany/css/erpnext_germany.css"
23 | # web_include_js = "/assets/erpnext_germany/js/erpnext_germany.js"
24 |
25 | # include custom scss in every website theme (without file extension ".scss")
26 | # website_theme_scss = "erpnext_germany/public/scss/website"
27 |
28 | # include js, css files in header of web form
29 | # webform_include_js = {"doctype": "public/js/doctype.js"}
30 | # webform_include_css = {"doctype": "public/css/doctype.css"}
31 |
32 | # include js in page
33 | # page_js = {"page" : "public/js/file.js"}
34 |
35 | # include js in doctype views
36 | # doctype_js = {}
37 | # doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
38 | # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
39 | # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"}
40 |
41 | # Home Pages
42 | # ----------
43 |
44 | # application home page (will override Website Settings)
45 | # home_page = "login"
46 |
47 | # website user home page (by Role)
48 | # role_home_page = {
49 | # "Role": "home_page"
50 | # }
51 |
52 | # Generators
53 | # ----------
54 |
55 | # automatically create page for each record of this doctype
56 | # website_generators = ["Web Page"]
57 |
58 | # Jinja
59 | # ----------
60 |
61 | # add methods and filters to jinja environment
62 | # jinja = {
63 | # "methods": "erpnext_germany.utils.jinja_methods",
64 | # "filters": "erpnext_germany.utils.jinja_filters"
65 | # }
66 |
67 | # Installation
68 | # ------------
69 |
70 | # before_install = "erpnext_germany.install.before_install"
71 | after_install = "erpnext_germany.install.after_install"
72 |
73 | # Uninstallation
74 | # ------------
75 |
76 | before_uninstall = "erpnext_germany.uninstall.before_uninstall"
77 | # after_uninstall = "erpnext_germany.uninstall.after_uninstall"
78 |
79 | # Desk Notifications
80 | # ------------------
81 | # See frappe.core.notifications.get_notification_config
82 |
83 | # notification_config = "erpnext_germany.notifications.get_notification_config"
84 |
85 | # Permissions
86 | # -----------
87 | # Permissions evaluated in scripted ways
88 |
89 | # permission_query_conditions = {
90 | # "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions",
91 | # }
92 | #
93 | # has_permission = {
94 | # "Event": "frappe.desk.doctype.event.event.has_permission",
95 | # }
96 |
97 | # DocType Class
98 | # ---------------
99 | # Override standard doctype classes
100 |
101 | # override_doctype_class = {
102 | # "ToDo": "custom_app.overrides.CustomToDo"
103 | # }
104 |
105 | # Document Events
106 | # ---------------
107 | # Hook on document methods and events
108 |
109 | doc_events = {
110 | "Quotation": {
111 | "on_trash": "erpnext_germany.custom.sales.on_trash",
112 | },
113 | "Sales Order": {
114 | "on_trash": "erpnext_germany.custom.sales.on_trash",
115 | },
116 | "Sales Invoice": {
117 | "on_trash": "erpnext_germany.custom.sales.on_trash",
118 | },
119 | }
120 |
121 | # doc_events = {}
122 |
123 | # Scheduled Tasks
124 | # ---------------
125 |
126 | scheduler_events = {
127 | "all": ["erpnext_germany.tasks.all"],
128 | # "daily": [
129 | # "erpnext_germany.tasks.daily"
130 | # ],
131 | # "hourly": [
132 | # "erpnext_germany.tasks.hourly"
133 | # ],
134 | # "weekly": [
135 | # "erpnext_germany.tasks.weekly"
136 | # ],
137 | # "monthly": [
138 | # "erpnext_germany.tasks.monthly"
139 | # ],
140 | }
141 |
142 | # Testing
143 | # -------
144 |
145 | # before_tests = "erpnext_germany.install.before_tests"
146 |
147 | # Overriding Methods
148 | # ------------------------------
149 | #
150 | # override_whitelisted_methods = {
151 | # "frappe.desk.doctype.event.event.get_events": "erpnext_germany.event.get_events"
152 | # }
153 | #
154 | # each overriding function accepts a `data` argument;
155 | # generated from the base implementation of the doctype dashboard,
156 | # along with any modifications made in other Frappe apps
157 | # override_doctype_dashboards = {
158 | # "Task": "erpnext_germany.task.get_dashboard_data"
159 | # }
160 |
161 | # exempt linked doctypes from being automatically cancelled
162 | #
163 | # auto_cancel_exempted_doctypes = ["Auto Repeat"]
164 |
165 |
166 | # User Data Protection
167 | # --------------------
168 |
169 | # user_data_fields = [
170 | # {
171 | # "doctype": "{doctype_1}",
172 | # "filter_by": "{filter_by}",
173 | # "redact_fields": ["{field_1}", "{field_2}"],
174 | # "partial": 1,
175 | # },
176 | # {
177 | # "doctype": "{doctype_2}",
178 | # "filter_by": "{filter_by}",
179 | # "partial": 1,
180 | # },
181 | # {
182 | # "doctype": "{doctype_3}",
183 | # "strict": False,
184 | # },
185 | # {
186 | # "doctype": "{doctype_4}"
187 | # }
188 | # ]
189 |
190 | # Authentication and authorization
191 | # --------------------------------
192 |
193 | # auth_hooks = [
194 | # "erpnext_germany.auth.validate"
195 | # ]
196 |
197 | germany_custom_records = [
198 | {
199 | "doctype": "DocType Link",
200 | "parent": "Customer",
201 | "parentfield": "links",
202 | "parenttype": "Customize Form",
203 | "group": "Pre Sales",
204 | "link_doctype": "VAT ID Check",
205 | "link_fieldname": "party",
206 | "custom": 1,
207 | },
208 | {
209 | "doctype": "DocType Link",
210 | "parent": "Supplier",
211 | "parentfield": "links",
212 | "parenttype": "Customize Form",
213 | "group": "Vendor Evaluation",
214 | "link_doctype": "VAT ID Check",
215 | "link_fieldname": "party",
216 | "custom": 1,
217 | },
218 | ]
219 |
--------------------------------------------------------------------------------
/erpnext_germany/install.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | from csv import DictReader
3 |
4 | import frappe
5 | from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
6 | from frappe.custom.doctype.customize_form.customize_form import (
7 | docfield_properties,
8 | doctype_properties,
9 | )
10 | from frappe.custom.doctype.property_setter.property_setter import make_property_setter
11 | from frappe.exceptions import DuplicateEntryError
12 |
13 | from .custom_fields import get_custom_fields
14 | from .property_setters import get_property_setters
15 |
16 |
17 | def after_install():
18 | make_custom_fields()
19 | make_property_setters()
20 | import_data()
21 | insert_custom_records()
22 |
23 |
24 | def import_data():
25 | for doctype, filename in (
26 | ("Religious Denomination", "religious_denomination.csv"),
27 | ("Employee Health Insurance", "employee_health_insurance.csv"),
28 | ("Expense Claim Type", "expense_claim_type.csv"),
29 | ("Business Trip Region", "business_trip_region.csv"),
30 | ):
31 | if not frappe.db.exists("DocType", doctype):
32 | continue
33 |
34 | path = frappe.get_app_path("erpnext_germany", "data", filename)
35 | import_csv(doctype, path)
36 |
37 |
38 | def import_csv(doctype, path):
39 | with open(path) as csvfile:
40 | reader = DictReader(csvfile)
41 | for row in reader:
42 | if frappe.db.exists(doctype, row):
43 | # This doesn't catch all duplicates, because it expects all
44 | # fields to match, not (only) the primary key.
45 | continue
46 |
47 | doc = frappe.new_doc(doctype)
48 | doc.update(row)
49 | with contextlib.suppress(DuplicateEntryError):
50 | doc.insert()
51 |
52 |
53 | def make_property_setters():
54 | for doctypes, property_setters in get_property_setters().items():
55 | if isinstance(doctypes, str):
56 | doctypes = (doctypes,)
57 |
58 | for doctype in doctypes:
59 | for property_setter in property_setters:
60 | if property_setter[0]:
61 | for_doctype = False
62 | property_type = docfield_properties[property_setter[1]]
63 | else:
64 | for_doctype = True
65 | property_type = doctype_properties[property_setter[1]]
66 |
67 | make_property_setter(
68 | doctype=doctype,
69 | fieldname=property_setter[0],
70 | property=property_setter[1],
71 | value=property_setter[2],
72 | property_type=property_type,
73 | for_doctype=for_doctype,
74 | )
75 |
76 |
77 | def make_custom_fields():
78 | create_custom_fields(get_custom_fields())
79 |
80 |
81 | def insert_custom_records():
82 | for custom_record in frappe.get_hooks("germany_custom_records"):
83 | filters = custom_record.copy()
84 | # Clean up filters. They need to be a plain dict without nested dicts or lists.
85 | for key, value in custom_record.items():
86 | if isinstance(value, list | dict):
87 | del filters[key]
88 |
89 | if not frappe.db.exists(filters):
90 | frappe.get_doc(custom_record).insert(ignore_if_duplicate=True)
91 |
--------------------------------------------------------------------------------
/erpnext_germany/locale/de.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/locale/de.mo
--------------------------------------------------------------------------------
/erpnext_germany/locale/main.pot:
--------------------------------------------------------------------------------
1 | # Translations template for ERPNext Germany.
2 | # Copyright (C) 2025 ALYF GmbH
3 | # This file is distributed under the same license as the ERPNext Germany project.
4 | # FIRST AUTHOR , 2025.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: ERPNext Germany VERSION\n"
9 | "Report-Msgid-Bugs-To: hallo@alyf.de\n"
10 | "POT-Creation-Date: 2025-02-20 17:53+0053\n"
11 | "PO-Revision-Date: 2025-02-20 17:53+0053\n"
12 | "Last-Translator: hallo@alyf.de\n"
13 | "Language-Team: hallo@alyf.de\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=utf-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Generated-By: Babel 2.13.1\n"
18 |
19 | #. Label of a Currency field in DocType 'Business Trip Region'
20 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
21 | msgid "Accommodation"
22 | msgstr ""
23 |
24 | #. Label of a Check field in DocType 'Business Trip Allowance'
25 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
26 | msgid "Accommodation was Provided"
27 | msgstr ""
28 |
29 | #. Label of a Table field in DocType 'Business Trip'
30 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
31 | msgid "Accommodations"
32 | msgstr ""
33 |
34 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:30
35 | msgid "Account"
36 | msgstr ""
37 |
38 | #. Name of a role
39 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
40 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
41 | msgid "Accounts User"
42 | msgstr ""
43 |
44 | #. Label of a Small Text field in DocType 'VAT ID Check'
45 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
46 | msgid "Actual Trader Address"
47 | msgstr ""
48 |
49 | #. Label of a Data field in DocType 'VAT ID Check'
50 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
51 | msgid "Actual Trader Name"
52 | msgstr ""
53 |
54 | #. Label of a Link field in DocType 'Business Letter'
55 | #. Label of a Small Text field in DocType 'Business Letter'
56 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
57 | msgid "Address"
58 | msgstr ""
59 |
60 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
61 | #. Journey'
62 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
63 | msgid "Airplane"
64 | msgstr ""
65 |
66 | #. Label of a Table field in DocType 'Business Trip'
67 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
68 | msgid "Allowances"
69 | msgstr ""
70 |
71 | #. Label of a Link field in DocType 'Business Letter'
72 | #. Label of a Link field in DocType 'Business Trip'
73 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
74 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
75 | msgid "Amended From"
76 | msgstr ""
77 |
78 | #. Label of a Currency field in DocType 'Business Trip Allowance'
79 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
80 | msgid "Amount"
81 | msgstr ""
82 |
83 | #. Option for the 'Status' (Select) field in DocType 'Business Trip'
84 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
85 | msgid "Approved"
86 | msgstr ""
87 |
88 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:32
89 | msgid "April"
90 | msgstr ""
91 |
92 | #. Label of a Currency field in DocType 'Business Trip Region'
93 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
94 | msgid "Arrival or Departure"
95 | msgstr ""
96 |
97 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:36
98 | msgid "August"
99 | msgstr ""
100 |
101 | #. Option for the 'Status' (Select) field in DocType 'Business Trip'
102 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
103 | msgid "Billed"
104 | msgstr ""
105 |
106 | #. Label of a Check field in DocType 'Business Trip Allowance'
107 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
108 | msgid "Breakfast was Provided"
109 | msgstr ""
110 |
111 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
112 | #. Journey'
113 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
114 | msgid "Bus"
115 | msgstr ""
116 |
117 | #. Name of a DocType
118 | #. Linked DocType in Business Letter Template's connections
119 | #. Label of a shortcut in the Germany Workspace
120 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
121 | #: erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.json
122 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
123 | msgid "Business Letter"
124 | msgstr ""
125 |
126 | #. Name of a DocType
127 | #: erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.json
128 | msgid "Business Letter Template"
129 | msgstr ""
130 |
131 | #. Name of a DocType
132 | #. Linked DocType in Business Trip Region's connections
133 | #. Label of a shortcut in the Germany Workspace
134 | #: erpnext_germany/custom_fields.py:183
135 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
136 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
137 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
138 | #: erpnext_germany/patches/add_business_trip_to_expense_claim.py:15
139 | msgid "Business Trip"
140 | msgstr ""
141 |
142 | #. Name of a DocType
143 | #: erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/business_trip_accommodation.json
144 | msgid "Business Trip Accommodation"
145 | msgstr ""
146 |
147 | #. Name of a DocType
148 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
149 | msgid "Business Trip Allowance"
150 | msgstr ""
151 |
152 | #. Name of a DocType
153 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
154 | msgid "Business Trip Journey"
155 | msgstr ""
156 |
157 | #. Name of a DocType
158 | #. Label of a Link in the Germany Workspace
159 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
160 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
161 | msgid "Business Trip Region"
162 | msgstr ""
163 |
164 | #. Name of a DocType
165 | #: erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.json
166 | msgid "Business Trip Settings"
167 | msgstr ""
168 |
169 | #: erpnext_germany/custom/sales.py:15
170 | msgid "Cannot delete this transaction"
171 | msgstr ""
172 |
173 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
174 | #. Journey'
175 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
176 | msgid "Car"
177 | msgstr ""
178 |
179 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
180 | #. Journey'
181 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
182 | msgid "Car (private)"
183 | msgstr ""
184 |
185 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
186 | #. Journey'
187 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
188 | msgid "Car (rental)"
189 | msgstr ""
190 |
191 | #: erpnext_germany/custom_fields.py:124
192 | msgid "Children Eligible for Tax Credits"
193 | msgstr ""
194 |
195 | #. Label of a Data field in DocType 'Business Trip Accommodation'
196 | #: erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/business_trip_accommodation.json
197 | msgid "City"
198 | msgstr ""
199 |
200 | #. Label of a Link field in DocType 'Business Letter'
201 | #. Label of a Link field in DocType 'Business Trip'
202 | #. Label of a Link field in DocType 'VAT ID Check'
203 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
204 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
205 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
206 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:9
207 | msgid "Company"
208 | msgstr ""
209 |
210 | #. Option for the 'Status' (Select) field in DocType 'VAT ID Check'
211 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
212 | msgid "Completed"
213 | msgstr ""
214 |
215 | #. Label of a Link field in DocType 'Business Letter'
216 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
217 | msgid "Contact"
218 | msgstr ""
219 |
220 | #. Label of a Text Editor field in DocType 'Business Letter'
221 | #. Label of a Text Editor field in DocType 'Business Letter Template'
222 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
223 | #: erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.json
224 | msgid "Content"
225 | msgstr ""
226 |
227 | #. Label of a Text Editor field in DocType 'Business Letter'
228 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
229 | msgid "Content (Preview)"
230 | msgstr ""
231 |
232 | #. Label of a Link field in DocType 'Business Trip'
233 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
234 | msgid "Cost Center"
235 | msgstr ""
236 |
237 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:93
238 | msgid "Credit Closing Balance"
239 | msgstr ""
240 |
241 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:51
242 | msgid "Credit Opening Balance"
243 | msgstr ""
244 |
245 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:79
246 | msgid "Credit in {0}"
247 | msgstr ""
248 |
249 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:65
250 | msgid "Credit until {0}"
251 | msgstr ""
252 |
253 | #. Label of a Link field in DocType 'Business Trip'
254 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
255 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:37
256 | msgid "Currency"
257 | msgstr ""
258 |
259 | #. Label of a Link field in DocType 'Business Trip'
260 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
261 | msgid "Customer"
262 | msgstr ""
263 |
264 | #. Label of a Date field in DocType 'Business Letter'
265 | #. Label of a Date field in DocType 'Business Trip Allowance'
266 | #. Label of a Date field in DocType 'Business Trip Journey'
267 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
268 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
269 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
270 | msgid "Date"
271 | msgstr ""
272 |
273 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:86
274 | msgid "Debit Closing Balance"
275 | msgstr ""
276 |
277 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:44
278 | msgid "Debit Opening Balance"
279 | msgstr ""
280 |
281 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:72
282 | msgid "Debit in {0}"
283 | msgstr ""
284 |
285 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.py:58
286 | msgid "Debit until {0}"
287 | msgstr ""
288 |
289 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:40
290 | msgid "December"
291 | msgstr ""
292 |
293 | #. Label of a Link field in DocType 'Business Letter'
294 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
295 | msgid "Department"
296 | msgstr ""
297 |
298 | #. Label of a Check field in DocType 'Business Trip Allowance'
299 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
300 | msgid "Dinner was Provided"
301 | msgstr ""
302 |
303 | #. Label of a Check field in DocType 'Business Trip Region'
304 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
305 | msgid "Disabled"
306 | msgstr ""
307 |
308 | #. Label of a Int field in DocType 'Business Trip Journey'
309 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
310 | msgid "Distance"
311 | msgstr ""
312 |
313 | #: erpnext_germany/config/desktop.py:10
314 | msgid "ERPNext Germany"
315 | msgstr ""
316 |
317 | #. Label of a Data field in DocType 'Business Letter'
318 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
319 | msgid "Email"
320 | msgstr ""
321 |
322 | #. Label of a Link field in DocType 'Business Letter'
323 | #. Name of a role
324 | #. Label of a Link field in DocType 'Business Trip'
325 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
326 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
327 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
328 | msgid "Employee"
329 | msgstr ""
330 |
331 | #. Label of a Data field in DocType 'Business Letter'
332 | #. Label of a Data field in DocType 'Business Trip'
333 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
334 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
335 | msgid "Employee Name"
336 | msgstr ""
337 |
338 | #. Option for the 'Status' (Select) field in DocType 'VAT ID Check'
339 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
340 | msgid "Error"
341 | msgstr ""
342 |
343 | #. Linked DocType in Business Trip's connections
344 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
345 | msgid "Expense Claim"
346 | msgstr ""
347 |
348 | #. Label of a Link field in DocType 'Business Trip Settings'
349 | #: erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.json
350 | msgid "Expense Claim Type for Additional Meal Expenses"
351 | msgstr ""
352 |
353 | #. Label of a Link field in DocType 'Business Trip Settings'
354 | #: erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.json
355 | msgid "Expense Claim Type for Mileage Allowance"
356 | msgstr ""
357 |
358 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:30
359 | msgid "February"
360 | msgstr ""
361 |
362 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:17
363 | msgid "Fiscal Year"
364 | msgstr ""
365 |
366 | #. Description of the 'Whole Day' (Currency) field in DocType 'Business Trip
367 | #. Region'
368 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
369 | msgid "For an absence of at least 24 hours per calendar day"
370 | msgstr ""
371 |
372 | #. Description of the 'Arrival or Departure' (Currency) field in DocType
373 | #. 'Business Trip Region'
374 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
375 | msgid "For the day of arrival and departure and for an absence of more than 8 hours per calendar day"
376 | msgstr ""
377 |
378 | #. Label of a Data field in DocType 'Business Trip Journey'
379 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
380 | msgid "From"
381 | msgstr ""
382 |
383 | #. Label of a Date field in DocType 'Business Trip'
384 | #. Label of a Date field in DocType 'Business Trip Accommodation'
385 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
386 | #: erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/business_trip_accommodation.json
387 | msgid "From Date"
388 | msgstr ""
389 |
390 | #. Label of a Time field in DocType 'Business Trip Allowance'
391 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
392 | msgid "From Time"
393 | msgstr ""
394 |
395 | #. Name of a Workspace
396 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
397 | msgid "Germany"
398 | msgstr ""
399 |
400 | #. Name of a role
401 | #: erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.json
402 | msgid "HR Manager"
403 | msgstr ""
404 |
405 | #. Name of a role
406 | #: erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.json
407 | msgid "HR User"
408 | msgstr ""
409 |
410 | #: erpnext_germany/custom_fields.py:138
411 | msgid "Has Children"
412 | msgstr ""
413 |
414 | #: erpnext_germany/custom_fields.py:144
415 | msgid "Has Other Employments"
416 | msgstr ""
417 |
418 | #: erpnext_germany/custom_fields.py:150
419 | msgid "Highest School Qualification"
420 | msgstr ""
421 |
422 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check_list.js:8
423 | msgid "Invalid"
424 | msgstr ""
425 |
426 | #. Option for the 'Status' (Select) field in DocType 'VAT ID Check'
427 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
428 | msgid "Invalid Input"
429 | msgstr ""
430 |
431 | #: erpnext_germany/custom_fields.py:70
432 | msgid "Is Severely Disabled"
433 | msgstr ""
434 |
435 | #. Label of a Check field in DocType 'VAT ID Check'
436 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
437 | msgid "Is Valid"
438 | msgstr ""
439 |
440 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:29
441 | msgid "January"
442 | msgstr ""
443 |
444 | #. Label of a Table field in DocType 'Business Trip'
445 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
446 | msgid "Journeys"
447 | msgstr ""
448 |
449 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:35
450 | msgid "July"
451 | msgstr ""
452 |
453 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:34
454 | msgid "June"
455 | msgstr ""
456 |
457 | #. Label of a Link field in DocType 'Business Letter'
458 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
459 | msgid "Link Document Type"
460 | msgstr ""
461 |
462 | #. Label of a Dynamic Link field in DocType 'Business Letter'
463 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
464 | msgid "Link Name"
465 | msgstr ""
466 |
467 | #. Label of a Data field in DocType 'Business Letter'
468 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
469 | msgid "Link Title"
470 | msgstr ""
471 |
472 | #. Label of a Link field in DocType 'Business Letter'
473 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
474 | msgid "Load From Template"
475 | msgstr ""
476 |
477 | #. Description of the 'Accommodation' (Currency) field in DocType 'Business
478 | #. Trip Region'
479 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
480 | msgid "Lump sum for accommodation expenses"
481 | msgstr ""
482 |
483 | #. Label of a Check field in DocType 'Business Trip Allowance'
484 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
485 | msgid "Lunch was Provided"
486 | msgstr ""
487 |
488 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:31
489 | msgid "March"
490 | msgstr ""
491 |
492 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:33
493 | msgid "May"
494 | msgstr ""
495 |
496 | #. Label of a Currency field in DocType 'Business Trip Settings'
497 | #: erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.json
498 | msgid "Mileage Allowance"
499 | msgstr ""
500 |
501 | #. Label of a Select field in DocType 'Business Trip Journey'
502 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
503 | msgid "Mode of Transport"
504 | msgstr ""
505 |
506 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:25
507 | msgid "Month"
508 | msgstr ""
509 |
510 | #: erpnext_germany/custom_fields.py:63
511 | msgid "Nationality"
512 | msgstr ""
513 |
514 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:39
515 | msgid "November"
516 | msgstr ""
517 |
518 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:38
519 | msgid "October"
520 | msgstr ""
521 |
522 | #: erpnext_germany/custom/sales.py:12
523 | msgid "Only the most recent {0} can be deleted in order to avoid gaps in numbering."
524 | msgstr ""
525 |
526 | #. Option for the 'Status' (Select) field in DocType 'Business Trip'
527 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
528 | msgid "Paid"
529 | msgstr ""
530 |
531 | #. Description of the 'Total Mileage Allowance' (Currency) field in DocType
532 | #. 'Business Trip'
533 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
534 | msgid "Paid only for Car (private)"
535 | msgstr ""
536 |
537 | #. Label of a Dynamic Link field in DocType 'VAT ID Check'
538 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
539 | msgid "Party"
540 | msgstr ""
541 |
542 | #. Label of a Link field in DocType 'VAT ID Check'
543 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
544 | msgid "Party Address"
545 | msgstr ""
546 |
547 | #. Label of a Link field in DocType 'VAT ID Check'
548 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
549 | msgid "Party Type"
550 | msgstr ""
551 |
552 | #. Label of a Data field in DocType 'VAT ID Check'
553 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
554 | msgid "Party VAT ID"
555 | msgstr ""
556 |
557 | #. Option for the 'Status' (Select) field in DocType 'VAT ID Check'
558 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
559 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check_list.js:10
560 | msgid "Planned"
561 | msgstr ""
562 |
563 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.js:49
564 | msgid "Please enter a start and end date of the trip!"
565 | msgstr ""
566 |
567 | #. Label of a Tab Break field in DocType 'Business Letter'
568 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
569 | msgid "Preview"
570 | msgstr ""
571 |
572 | #. Label of a Link field in DocType 'Business Trip'
573 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
574 | msgid "Project"
575 | msgstr ""
576 |
577 | #. Name of a role
578 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
579 | msgid "Projects Manager"
580 | msgstr ""
581 |
582 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
583 | #. Journey'
584 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
585 | msgid "Public Transport"
586 | msgstr ""
587 |
588 | #. Label of a Attach field in DocType 'Business Trip Accommodation'
589 | #. Label of a Attach field in DocType 'Business Trip Journey'
590 | #: erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/business_trip_accommodation.json
591 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
592 | msgid "Receipt"
593 | msgstr ""
594 |
595 | #. Label of a Column Break field in DocType 'Business Letter'
596 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
597 | msgid "Recipient"
598 | msgstr ""
599 |
600 | #. Label of a Section Break field in DocType 'Business Letter'
601 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
602 | msgid "Reference"
603 | msgstr ""
604 |
605 | #. Label of a Link field in DocType 'Business Trip'
606 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
607 | msgid "Region"
608 | msgstr ""
609 |
610 | #: erpnext_germany/custom_fields.py:46
611 | msgid "Register Court"
612 | msgstr ""
613 |
614 | #: erpnext_germany/custom_fields.py:14
615 | msgid "Register Information"
616 | msgstr ""
617 |
618 | #: erpnext_germany/custom_fields.py:34
619 | msgid "Register Number"
620 | msgstr ""
621 |
622 | #: erpnext_germany/custom_fields.py:21
623 | msgid "Register Type"
624 | msgstr ""
625 |
626 | #. Option for the 'Status' (Select) field in DocType 'Business Trip'
627 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
628 | msgid "Rejected"
629 | msgstr ""
630 |
631 | #. Name of a DocType
632 | #. Label of a Link in the Germany Workspace
633 | #: erpnext_germany/custom_fields.py:130
634 | #: erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.json
635 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
636 | msgid "Religious Denomination"
637 | msgstr ""
638 |
639 | #. Label of a Card Break in the Germany Workspace
640 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
641 | msgid "Reports"
642 | msgstr ""
643 |
644 | #. Label of a Data field in DocType 'VAT ID Check'
645 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
646 | msgid "Request ID"
647 | msgstr ""
648 |
649 | #. Label of a Section Break field in DocType 'VAT ID Check'
650 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
651 | msgid "Requester"
652 | msgstr ""
653 |
654 | #. Label of a Data field in DocType 'VAT ID Check'
655 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
656 | msgid "Requester VAT ID"
657 | msgstr ""
658 |
659 | #. Label of a Section Break field in DocType 'VAT ID Check'
660 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
661 | msgid "Result"
662 | msgstr ""
663 |
664 | #. Option for the 'Status' (Select) field in DocType 'VAT ID Check'
665 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
666 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check_list.js:12
667 | msgid "Running"
668 | msgstr ""
669 |
670 | #. Name of a role
671 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
672 | msgid "Sales Manager"
673 | msgstr ""
674 |
675 | #. Name of a role
676 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
677 | msgid "Sales Master Manager"
678 | msgstr ""
679 |
680 | #. Name of a role
681 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
682 | msgid "Sales User"
683 | msgstr ""
684 |
685 | #. Label of a Column Break field in DocType 'Business Letter'
686 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
687 | msgid "Sender"
688 | msgstr ""
689 |
690 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.js:37
691 | msgid "September"
692 | msgstr ""
693 |
694 | #. Option for the 'Status' (Select) field in DocType 'VAT ID Check'
695 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
696 | msgid "Service Unavailable"
697 | msgstr ""
698 |
699 | #. Label of a Card Break in the Germany Workspace
700 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
701 | msgid "Setup"
702 | msgstr ""
703 |
704 | #. Label of a Select field in DocType 'Business Trip'
705 | #. Label of a Select field in DocType 'VAT ID Check'
706 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
707 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
708 | msgid "Status"
709 | msgstr ""
710 |
711 | #. Label of a Data field in DocType 'Business Letter'
712 | #. Label of a Data field in DocType 'Business Letter Template'
713 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
714 | #: erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.json
715 | msgid "Subject"
716 | msgstr ""
717 |
718 | #. Label of a Data field in DocType 'Business Letter'
719 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
720 | msgid "Subject (Preview)"
721 | msgstr ""
722 |
723 | #. Option for the 'Status' (Select) field in DocType 'Business Trip'
724 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
725 | msgid "Submitted"
726 | msgstr ""
727 |
728 | #. Name of a report
729 | #. Label of a Link in the Germany Workspace
730 | #: erpnext_germany/erpnext_germany/report/summen__und_saldenliste/summen__und_saldenliste.json
731 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
732 | msgid "Summen- und Saldenliste"
733 | msgstr ""
734 |
735 | #. Name of a role
736 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.json
737 | #: erpnext_germany/erpnext_germany/doctype/business_letter_template/business_letter_template.json
738 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
739 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
740 | #: erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.json
741 | #: erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.json
742 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
743 | msgid "System Manager"
744 | msgstr ""
745 |
746 | #: erpnext_germany/custom_fields.py:116
747 | msgid "Tax Bracket"
748 | msgstr ""
749 |
750 | #: erpnext_germany/custom_fields.py:158 erpnext_germany/custom_fields.py:169
751 | msgid "Tax Exemption Reason"
752 | msgstr ""
753 |
754 | #: erpnext_germany/custom_fields.py:90
755 | msgid "Tax ID"
756 | msgstr ""
757 |
758 | #: erpnext_germany/custom_fields.py:97
759 | msgid "Tax Office"
760 | msgstr ""
761 |
762 | #: erpnext_germany/custom_fields.py:104
763 | msgid "Tax Office Number"
764 | msgstr ""
765 |
766 | #: erpnext_germany/custom_fields.py:83
767 | msgid "Taxes"
768 | msgstr ""
769 |
770 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
771 | #. Journey'
772 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
773 | msgid "Taxi"
774 | msgstr ""
775 |
776 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.js:57
777 | msgid "The end date should not be before the start date!"
778 | msgstr ""
779 |
780 | #. Label of a Data field in DocType 'Business Trip'
781 | #. Label of a Data field in DocType 'Business Trip Region'
782 | #. Label of a Data field in DocType 'Religious Denomination'
783 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
784 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
785 | #: erpnext_germany/erpnext_germany/doctype/religious_denomination/religious_denomination.json
786 | msgid "Title"
787 | msgstr ""
788 |
789 | #. Label of a Data field in DocType 'Business Trip Journey'
790 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
791 | msgid "To"
792 | msgstr ""
793 |
794 | #. Label of a Date field in DocType 'Business Trip'
795 | #. Label of a Date field in DocType 'Business Trip Accommodation'
796 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
797 | #: erpnext_germany/erpnext_germany/doctype/business_trip_accommodation/business_trip_accommodation.json
798 | msgid "To Date"
799 | msgstr ""
800 |
801 | #. Label of a Time field in DocType 'Business Trip Allowance'
802 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
803 | msgid "To Time"
804 | msgstr ""
805 |
806 | #. Label of a Currency field in DocType 'Business Trip'
807 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
808 | msgid "Total Allowance"
809 | msgstr ""
810 |
811 | #. Label of a Currency field in DocType 'Business Trip'
812 | #: erpnext_germany/erpnext_germany/doctype/business_trip/business_trip.json
813 | msgid "Total Mileage Allowance"
814 | msgstr ""
815 |
816 | #. Label of a Data field in DocType 'VAT ID Check'
817 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
818 | msgid "Trader City"
819 | msgstr ""
820 |
821 | #. Label of a Check field in DocType 'VAT ID Check'
822 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
823 | msgid "Trader City Match"
824 | msgstr ""
825 |
826 | #. Label of a Data field in DocType 'VAT ID Check'
827 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
828 | msgid "Trader Name"
829 | msgstr ""
830 |
831 | #. Label of a Check field in DocType 'VAT ID Check'
832 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
833 | msgid "Trader Name Match"
834 | msgstr ""
835 |
836 | #. Label of a Data field in DocType 'VAT ID Check'
837 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
838 | msgid "Trader Postcode"
839 | msgstr ""
840 |
841 | #. Label of a Check field in DocType 'VAT ID Check'
842 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
843 | msgid "Trader Postcode Match"
844 | msgstr ""
845 |
846 | #. Label of a Data field in DocType 'VAT ID Check'
847 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
848 | msgid "Trader Street"
849 | msgstr ""
850 |
851 | #. Label of a Check field in DocType 'VAT ID Check'
852 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
853 | msgid "Trader Street Match"
854 | msgstr ""
855 |
856 | #. Option for the 'Mode of Transport' (Select) field in DocType 'Business Trip
857 | #. Journey'
858 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
859 | msgid "Train"
860 | msgstr ""
861 |
862 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.js:47
863 | msgid "Unsaved changes. Save the document first to get a preview."
864 | msgstr ""
865 |
866 | #. Description of the 'Distance' (Int) field in DocType 'Business Trip Journey'
867 | #: erpnext_germany/erpnext_germany/doctype/business_trip_journey/business_trip_journey.json
868 | msgid "Used to calculate the total Mileage Allowance"
869 | msgstr ""
870 |
871 | #. Name of a DocType
872 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check.json
873 | msgid "VAT ID Check"
874 | msgstr ""
875 |
876 | #: erpnext_germany/erpnext_germany/doctype/vat_id_check/vat_id_check_list.js:7
877 | msgid "Valid"
878 | msgstr ""
879 |
880 | #. Label of a Date field in DocType 'Business Trip Region'
881 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
882 | msgid "Valid From"
883 | msgstr ""
884 |
885 | #. Label of a shortcut in the Germany Workspace
886 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
887 | msgid "Vat Id Check"
888 | msgstr ""
889 |
890 | #. Label of a Check field in DocType 'Business Trip Allowance'
891 | #. Label of a Currency field in DocType 'Business Trip Region'
892 | #: erpnext_germany/erpnext_germany/doctype/business_trip_allowance/business_trip_allowance.json
893 | #: erpnext_germany/erpnext_germany/doctype/business_trip_region/business_trip_region.json
894 | msgid "Whole Day"
895 | msgstr ""
896 |
897 | #: erpnext_germany/custom_fields.py:76
898 | msgid "Working Hours Per Week"
899 | msgstr ""
900 |
901 | #: erpnext_germany/public/js/erpnext_germany/business_letter.js:7
902 | msgid "You can use {0} in the Subject and Content fields for dynamic values.
All address details are available under the address
object (e.g., {{ address.city }}
), contact details under the contact
object (e.g., {{ contact.first_name }}
), and any specific information related to the dynamically linked document under the reference
object (e.g., {{ reference.some_field }}
).
"
903 | msgstr ""
904 |
905 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.py:53
906 | msgid "cancelled Business Letter {0}"
907 | msgstr ""
908 |
909 | #: erpnext_germany/erpnext_germany/doctype/business_letter/business_letter.py:46
910 | msgid "submitted Business Letter {0}"
911 | msgstr ""
912 |
913 | #. Description of the 'Mileage Allowance' (Currency) field in DocType 'Business
914 | #. Trip Settings'
915 | #: erpnext_germany/erpnext_germany/doctype/business_trip_settings/business_trip_settings.json
916 | msgid "value / distance: e.g. 0.30€/km"
917 | msgstr ""
918 |
919 | #. Count format of shortcut in the Germany Workspace
920 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
921 | msgid "{} Invalid"
922 | msgstr ""
923 |
924 | #. Count format of shortcut in the Germany Workspace
925 | #: erpnext_germany/erpnext_germany/workspace/germany/germany.json
926 | msgid "{} Open"
927 | msgstr ""
928 |
929 |
--------------------------------------------------------------------------------
/erpnext_germany/modules.txt:
--------------------------------------------------------------------------------
1 | ERPNext Germany
--------------------------------------------------------------------------------
/erpnext_germany/patches.txt:
--------------------------------------------------------------------------------
1 | [pre_model_sync]
2 |
3 | [post_model_sync]
4 | execute:from erpnext_germany.install import after_install; after_install() # 7
5 | erpnext_germany.patches.add_tax_exemption_reason_fields
6 | execute:from erpnext_germany.install import insert_custom_records; insert_custom_records()
7 | erpnext_germany.patches.change_position_of_register_info
8 | erpnext_germany.patches.dynamic_party_in_vat_id_check
9 | erpnext_germany.patches.add_business_trip_to_expense_claim
10 | erpnext_germany.patches.import_business_trip_regions
11 | execute:from erpnext_germany.install import make_custom_fields; make_custom_fields()
12 | execute:frappe.db.set_single_value("ERPNext Germany Settings", "prevent_gaps_in_transaction_naming", 1)
13 | erpnext_germany.patches.set_business_trip_settings
14 | erpnext_germany.patches.remove_some_employee_property_setters
15 | execute:from erpnext_germany.install import make_property_setters; make_property_setters()
16 |
--------------------------------------------------------------------------------
/erpnext_germany/patches/add_business_trip_to_expense_claim.py:
--------------------------------------------------------------------------------
1 | from frappe import get_app_path, get_installed_apps
2 | from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
3 |
4 | from erpnext_germany.custom_fields import _
5 | from erpnext_germany.install import import_csv
6 |
7 |
8 | def execute():
9 | if "hrms" in get_installed_apps():
10 | create_custom_fields(
11 | {
12 | "Expense Claim": [
13 | {
14 | "fieldtype": "Link",
15 | "fieldname": "business_trip",
16 | "label": _("Business Trip"),
17 | "options": "Business Trip",
18 | "insert_after": "company",
19 | },
20 | ]
21 | }
22 | )
23 |
24 | import_csv("Expense Claim Type", get_app_path("erpnext_germany", "data", "expense_claim_type.csv"))
25 |
--------------------------------------------------------------------------------
/erpnext_germany/patches/add_tax_exemption_reason_fields.py:
--------------------------------------------------------------------------------
1 | from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
2 |
3 |
4 | def execute():
5 | create_custom_fields(
6 | {
7 | ("Quotation", "Sales Order", "Sales Invoice"): [
8 | {
9 | "label": "Tax Exemption Reason",
10 | "fieldtype": "Small Text",
11 | "fieldname": "tax_exemption_reason",
12 | "fetch_from": "taxes_and_charges.tax_exemption_reason",
13 | "depends_on": "tax_exemption_reason",
14 | "insert_after": "taxes_and_charges",
15 | "translatable": 0,
16 | },
17 | ],
18 | "Sales Taxes and Charges Template": [
19 | {
20 | "label": "Tax Exemption Reason",
21 | "fieldtype": "Small Text",
22 | "fieldname": "tax_exemption_reason",
23 | "insert_after": "tax_category",
24 | "translatable": 0,
25 | }
26 | ],
27 | }
28 | )
29 |
--------------------------------------------------------------------------------
/erpnext_germany/patches/change_position_of_register_info.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 |
4 | def execute():
5 | """Update the position of the Register Information section in Customer and Supplier."""
6 | for dt in ("Customer", "Supplier"):
7 | cf = frappe.db.exists("Custom Field", {"fieldname": "register_sb_1", "dt": dt})
8 | if not cf:
9 | continue
10 |
11 | frappe.db.set_value("Custom Field", cf, "insert_after", "companies")
12 |
--------------------------------------------------------------------------------
/erpnext_germany/patches/dynamic_party_in_vat_id_check.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from frappe.model.utils.rename_field import rename_field
3 |
4 |
5 | def execute():
6 | """Copy data to renamed fields in VAT ID Check.
7 |
8 | party_type -> "Customer",
9 | customer -> party,
10 | customer_vat_id -> party_vat_id,
11 | customer_address -> party_address,
12 |
13 | Add link to Supplier connections.
14 | Update link in Customer connections.
15 | """
16 | dt = "VAT ID Check"
17 |
18 | frappe.db.sql("UPDATE `tabVAT ID Check` SET party_type = 'Customer'")
19 |
20 | rename_field(dt, "customer", "party")
21 | rename_field(dt, "customer_vat_id", "party_vat_id")
22 | rename_field(dt, "customer_address", "party_address")
23 |
24 | # Add link to Supplier connections
25 | frappe.get_doc(
26 | {
27 | "doctype": "DocType Link",
28 | "parent": "Supplier",
29 | "parentfield": "links",
30 | "parenttype": "Customize Form",
31 | "group": "Vendor Evaluation",
32 | "link_doctype": dt,
33 | "link_fieldname": "party",
34 | "custom": 1,
35 | }
36 | ).insert(ignore_if_duplicate=True)
37 |
38 | # Update link in Customer connections
39 | customer_link = frappe.db.exists("DocType Link", {"link_doctype": dt, "link_fieldname": "customer"})
40 | if customer_link:
41 | frappe.db.set_value("DocType Link", customer_link, "link_fieldname", "party")
42 |
--------------------------------------------------------------------------------
/erpnext_germany/patches/import_business_trip_regions.py:
--------------------------------------------------------------------------------
1 | from frappe import get_app_path
2 |
3 | from erpnext_germany.install import import_csv
4 |
5 |
6 | def execute():
7 | import_csv("Business Trip Region", get_app_path("erpnext_germany", "data", "business_trip_region.csv"))
8 |
--------------------------------------------------------------------------------
/erpnext_germany/patches/remove_some_employee_property_setters.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 |
4 | def execute():
5 | frappe.db.delete(
6 | "Property Setter",
7 | {
8 | "doc_type": "Employee",
9 | "field_name": "bank_ac_no",
10 | "property": "label",
11 | "value": "IBAN",
12 | },
13 | )
14 |
15 | frappe.db.delete(
16 | "Property Setter",
17 | {
18 | "doc_type": "Employee",
19 | "field_name": "ctc",
20 | "property": "hidden",
21 | "value": 1,
22 | },
23 | )
24 |
--------------------------------------------------------------------------------
/erpnext_germany/patches/set_business_trip_settings.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 | from erpnext_germany.erpnext_germany.doctype.business_trip.business_trip import (
4 | DEFAULT_EXPENSE_CLAIM_TYPE,
5 | )
6 |
7 |
8 | def execute():
9 | if not frappe.db.exists("Expense Claim Type", DEFAULT_EXPENSE_CLAIM_TYPE):
10 | return
11 |
12 | settings = frappe.get_single("Business Trip Settings")
13 | if not settings.expense_claim_type:
14 | settings.expense_claim_type = DEFAULT_EXPENSE_CLAIM_TYPE
15 | if not settings.expense_claim_type_car:
16 | settings.expense_claim_type_car = DEFAULT_EXPENSE_CLAIM_TYPE
17 | if not settings.mileage_allowance:
18 | settings.mileage_allowance = 0.3
19 |
20 | settings.save()
21 |
--------------------------------------------------------------------------------
/erpnext_germany/property_setters.py:
--------------------------------------------------------------------------------
1 | PROTECTED_FILE_DOCTYPES = (
2 | "Quotation",
3 | "Sales Order",
4 | "Delivery Note",
5 | "Sales Invoice",
6 | "Request for Quotation",
7 | "Supplier Quotation",
8 | "Purchase Order",
9 | "Purchase Receipt",
10 | "Purchase Invoice",
11 | "Journal Entry",
12 | "Payment Entry",
13 | "Asset",
14 | "Asset Depreciation Schedule",
15 | "Asset Repair",
16 | "Asset Value Adjustment",
17 | "Asset Capitalization",
18 | "POS Invoice",
19 | "Dunning",
20 | "Business Letter",
21 | "Period Closing Voucher",
22 | "Contract",
23 | "Blanket Order",
24 | )
25 |
26 |
27 | def get_property_setters():
28 | return {
29 | "Employee": [
30 | ("salary_currency", "default", "EUR"),
31 | ("salary_mode", "default", "Bank"),
32 | ("permanent_accommodation_type", "hidden", 1),
33 | ("current_accommodation_type", "hidden", 1),
34 | ],
35 | PROTECTED_FILE_DOCTYPES: [
36 | (None, "protect_attached_files", 1),
37 | ],
38 | }
39 |
--------------------------------------------------------------------------------
/erpnext_germany/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/public/.gitkeep
--------------------------------------------------------------------------------
/erpnext_germany/public/js/erpnext_germany.bundle.js:
--------------------------------------------------------------------------------
1 | import "./erpnext_germany/business_letter.js";
2 |
--------------------------------------------------------------------------------
/erpnext_germany/public/js/erpnext_germany/business_letter.js:
--------------------------------------------------------------------------------
1 | frappe.provide("erpnext_germany.business_letter");
2 |
3 | erpnext_germany.business_letter = {
4 | ...erpnext_germany.business_letter,
5 |
6 | get_help_text() {
7 | return __(
8 | "You can use {0} in the Subject and Content fields for dynamic values.
All address details are available under the address
object (e.g., {{ address.city }}
), contact details under the contact
object (e.g., {{ contact.first_name }}
), and any specific information related to the dynamically linked document under the reference
object (e.g., {{ reference.some_field }}
).
",
9 | [
10 | 'Jinja tags',
11 | ]
12 | );
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/erpnext_germany/tasks.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from erpnext import get_default_company
3 | from frappe.query_builder import DocType
4 | from pypika import Interval
5 | from pypika import functions as fn
6 |
7 |
8 | def all():
9 | check_some_parties()
10 |
11 |
12 | def get_customers(batch_size=4):
13 | """Return a list of n customers who didn't have their VAT ID checked in the last 3 months."""
14 | customers = DocType("Customer")
15 | last_check = get_last_check_query("Customer")
16 |
17 | return (
18 | frappe.qb.from_(customers)
19 | .left_join(last_check)
20 | .on(customers.name == last_check.party)
21 | .select(
22 | fn.LiteralValue("'Customer'"),
23 | customers.name,
24 | customers.customer_name,
25 | customers.customer_primary_address,
26 | customers.tax_id,
27 | )
28 | .where(
29 | customers.tax_id.notnull()
30 | & (customers.disabled == 0)
31 | & (last_check.creation.isnull() | (last_check.creation < fn.Now() - Interval(months=3)))
32 | )
33 | .limit(batch_size)
34 | .run()
35 | )
36 |
37 |
38 | def get_suppliers(batch_size=4):
39 | """Return a list of n suppliers who didn't have their VAT ID checked in the last 3 months."""
40 | suppliers = DocType("Supplier")
41 | last_check = get_last_check_query("Supplier")
42 |
43 | return (
44 | frappe.qb.from_(suppliers)
45 | .left_join(last_check)
46 | .on(suppliers.name == last_check.party)
47 | .select(
48 | fn.LiteralValue("'Supplier'"),
49 | suppliers.name,
50 | suppliers.supplier_name,
51 | suppliers.supplier_primary_address,
52 | suppliers.tax_id,
53 | )
54 | .where(
55 | suppliers.tax_id.notnull()
56 | & (suppliers.disabled == 0)
57 | & (last_check.creation.isnull() | (last_check.creation < fn.Now() - Interval(months=3)))
58 | )
59 | .limit(batch_size)
60 | .run()
61 | )
62 |
63 |
64 | def get_last_check_query(party_type: str):
65 | vat_id_checks = DocType("VAT ID Check")
66 | return (
67 | frappe.qb.from_(vat_id_checks)
68 | .select(
69 | vat_id_checks.party,
70 | fn.Max(vat_id_checks.creation).as_("creation"),
71 | )
72 | .where(
73 | (vat_id_checks.party_type == party_type)
74 | & (vat_id_checks.status.isin(["Completed", "Invalid Input"]))
75 | )
76 | .groupby(vat_id_checks.party)
77 | )
78 |
79 |
80 | def check_some_parties():
81 | """Check VAT IDs of customers who didn't have their VAT ID checked in the last 3 months."""
82 | requester_vat_id = None
83 | if company := get_default_company():
84 | requester_vat_id = frappe.get_cached_value("Company", company, "tax_id")
85 |
86 | for party_type, party, party_name, primary_address, vat_id in get_customers() + get_suppliers():
87 | doc = frappe.new_doc("VAT ID Check")
88 | doc.party_type = party_type
89 | doc.party = party
90 | doc.trader_name = party_name
91 | doc.party_vat_id = vat_id
92 | doc.company = company
93 | doc.requester_vat_id = requester_vat_id
94 | if primary_address:
95 | address = frappe.get_doc("Address", primary_address)
96 | doc.trader_street = address.address_line1
97 | doc.trader_postcode = address.pincode
98 | doc.trader_city = address.city
99 |
100 | doc.insert(ignore_permissions=True)
101 |
--------------------------------------------------------------------------------
/erpnext_germany/templates/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/templates/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/templates/pages/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/templates/pages/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/uninstall.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 | from .custom_fields import get_custom_fields
4 | from .property_setters import get_property_setters
5 |
6 |
7 | def before_uninstall():
8 | remove_custom_fields()
9 | remove_property_setters()
10 | remove_custom_records()
11 |
12 |
13 | def remove_custom_fields():
14 | print("* removing custom fields...")
15 | for doctypes, custom_fields in get_custom_fields().items():
16 | if isinstance(doctypes, str):
17 | doctypes = (doctypes,)
18 |
19 | for doctype in doctypes:
20 | for cf in custom_fields:
21 | frappe.db.delete(
22 | "Custom Field",
23 | {
24 | "dt": doctype,
25 | "fieldname": cf.get("fieldname"),
26 | },
27 | )
28 |
29 |
30 | def remove_property_setters():
31 | print("* removing property setters...")
32 | for doctypes, property_setters in get_property_setters().items():
33 | if isinstance(doctypes, str):
34 | doctypes = (doctypes,)
35 |
36 | for doctype in doctypes:
37 | for ps in property_setters:
38 | frappe.db.delete(
39 | "Property Setter",
40 | {
41 | "doc_type": doctype,
42 | "field_name": ps[0],
43 | "property": ps[1],
44 | "value": ps[2],
45 | },
46 | )
47 |
48 |
49 | def remove_custom_records():
50 | print("* removing custom records...")
51 | for record in frappe.get_hooks("germany_custom_records"):
52 | doctype = record.pop("doctype")
53 | frappe.db.delete(doctype, record)
54 |
--------------------------------------------------------------------------------
/erpnext_germany/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyf-de/erpnext_germany/f047b8ec625d32f09aea578ceb4ee79b675e49bd/erpnext_germany/utils/__init__.py
--------------------------------------------------------------------------------
/erpnext_germany/utils/eu_vat.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from requests.exceptions import ConnectionError
4 | from tenacity import (
5 | retry,
6 | retry_any,
7 | retry_if_exception_message,
8 | retry_if_exception_type,
9 | stop_after_attempt,
10 | wait_exponential,
11 | )
12 | from zeep import Client
13 |
14 | WSDL_URL = "https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl"
15 | COUNTRY_CODE_REGEX = r"^[A-Z]{2}$"
16 | VAT_NUMBER_REGEX = r"^[0-9A-Za-z\+\*\.]{2,12}$"
17 |
18 |
19 | def parse_vat_id(vat_id: str) -> tuple[str, str]:
20 | country_code = vat_id[:2].upper()
21 | vat_number = vat_id[2:].replace(" ", "")
22 |
23 | # check vat_number and country_code with regex
24 | if not re.match(COUNTRY_CODE_REGEX, country_code):
25 | raise ValueError("Invalid country code")
26 |
27 | if not re.match(VAT_NUMBER_REGEX, vat_number):
28 | raise ValueError("Invalid VAT number")
29 |
30 | return country_code, vat_number
31 |
32 |
33 | def check_vat(country_code: str, vat_number: str):
34 | """Use the EU VAT checker to validate a VAT ID."""
35 | return Client(WSDL_URL).service.checkVat(vatNumber=vat_number, countryCode=country_code)
36 |
37 |
38 | @retry(
39 | retry=retry_any(
40 | retry_if_exception_message(message="GLOBAL_MAX_CONCURRENT_REQ"),
41 | retry_if_exception_message(message="MS_MAX_CONCURRENT_REQ"),
42 | retry_if_exception_message(message="SERVICE_UNAVAILABLE"),
43 | retry_if_exception_message(message="MS_UNAVAILABLE"),
44 | retry_if_exception_message(message="TIMEOUT"),
45 | retry_if_exception_type(ConnectionError),
46 | ),
47 | stop=stop_after_attempt(3),
48 | wait=wait_exponential(multiplier=1, min=2, max=64),
49 | )
50 | def check_vat_approx(
51 | country_code: str,
52 | vat_number: str,
53 | trader_name: str | None = None,
54 | trader_street: str | None = None,
55 | trader_postcode: str | None = None,
56 | trader_city: str | None = None,
57 | requester_country_code: str | None = None,
58 | requester_vat_number: str | None = None,
59 | ):
60 | return Client(WSDL_URL).service.checkVatApprox(
61 | countryCode=country_code,
62 | vatNumber=vat_number,
63 | traderName=trader_name,
64 | traderStreet=trader_street,
65 | traderPostcode=trader_postcode,
66 | traderCity=trader_city,
67 | requesterCountryCode=requester_country_code,
68 | requesterVatNumber=requester_vat_number,
69 | )
70 |
--------------------------------------------------------------------------------
/erpnext_germany/utils/test_utils.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from .eu_vat import is_valid_vat_id
4 |
5 |
6 | class TestUtils(TestCase):
7 | def test_validate_vat_id(self):
8 | valid_ids = [
9 | "DE329035522", # ALYF
10 | "DE210157578", # SAP
11 | ]
12 | invalid_ids = [
13 | "ABC123",
14 | "Test Test",
15 | "DE1234567890",
16 | ]
17 |
18 | for vat_id in valid_ids:
19 | self.assertTrue(is_valid_vat_id(vat_id))
20 |
21 | for vat_id in invalid_ids:
22 | self.assertFalse(is_valid_vat_id(vat_id))
23 |
--------------------------------------------------------------------------------
/flake8.conf:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore =
3 | B001,
4 | B007,
5 | B009,
6 | B010,
7 | B950,
8 | E101,
9 | E111,
10 | E114,
11 | E116,
12 | E117,
13 | E121,
14 | E122,
15 | E123,
16 | E124,
17 | E125,
18 | E126,
19 | E127,
20 | E128,
21 | E131,
22 | E201,
23 | E202,
24 | E203,
25 | E211,
26 | E221,
27 | E222,
28 | E223,
29 | E224,
30 | E225,
31 | E226,
32 | E228,
33 | E231,
34 | E241,
35 | E242,
36 | E251,
37 | E261,
38 | E262,
39 | E265,
40 | E266,
41 | E271,
42 | E272,
43 | E273,
44 | E274,
45 | E301,
46 | E302,
47 | E303,
48 | E305,
49 | E306,
50 | E402,
51 | E501,
52 | E502,
53 | E701,
54 | E702,
55 | E703,
56 | E741,
57 | F401,
58 | F403,
59 | F405,
60 | W191,
61 | W291,
62 | W292,
63 | W293,
64 | W391,
65 | W503,
66 | W504,
67 | E711,
68 | E129,
69 | F841,
70 | E713,
71 | E712,
72 | E722,
73 |
74 |
75 | max-line-length = 200
76 | exclude=.github/helper/semgrep_rules,test_*.py
77 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "erpnext_germany"
3 | authors = [
4 | { name = "ALYF GmbH", email = "hallo@alyf.de"}
5 | ]
6 | description = "App to hold regional code for Germany, built on top of ERPNext."
7 | requires-python = ">=3.10"
8 | readme = "README.md"
9 | dynamic = ["version"]
10 | dependencies = [
11 | "zeep~=4.2.1",
12 | "tenacity~=8.2.3",
13 | ]
14 |
15 | [build-system]
16 | requires = ["flit_core >=3.4,<4"]
17 | build-backend = "flit_core.buildapi"
18 |
19 | [tool.bench.frappe-dependencies]
20 | frappe = ">=15.64.0,<16.0.0"
21 | erpnext = ">=15.58.0,<16.0.0"
22 |
23 | [tool.ruff]
24 | line-length = 110
25 | target-version = "py310"
26 |
27 | [tool.ruff.lint]
28 | select = [
29 | "F",
30 | "E",
31 | "W",
32 | "I",
33 | "UP",
34 | "B",
35 | "RUF",
36 | ]
37 | ignore = [
38 | "E101", # indentation contains mixed spaces and tabs
39 | "E402", # module level import not at top of file
40 | "W191", # indentation contains tabs
41 | ]
42 | typing-modules = ["frappe.types.DF"]
43 |
44 | [tool.ruff.format]
45 | quote-style = "double"
46 | indent-style = "tab"
47 | docstring-code-format = true
48 |
49 | [project.urls]
50 | Homepage = "https://github.com/alyf-de/erpnext_germany"
51 | Repository = "https://github.com/alyf-de/erpnext_germany.git"
52 | "Bug Reports" = "https://github.com/alyf-de/erpnext_germany/issues"
53 |
--------------------------------------------------------------------------------