Auto invoice generator: Allow creation of invoices, though we have drafts for a customer, skip positions from drafts.

This commit is contained in:
Beate Trenziok 2024-08-01 06:23:19 +02:00
parent 4653083a5d
commit 1909ff6ed9
2 changed files with 353 additions and 140 deletions

View File

@ -12,6 +12,7 @@
"invoices_from_delivery_notes_section", "invoices_from_delivery_notes_section",
"customer", "customer",
"get_invoice", "get_invoice",
"inv_without_draft",
"it_contract_invoices_section", "it_contract_invoices_section",
"billing_month", "billing_month",
"contract_invoices", "contract_invoices",
@ -48,7 +49,7 @@
"fieldname": "get_invoice", "fieldname": "get_invoice",
"fieldtype": "Button", "fieldtype": "Button",
"label": "Get Invoice", "label": "Get Invoice",
"options": "get_invoice_dict" "options": "generate_invoices"
}, },
{ {
"fieldname": "date", "fieldname": "date",
@ -93,15 +94,22 @@
"fieldname": "it_contract_invoices_section", "fieldname": "it_contract_invoices_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "IT Contract Invoices" "label": "IT Contract Invoices"
},
{
"description": "Rechnungserstellung f\u00fcr Kunden, f\u00fcr die rechnungen im Entwurfstatus existeren",
"fieldname": "inv_without_draft",
"fieldtype": "Button",
"label": "Invoice without draft article ",
"options": "generate_invoices_without_draft_items"
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"migration_hash": "d64bd35ec0b0d442bcfc080be217f2f6", "modified": "2024-07-31 11:37:09.454442",
"modified": "2023-05-31 11:12:52.773165",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "MSP", "module": "MSP",
"name": "Auto Invoice Generator", "name": "Auto Invoice Generator",
"naming_rule": "Expression (old style)",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
@ -119,5 +127,6 @@
], ],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@ -86,7 +86,7 @@ class AutoInvoiceGenerator(Document):
return cust_del_note return cust_del_note
def get_invoicing_items_for_cust(self,cust): def get_invoicing_items_for_customer(self,cust):
#Funktion liefert eine Liste aller abrechenbaren Items für den jeweiligen Kunden #Funktion liefert eine Liste aller abrechenbaren Items für den jeweiligen Kunden
del_not = self.get_delivery_notes_for_invoicing() del_not = self.get_delivery_notes_for_invoicing()
@ -114,154 +114,341 @@ class AutoInvoiceGenerator(Document):
@frappe.whitelist() #@frappe.whitelist()
def get_invoice_dict(self): # def get_invoice_dict(self):
self.get_customer_asap_billing_mode() # self.get_customer_asap_billing_mode()
del_not = self.get_delivery_notes_for_invoicing() # del_not = self.get_delivery_notes_for_invoicing()
cust_list = self.get_customer_for_invoicing(del_not) # cust_list = self.get_customer_for_invoicing(del_not)
if self.customer: # if self.customer:
customer_list = [self.customer] # customer_list = [self.customer]
else: # else:
customer_list = cust_list # customer_list = cust_list
print(len(customer_list)) # print(len(customer_list))
cust_count = 0 # cust_count = 0
invoice_count = 0 # invoice_count = 0
log_list = [] # log_list = []
for cust in customer_list: # for cust in customer_list:
cust_doc = frappe.get_doc("Customer",cust) # cust_doc = frappe.get_doc("Customer",cust)
invoice_in_draft = frappe.get_all("Sales Invoice", filters = {"status" : "Draft", "customer": cust}) # invoice_in_draft = frappe.get_all("Sales Invoice", filters = {"status" : "Draft", "customer": cust})
if len(invoice_in_draft) > 0: # if len(invoice_in_draft) > 0:
log_list.append("Für Kunde"+ " "+ cust + " wurden keine Rechnungen erstellt, da noch nicht berechnete Rechnungen in Draft vorhanden") # log_list.append("Für Kunde"+ " "+ cust + " wurden keine Rechnungen erstellt, da noch nicht berechnete Rechnungen in Draft vorhanden")
continue # continue
else: # else:
cust_count += 1 # cust_count += 1
items = self.get_invoicing_items_for_cust(cust) # items = self.get_invoicing_items_for_cust(cust)
print(items) # print(items)
print("len Items") # print("len Items")
print(len(items)) # print(len(items))
invoicing_items = [] # invoicing_items = []
if cust_doc.billing_mode == "ASAP": # if cust_doc.billing_mode == "ASAP":
x = self.get_delivery_notes_for_customer(cust, del_not) # x = self.get_delivery_notes_for_customer(cust, del_not)
for dn in x: # for dn in x:
item_doc = frappe.get_doc("Delivery Note", dn["name"]) # item_doc = frappe.get_doc("Delivery Note", dn["name"])
items = [self.create_invoice_doc_item(item) for item in item_doc.items] # items = [self.create_invoice_doc_item(item) for item in item_doc.items]
for item in items: # for item in items:
item.delivery_note= dn["name"] # item.delivery_note= dn["name"]
if len(items) > 0: # if len(items) > 0:
self.create_invoice(cust, items, "Abrechnung Lieferschein " + dn["name"]+ " " + cust_doc.customer_name) # self.create_invoice(cust, items, "Abrechnung Lieferschein " + dn["name"]+ " " + cust_doc.customer_name)
invoice_count += 1 # invoice_count += 1
elif cust_doc.billing_mode == "Collective Bill": # elif cust_doc.billing_mode == "Collective Bill":
for item in items: # for item in items:
invoice_doc_item = self.create_invoice_doc_item(item) # invoice_doc_item = self.create_invoice_doc_item(item)
invoicing_items.append(invoice_doc_item) # invoicing_items.append(invoice_doc_item)
if len(invoicing_items) > 0: # if len(invoicing_items) > 0:
self.create_invoice(cust, invoicing_items, "Sammelrechnung " + cust_doc.customer_name) # self.create_invoice(cust, invoicing_items, "Sammelrechnung " + cust_doc.customer_name)
invoice_count += 1 # invoice_count += 1
else: # else:
item_group_separation_dict = get_item_group_assignment_table(cust) # item_group_separation_dict = get_item_group_assignment_table(cust)
print(item_group_separation_dict) # print(item_group_separation_dict)
separation_item_groups = [[item_group_separation_dict[x].item_group,item_group_separation_dict[x].filter] for x in range(1, len(item_group_separation_dict) + 1) ] # separation_item_groups = [[item_group_separation_dict[x].item_group,item_group_separation_dict[x].filter] for x in range(1, len(item_group_separation_dict) + 1) ]
if cust_doc.billing_mode == "per Item Group": # if cust_doc.billing_mode == "per Item Group":
print("vor Separation") # print("vor Separation")
print(len(items)) # print(len(items))
for el in separation_item_groups: # for el in separation_item_groups:
print(el[1]) # print(el[1])
print(items) # print(items)
a = [] # a = []
items_list = items.copy() # items_list = items.copy()
for item in items_list: # for item in items_list:
# for i in item_name: # # for i in item_name:
# item = filter(lambda item: item['name'] == i, items) # # item = filter(lambda item: item['name'] == i, items)
print(item) # print(item)
#print(item.item_group) # #print(item.item_group)
if item.item_group in el[1]: # if item.item_group in el[1]:
print(True) # print(True)
invoice_doc_item = self.create_invoice_doc_item(item) # invoice_doc_item = self.create_invoice_doc_item(item)
print(invoice_doc_item) # print(invoice_doc_item)
a.append(invoice_doc_item) # a.append(invoice_doc_item)
items.remove(item) # items.remove(item)
print(len(a)) # print(len(a))
print(len(items)) # print(len(items))
else: # else:
print(False) # print(False)
if len(a) > 0: # if len(a) > 0:
self.create_invoice(cust, a, el[0] + " " +cust_doc.customer_name) # self.create_invoice(cust, a, el[0] + " " +cust_doc.customer_name)
invoice_count +=1 # invoice_count +=1
print("nach Separation") # print("nach Separation")
print(len(items)) # print(len(items))
i_items = [self.create_invoice_doc_item(item) for item in items] # i_items = [self.create_invoice_doc_item(item) for item in items]
if len(i_items) > 0: # if len(i_items) > 0:
self.create_invoice(cust, i_items, "Abrechnung " + cust_doc.customer_name) # self.create_invoice(cust, i_items, "Abrechnung " + cust_doc.customer_name)
invoice_count += 1 # invoice_count += 1
if cust_doc.billing_mode == "per Sales Order, remaining per Item Group": # if cust_doc.billing_mode == "per Sales Order, remaining per Item Group":
sales_order_items = [] # sales_order_items = []
item_list = items.copy() # item_list = items.copy()
for item in item_list: # for item in item_list:
if item.against_sales_order: # if item.against_sales_order:
invoice_doc_item = self.create_invoice_doc_item(item) # invoice_doc_item = self.create_invoice_doc_item(item)
sales_order_items.append(invoice_doc_item) # sales_order_items.append(invoice_doc_item)
items.remove(item) # items.remove(item)
if len(sales_order_items) > 0: # if len(sales_order_items) > 0:
x = [i.sales_order for i in sales_order_items] # x = [i.sales_order for i in sales_order_items]
a = list(set(x)) # a = list(set(x))
for el in a: # for el in a:
sal_ord_it = [] # sal_ord_it = []
for i in sales_order_items: # for i in sales_order_items:
if i.sales_order == el: # if i.sales_order == el:
sal_ord_it.append(i) # sal_ord_it.append(i)
self.create_invoice(cust, sal_ord_it, "Sales Order "+ el+ " " + cust_doc.customer_name) # self.create_invoice(cust, sal_ord_it, "Sales Order "+ el+ " " + cust_doc.customer_name)
invoice_count += 1 # invoice_count += 1
for el in separation_item_groups: # for el in separation_item_groups:
print(el[1]) # print(el[1])
a = [] # a = []
it_list = items.copy() # it_list = items.copy()
for item in it_list: # for item in it_list:
if item.item_group in el[1]: # if item.item_group in el[1]:
invoice_doc_item = self.create_invoice_doc_item(item) # invoice_doc_item = self.create_invoice_doc_item(item)
a.append(invoice_doc_item) # a.append(invoice_doc_item)
items.remove(item) # items.remove(item)
if len(a) > 0: # if len(a) > 0:
self.create_invoice(cust, a, el[0] + " " +cust_doc.customer_name) # self.create_invoice(cust, a, el[0] + " " +cust_doc.customer_name)
invoice_count += 1 # invoice_count += 1
i_items = [self.create_invoice_doc_item(item) for item in items] # i_items = [self.create_invoice_doc_item(item) for item in items]
if len(i_items) > 0: # if len(i_items) > 0:
self.create_invoice(cust, i_items, "Abrechnung " + cust_doc.customer_name) # self.create_invoice(cust, i_items, "Abrechnung " + cust_doc.customer_name)
invoice_count += 1 # invoice_count += 1
frappe.msgprint("Für " + str(cust_count)+ " Kunden wurden " + str(invoice_count) + " Rechnungen erstellt.") # frappe.msgprint("Für " + str(cust_count)+ " Kunden wurden " + str(invoice_count) + " Rechnungen erstellt.")
self.date = datetime.today().strftime('%Y-%m-%d') # self.date = datetime.today().strftime('%Y-%m-%d')
self.invoice_count = invoice_count # self.invoice_count = invoice_count
self.customer_count = cust_count # self.customer_count = cust_count
log_str = "" # log_str = ""
for i in log_list: # for i in log_list:
log_str += i + "\n" # log_str += i + "\n"
self.log = log_str # self.log = log_str
self.save() # self.save()
# def create_invoice_doc_item(self, item):
# #Funktion kreiert Invoice Item aus den gegebenen Delivery Note Items
# 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": item.dn_detail
# })
# return invoice_doc_item
@frappe.whitelist()
def generate_invoices(self):
self.get_customer_asap_billing_mode()
delivery_notes = self.get_delivery_notes_for_invoicing()
customer_list = self.get_customer_for_invoicing(delivery_notes)
customer_list = [self.customer] if self.customer else customer_list
print(len(customer_list))
customer_count = 0
invoice_count = 0
log_list = []
for customer in customer_list:
customer_doc = frappe.get_doc("Customer", customer)
if self.check_draft_invoices(customer):
log_list.append(f"No invoices created for customer {customer} as draft invoices are present.")
continue
customer_count += 1
items = self.get_invoicing_items_for_customer(customer)
print(items)
print("Number of Items")
print(len(items))
if customer_doc.billing_mode == "ASAP":
invoice_count += self.process_asap_billing(customer, delivery_notes, customer_doc)
elif customer_doc.billing_mode == "Collective Bill":
invoice_count += self.process_collective_billing(customer, items, customer_doc)
else:
invoice_count += self.process_other_billing_modes(customer, items, customer_doc)
self.finish_invoice_creation(customer_count, invoice_count, log_list)
@frappe.whitelist()
def generate_invoices_without_draft_items(self):
self.get_customer_asap_billing_mode()
delivery_notes = self.get_delivery_notes_for_invoicing()
customer_list = self.get_customer_for_invoicing(delivery_notes)
customer_list = [self.customer] if self.customer else customer_list
print(len(customer_list))
customer_count = 0
invoice_count = 0
log_list = []
for customer in customer_list:
customer_doc = frappe.get_doc("Customer", customer)
draft_items = self.get_draft_items_for_customer(customer)
items = self.get_invoicing_items_for_customer(customer)
items = [item for item in items if item.name not in draft_items]
if not items:
log_list.append(f"No invoices created for customer {customer} as all items are in draft invoices.")
continue
customer_count += 1
print(items)
print("Number of Items")
print(len(items))
if customer_doc.billing_mode == "ASAP":
invoice_count += self.process_asap_billing(customer, delivery_notes, customer_doc, items)
elif customer_doc.billing_mode == "Collective Bill":
invoice_count += self.process_collective_billing(customer, items, customer_doc)
else:
invoice_count += self.process_other_billing_modes(customer, items, customer_doc)
self.finish_invoice_creation(customer_count, invoice_count, log_list)
def check_draft_invoices(self, customer):
draft_invoices = frappe.get_all("Sales Invoice", filters={"status": "Draft", "customer": customer})
return len(draft_invoices) > 0
def get_draft_items_for_customer(self, customer):
draft_items = frappe.get_all("Sales Invoice Item", filters={"parentfield": "items", "parent": ["in", frappe.get_all("Sales Invoice", filters={"status": "Draft", "customer": customer}, pluck="name")]})
return [item.dn_detail for item in draft_items]
def process_asap_billing(self, customer, delivery_notes, customer_doc, items=None):
invoice_count = 0
deliveries = self.get_delivery_notes_for_customer(customer, delivery_notes)
for delivery in deliveries:
delivery_doc = frappe.get_doc("Delivery Note", delivery["name"])
items = items or [self.create_invoice_doc_item(delivery_item) for delivery_item in delivery_doc.items]
for item in items:
item.delivery_note = delivery["name"]
if items:
self.create_invoice(customer, items, f"Invoice for Delivery Note {delivery['name']} {customer_doc.customer_name}")
invoice_count += 1
return invoice_count
def process_collective_billing(self, customer, items, customer_doc):
invoice_items = [self.create_invoice_doc_item(item) for item in items]
if invoice_items:
self.create_invoice(customer, invoice_items, f"Collective Bill {customer_doc.customer_name}")
return 1
return 0
def process_other_billing_modes(self, customer, items, customer_doc):
invoice_count = 0
item_group_separation_dict = get_item_group_assignment_table(customer)
separation_item_groups = [[item_group_separation_dict[x].item_group, item_group_separation_dict[x].filter] for x in range(1, len(item_group_separation_dict) + 1)]
if customer_doc.billing_mode == "per Item Group":
invoice_count += self.process_per_item_group(customer, items, separation_item_groups, customer_doc)
elif customer_doc.billing_mode == "per Sales Order, remaining per Item Group":
invoice_count += self.process_per_sales_order_remaining_per_item_group(customer, items, separation_item_groups, customer_doc)
return invoice_count
def process_per_item_group(self, customer, items, separation_item_groups, customer_doc):
invoice_count = 0
for group in separation_item_groups:
group_items, items = self.separate_items_by_group(items, group[1])
if group_items:
self.create_invoice(customer, group_items, f"{group[0]} {customer_doc.customer_name}")
invoice_count += 1
remaining_items = [self.create_invoice_doc_item(item) for item in items]
if remaining_items:
self.create_invoice(customer, remaining_items, f"Invoice {customer_doc.customer_name}")
invoice_count += 1
return invoice_count
def process_per_sales_order_remaining_per_item_group(self, customer, items, separation_item_groups, customer_doc):
invoice_count = 0
sales_order_items, items = self.separate_sales_order_items(items)
invoice_count += self.create_sales_order_invoices(customer, sales_order_items, customer_doc)
for group in separation_item_groups:
group_items, items = self.separate_items_by_group(items, group[1])
if group_items:
self.create_invoice(customer, group_items, f"{group[0]} {customer_doc.customer_name}")
invoice_count += 1
remaining_items = [self.create_invoice_doc_item(item) for item in items]
if remaining_items:
self.create_invoice(customer, remaining_items, f"Invoice {customer_doc.customer_name}")
invoice_count += 1
return invoice_count
def separate_items_by_group(self, items, group_filter):
group_items = []
remaining_items = items.copy()
for item in remaining_items:
if item.item_group in group_filter:
group_items.append(self.create_invoice_doc_item(item))
items.remove(item)
return group_items, items
def separate_sales_order_items(self, items):
sales_order_items = [self.create_invoice_doc_item(item) for item in items if item.against_sales_order]
remaining_items = [item for item in items if not item.against_sales_order]
return sales_order_items, remaining_items
def create_sales_order_invoices(self, customer, sales_order_items, customer_doc):
invoice_count = 0
sales_orders = set(item.sales_order for item in sales_order_items)
for order in sales_orders:
order_items = [item for item in sales_order_items if item.sales_order == order]
self.create_invoice(customer, order_items, f"Sales Order {order} {customer_doc.customer_name}")
invoice_count += 1
return invoice_count
def create_invoice_doc_item(self, item): def create_invoice_doc_item(self, item):
#Funktion kreiert Invoice Item aus den gegebenen Delivery Note Items return frappe.get_doc({
invoice_doc_item = frappe.get_doc({ "doctype": "Sales Invoice Item",
"doctype": "Sales Invoice Item", "item_code": item.item_code,
"item_code": item.item_code, "description": item.description,
"description": item.description, "qty": item.qty,
"qty": item.qty, "uom": item.uom,
"uom" : item.uom, "rate": item.rate,
"rate": item.rate, "sales_order": item.against_sales_order,
"sales_order": item.against_sales_order, "dn_detail": item.name,
"dn_detail": item.name, "parent": "delivery_note",
"parent": "delivery_note", "delivery_note": item.dn_detail
"delivery_note": item.dn_detail })
})
return invoice_doc_item def finish_invoice_creation(self, customer_count, invoice_count, log_list):
frappe.msgprint(f"{customer_count} customers had {invoice_count} invoices created.")
self.date = datetime.today().strftime('%Y-%m-%d')
self.invoice_count = invoice_count
self.customer_count = customer_count
self.log = "\n".join(log_list)
self.save()
def create_invoice(self,cust,invoice_doc_items,title): def create_invoice(self,cust,invoice_doc_items,title):
@ -400,6 +587,23 @@ class AutoInvoiceGenerator(Document):
new_billing_month = "{:02d}.{}".format(month, year) new_billing_month = "{:02d}.{}".format(month, year)
return new_billing_month return new_billing_month
@frappe.whitelist()
def get_invoice_despite_draft(self):
self.get_customer_asap_billing_mode()
del_not = self.get_delivery_notes_for_invoicing()
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))
log_list = []
for cust in customer_list:
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:
log_list.append("Für Kunde"+ " "+ cust + " wurden keine Rechnungen erstellt, da noch nicht berechnete Rechnungen in Draft vorhanden")