diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json index 5c3519a1592..02b0c4d937b 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json @@ -18,7 +18,8 @@ "in_list_view": 1, "label": "Invoice", "options": "Sales Invoice", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fetch_from": "sales_invoice.customer", @@ -60,7 +61,7 @@ } ], "istable": 1, - "modified": "2019-09-26 11:05:36.016772", + "modified": "2020-02-20 16:16:20.724620", "modified_by": "Administrator", "module": "Accounts", "name": "Discounted Invoice", diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py index 93dfcc14bda..109737f7276 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py @@ -7,4 +7,4 @@ from __future__ import unicode_literals from frappe.model.document import Document class DiscountedInvoice(Document): - pass + pass \ No newline at end of file diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 041e419752b..f9e4fd77148 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -232,11 +232,36 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga if bal < 0 and not on_cancel: frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) - # Update outstanding amt on against voucher if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: + update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal) + +def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal): + data = [] + # Update outstanding amt on against voucher + if against_voucher_type == "Fees": ref_doc = frappe.get_doc(against_voucher_type, against_voucher) ref_doc.db_set('outstanding_amount', bal) ref_doc.set_status(update=True) + return + elif against_voucher_type == "Purchase Invoice": + from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status + data = frappe.db.get_value(against_voucher_type, against_voucher, + ["name as purchase_invoice", "outstanding_amount", + "is_return", "due_date", "docstatus"]) + elif against_voucher_type == "Sales Invoice": + from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status + data = frappe.db.get_value(against_voucher_type, against_voucher, + ["name as sales_invoice", "outstanding_amount", "is_discounted", + "is_return", "due_date", "docstatus"]) + + precision = frappe.get_precision(against_voucher_type, "outstanding_amount") + data = list(data) + data.append(precision) + status = get_status(data) + frappe.db.set_value(against_voucher_type, against_voucher, { + 'outstanding_amount': bal, + 'status': status + }) def validate_frozen_account(account, adv_adj=None): frozen_account = frappe.db.get_value("Account", account, "freeze_account") @@ -274,6 +299,9 @@ def update_against_account(voucher_type, voucher_no): if d.against != new_against: frappe.db.set_value("GL Entry", d.name, "against", new_against) +def on_doctype_update(): + frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"]) + frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"]) def rename_gle_sle_docs(): for doctype in ["GL Entry", "Stock Ledger Entry"]: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a68c36846de..847fbed58c8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -125,6 +125,27 @@ class PurchaseInvoice(BuyingController): else: self.remarks = _("No Remarks") + def set_status(self, update=False, status=None, update_modified=True): + if self.is_new(): + if self.get('amended_from'): + self.status = 'Draft' + return + + if not status: + precision = self.precision("outstanding_amount") + args = [ + self.name, + self.outstanding_amount, + self.is_return, + self.due_date, + self.docstatus, + precision + ] + status = get_status(args) + + if update: + self.db_set('status', status, update_modified = update_modified) + def set_missing_values(self, for_validate=False): if not self.credit_to: self.credit_to = get_party_account("Supplier", self.supplier, self.company) @@ -1007,6 +1028,34 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() +def get_status(*args): + purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0] + + outstanding_amount = flt(outstanding_amount, precision) + due_date = getdate(due_date) + now_date = getdate() + + if docstatus == 2: + status = "Cancelled" + elif docstatus == 1: + if outstanding_amount > 0 and due_date < now_date: + status = "Overdue" + elif outstanding_amount > 0 and due_date >= now_date: + status = "Unpaid" + #Check if outstanding amount is 0 due to debit note issued against invoice + elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}): + status = "Debit Note Issued" + elif is_return == 1: + status = "Return" + elif outstanding_amount <=0: + status = "Paid" + else: + status = "Submitted" + else: + status = "Draft" + + return status + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 658e703b4e3..bcaa394b06e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1217,62 +1217,83 @@ class SalesInvoice(SellingController): self.set_missing_values(for_validate = True) - def get_discounting_status(self): - status = None - if self.is_discounted: - invoice_discounting_list = frappe.db.sql(""" - select status - from `tabInvoice Discounting` id, `tabDiscounted Invoice` d - where - id.name = d.parent - and d.sales_invoice=%s - and id.docstatus=1 - and status in ('Disbursed', 'Settled') - """, self.name) - for d in invoice_discounting_list: - status = d[0] - if status == "Disbursed": - break - return status - def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): if self.get('amended_from'): self.status = 'Draft' return - precision = self.precision("outstanding_amount") - outstanding_amount = flt(self.outstanding_amount, precision) - due_date = getdate(self.due_date) - nowdate = getdate() - discountng_status = self.get_discounting_status() - if not status: - if self.docstatus == 2: - status = "Cancelled" - elif self.docstatus == 1: - if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': - self.status = "Overdue and Discounted" - elif outstanding_amount > 0 and due_date < nowdate: - self.status = "Overdue" - elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': - self.status = "Unpaid and Discounted" - elif outstanding_amount > 0 and due_date >= nowdate: - self.status = "Unpaid" - #Check if outstanding amount is 0 due to credit note issued against invoice - elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): - self.status = "Credit Note Issued" - elif self.is_return == 1: - self.status = "Return" - elif outstanding_amount<=0: - self.status = "Paid" - else: - self.status = "Submitted" - else: - self.status = "Draft" + precision = self.precision("outstanding_amount") + args = [ + self.name, + self.outstanding_amount, + self.is_discounted, + self.is_return, + self.due_date, + self.docstatus, + precision, + ] + status = get_status(args) if update: - self.db_set('status', self.status, update_modified = update_modified) + self.db_set('status', status, update_modified = update_modified) + +def get_discounting_status(sales_invoice): + status = None + + invoice_discounting_list = frappe.db.sql(""" + select status + from `tabInvoice Discounting` id, `tabDiscounted Invoice` d + where + id.name = d.parent + and d.sales_invoice=%s + and id.docstatus=1 + and status in ('Disbursed', 'Settled') + """, sales_invoice) + + for d in invoice_discounting_list: + status = d[0] + if status == "Disbursed": + break + + return status + +def get_status(*args): + sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0] + + discounting_status = None + if is_discounted: + discounting_status = get_discounting_status(sales_invoice) + + outstanding_amount = flt(outstanding_amount, precision) + due_date = getdate(due_date) + now_date = getdate() + + if docstatus == 2: + status = "Cancelled" + elif docstatus == 1: + if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed': + status = "Overdue and Discounted" + elif outstanding_amount > 0 and due_date < now_date: + status = "Overdue" + elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed': + status = "Unpaid and Discounted" + elif outstanding_amount > 0 and due_date >= now_date: + status = "Unpaid" + #Check if outstanding amount is 0 due to credit note issued against invoice + elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}): + status = "Credit Note Issued" + elif is_return == 1: + status = "Return" + elif outstanding_amount <=0: + status = "Paid" + else: + status = "Submitted" + else: + status = "Draft" + + return status def validate_inter_company_party(doctype, party, company, inter_company_reference): if not party: @@ -1444,7 +1465,7 @@ def get_inter_company_details(doc, doctype): "party": party, "company": company } - + def get_internal_party(parties, link_doctype, doc): if len(parties) == 1: party = parties[0].name diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index bb1b7e392dc..6d53530321f 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -140,8 +140,11 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False): gle = frappe.get_doc(args) gle.flags.ignore_permissions = 1 gle.flags.from_repost = from_repost - gle.insert() + gle.validate() + gle.flags.ignore_permissions = True + gle.db_insert() gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) + gle.flags.ignore_validate = True gle.submit() def validate_account_for_perpetual_inventory(gl_map): diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 8b275a64fb3..b465a106f0e 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -44,17 +44,6 @@ status_map = { ["Closed", "eval:self.status=='Closed'"], ["On Hold", "eval:self.status=='On Hold'"], ], - "Purchase Invoice": [ - ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], - ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Debit Note Issued", - "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], - ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], - ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], - ["Cancelled", "eval:self.docstatus==2"], - ], "Purchase Order": [ ["Draft", None], ["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"],