diff --git a/msp/msp/doctype/auto_invoice_generator/__init__.py b/msp/msp/doctype/auto_invoice_generator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.js b/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.js new file mode 100644 index 0000000..d652f3a --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.js @@ -0,0 +1,8 @@ +// Copyright (c) 2022, itsdave GmbH and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Auto Invoice Generator', { + // refresh: function(frm) { + + // } +}); diff --git a/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.json b/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.json new file mode 100644 index 0000000..32f92c1 --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.json @@ -0,0 +1,97 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "AUTOINV-.#####", + "creation": "2022-08-29 13:47:37.295147", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "invoicing_grouped_by", + "close_invoiced_delivery_notes_section", + "close", + "invoices_from_delivery_notes_section", + "customer", + "get_invoice", + "date", + "invoice_count", + "customer_count" + ], + "fields": [ + { + "fieldname": "invoicing_grouped_by", + "fieldtype": "Select", + "label": "Invoicing grouped by", + "options": "\nService and Goods\nService and Goods and Sales Order" + }, + { + "fieldname": "close_invoiced_delivery_notes_section", + "fieldtype": "Section Break", + "label": "Close invoiced Delivery Notes" + }, + { + "fieldname": "close", + "fieldtype": "Button", + "label": "Close" + }, + { + "fieldname": "invoices_from_delivery_notes_section", + "fieldtype": "Section Break", + "label": "Invoices from delivery notes" + }, + { + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer" + }, + { + "fieldname": "get_invoice", + "fieldtype": "Button", + "label": "Get Invoice", + "options": "get_invoice_dict" + }, + { + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date" + }, + { + "fieldname": "invoice_count", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Invoice count" + }, + { + "fieldname": "customer_count", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Customer count" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-08-29 14:26:06.333736", + "modified_by": "Administrator", + "module": "MSP", + "name": "Auto Invoice Generator", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.py b/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.py new file mode 100644 index 0000000..b906b80 --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator/auto_invoice_generator.py @@ -0,0 +1,209 @@ +# Copyright (c) 2022, itsdave GmbH and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document +from doctest import debug +import frappe +from frappe.model.document import Document +from erpnext.accounts.party import set_taxes as party_st +from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice +from datetime import datetime +from frappe import ValidationError, _ + +class AutoInvoiceGenerator(Document): + + @frappe.whitelist() + def close_invoiced_delivery_notes(self): + dns = frappe.get_all("Delivery Note", filters={"status": "To Bill"}) + print(len(dns)) + dns_to_close = [] + + for el in dns: + try: + make_sales_invoice(el["name"]) + except ValidationError as e: + if str(e) == _("All these items have already been Invoiced/Returned"): + print(_("All these items have already been Invoiced/Returned") + " in: " + el["name"]) + dns_to_close.append(el["name"]) + count = 0 + print(dns_to_close) + for pos in dns_to_close: + count +=1 + frappe.db.set_value("Delivery Note", pos, "status", "Closed") + frappe.msgprint( str(count)+ " delivery notes were closed") + + @frappe.whitelist() + def get_delivery_notes_for_invoicing(self): + + #Funktion soll eine Liste aller Lieferscheine zurückgeben die abzurechnen sind + filters = {"status" : "to bill","is_return": 0, "project": "", "blocked_for_billing": 0 } + #filters = {"status" : "to bill","is_return": 0, "project": ""} + delivery_notes_list = frappe.get_all("Delivery Note", filters = filters, fields = ["name", "customer", "project"]) + print(len(delivery_notes_list)) + dl = [x.name for x in delivery_notes_list if x.project] + print(dl) + print(len(dl)) + + return delivery_notes_list + @frappe.whitelist() + def get_customer_for_invoicing(self, delivery_note): + + #Funktion erstellt eine Liste von Kunden für die Rechnungen erstellt werden sollen + + delivery_notes = self.get_delivery_notes_for_invoicing() + + customer_list = [x.customer for x in delivery_notes] + + customer_list_exc_duplicate = list(set(customer_list)) + #print(customer_list_exc_duplicate) + + return customer_list_exc_duplicate + + def get_delivery_notes_for_customer(self, cust, delivery_notes): + +#Funktion listet alle Lieferscheine für den jeweiligen Kunden auf + cust_del_note = [] + + for el in delivery_notes: + + if el["customer"] == cust: + cust_del_note.append(el) + + return cust_del_note + @frappe.whitelist() + def get_invoice_dict(self): + + + del_not = self.get_delivery_notes_for_invoicing() + print(del_not) + cust_list = self.get_customer_for_invoicing(del_not) + if self.customer: + customer_list = [self.customer] + else: + customer_list = cust_list + + print(len(customer_list)) + cust_count = 0 + invoice_count = 0 + for cust in customer_list: + print(cust) + cust_doc = frappe.get_doc("Customer",cust) + invoice_in_draft = frappe.get_all("Sales Invoice", filters = {"status" : "Draft", "customer": cust}) + if len(invoice_in_draft) > 0: + print("nicht bearbeitet") + print(cust) + continue + else: + cust_count += 1 + #if self.invoicing_grouped_by == "None": + + delivery_note_list = self.get_delivery_notes_for_customer(cust,del_not) + #print(delivery_note_list) + if len(delivery_note_list) < 1: + frappe.msgprint("Keine abrechnenbaren Lieferscheine vorhanden") + sales_order_items = [] + service_items = [] + goods_items =[] + for dn in delivery_note_list: + + item_doc = frappe.get_doc("Delivery Note", dn["name"]) + + for item in item_doc.items: + print(item.item_group) + invoice_doc_item = frappe.get_doc({ + "doctype": "Sales Invoice Item", + "item_code": item.item_code, + "description": item.description, + "qty": item.qty, + "uom" : item.uom, + "rate": item.rate, + "sales_order": item.against_sales_order, + "dn_detail": item.name, + "parent": "delivery_note", + "delivery_note": dn["name"] + + }) + print(item.dn_detail) + print(dn["name"]) + print(item.qty) + print(item.uom) + if item.item_group == "Dienstleistungen" or item.item_group == "Anfahrten" or item.item_group == "Arbeitszeiten Techniker" or item.item_group == "Anwendungsentwicklung": + service_items.append(invoice_doc_item) + elif item.against_sales_order: + sales_order_items.append(invoice_doc_item) + else: + goods_items.append(invoice_doc_item) + + if self.invoicing_grouped_by == "Service and Goods and Sales Order": + self.create_invoice(cust, goods_items, "Ware " + cust_doc.customer_name) + if len(goods_items) > 0: + invoice_count += 1 + self.create_invoice(cust, service_items, "Dienstleistung " + cust_doc.customer_name) + if len(service_items) > 0: + invoice_count += 1 + #self.create_invoice(cust, sales_order_items, "Sales Order " + cust_doc.customer_name) + if len(sales_order_items) > 0: + print("Sales Order Check") + x = [i.sales_order for i in sales_order_items] + print(x) + a = list(set(x)) + for el in a: + sal_ord_it = [] + for i in sales_order_items: + if i.sales_order == el: + sal_ord_it.append(i) + self.create_invoice(cust, sal_ord_it, "Sales Order "+ el+ " " + cust_doc.customer_name) + invoice_count += 1 + elif self.invoicing_grouped_by == "Service and Goods": + invoice_item = sales_order_items + goods_items + self.create_invoice(cust, invoice_item, "Ware " + cust_doc.customer_name) + if len(invoice_item) > 0: + invoice_count += 1 + self.create_invoice(cust, service_items, "Dienstleistung " + cust_doc.customer_name) + if len(service_items) >0: + invoice_count += 1 + else: + invoice_item = sales_order_items + service_items + goods_items + self.create_invoice(cust, invoice_item, "Dienstleistung und Ware "+ cust_doc.customer_name) + if len(invoice_item) > 0: + invoice_count += 1 + frappe.msgprint("Für " + str(cust_count)+ " Kunden wurden " + str(invoice_count) + " Rechnungen erstellt.") + self.date = datetime.today().strftime('%Y-%m-%d') + self.invoice_count = invoice_count + self.customer_count = cust_count + def create_invoice(self,cust,invoice_doc_items,title): + invoice_doc = frappe.get_doc({ + "doctype": "Sales Invoice", + "title": title, + "customer": cust, + "company": frappe.get_doc("Global Defaults").default_company, + "items": invoice_doc_items + }) + if len(invoice_doc_items)>0: + settings_doc = frappe.get_single("Auto Invoice Generator Settings") + customer_doc = frappe.get_doc("Customer", cust ) + + if customer_doc.payment_terms: + invoice_doc.payment_terms_template = customer_doc.payment_terms + else: + invoice_doc.payment_terms_template = settings_doc.payment_terms_template + invoice_doc.tc_name = settings_doc.tc_name + tac_doc = frappe.get_doc("Terms and Conditions", settings_doc.tc_name) + invoice_doc.terms = tac_doc.terms + + invoice_doc.taxes_and_charges = party_st(invoice_doc.customer, "Customer", invoice_doc.posting_date, invoice_doc.company) + taxes = frappe.get_doc("Sales Taxes and Charges Template", settings_doc.taxes_and_charges).taxes + + for tax in taxes: + new_tax = frappe.get_doc({ + "doctype": "Sales Taxes and Charges", + "charge_type": tax.charge_type, + "account_head": tax.account_head, + "rate": tax.rate, + "description": tax.description + }) + invoice_doc.append("taxes", new_tax) + invoice_doc.save() + + diff --git a/msp/msp/doctype/auto_invoice_generator/test_auto_invoice_generator.py b/msp/msp/doctype/auto_invoice_generator/test_auto_invoice_generator.py new file mode 100644 index 0000000..642d42b --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator/test_auto_invoice_generator.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022, itsdave GmbH and Contributors +# See license.txt + +# import frappe +import unittest + +class TestAutoInvoiceGenerator(unittest.TestCase): + pass diff --git a/msp/msp/doctype/auto_invoice_generator_settings/__init__.py b/msp/msp/doctype/auto_invoice_generator_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.js b/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.js new file mode 100644 index 0000000..e7b3189 --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2022, itsdave GmbH and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Auto Invoice Generator Settings', { + // refresh: function(frm) { + + // } +}); diff --git a/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.json b/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.json new file mode 100644 index 0000000..ab3ec08 --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.json @@ -0,0 +1,56 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2022-08-29 14:02:35.295304", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "tc_name", + "payment_terms_template", + "taxes_and_charges" + ], + "fields": [ + { + "fieldname": "tc_name", + "fieldtype": "Link", + "label": "Terms and Conditions", + "options": "Terms and Conditions" + }, + { + "fieldname": "payment_terms_template", + "fieldtype": "Link", + "label": "Payment Terms Template", + "options": "Payment Terms Template" + }, + { + "fieldname": "taxes_and_charges", + "fieldtype": "Link", + "label": "Taxes and Charges", + "options": "Sales Taxes and Charges Template" + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2022-08-29 14:04:11.402701", + "modified_by": "Administrator", + "module": "MSP", + "name": "Auto Invoice Generator Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.py b/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.py new file mode 100644 index 0000000..1031ac3 --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator_settings/auto_invoice_generator_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022, itsdave GmbH and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class AutoInvoiceGeneratorSettings(Document): + pass diff --git a/msp/msp/doctype/auto_invoice_generator_settings/test_auto_invoice_generator_settings.py b/msp/msp/doctype/auto_invoice_generator_settings/test_auto_invoice_generator_settings.py new file mode 100644 index 0000000..3b60b41 --- /dev/null +++ b/msp/msp/doctype/auto_invoice_generator_settings/test_auto_invoice_generator_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022, itsdave GmbH and Contributors +# See license.txt + +# import frappe +import unittest + +class TestAutoInvoiceGeneratorSettings(unittest.TestCase): + pass