From 3225102eb9212a424c7ad78351c51881c1b340bf Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 Aug 2014 11:18:32 +0530 Subject: [PATCH] Update outstanding amt based on party Party mandatory if account is receivable/payable Fetch advance based on party Reconciliation splitting logic based on party --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 17 +++++++++++------ .../doctype/journal_voucher/journal_voucher.py | 14 +++++++++++--- .../purchase_invoice/purchase_invoice.py | 8 ++++---- .../doctype/sales_invoice/sales_invoice.py | 6 ++++-- erpnext/accounts/general_ledger.py | 2 +- erpnext/accounts/utils.py | 3 +++ erpnext/controllers/accounts_controller.py | 13 ++++++++----- 7 files changed, 42 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index a5a30790a59..41a88c1e2f0 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -27,7 +27,7 @@ class GLEntry(Document): # Update outstanding amt on against voucher if self.against_voucher_type in ['Journal Voucher', 'Sales Invoice', 'Purchase Invoice'] \ and self.against_voucher and update_outstanding == 'Yes': - update_outstanding_amt(self.account, self.against_voucher_type, + update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher) def check_mandatory(self): @@ -36,6 +36,10 @@ class GLEntry(Document): if not self.get(k): frappe.throw(_("{0} is required").format(self.meta.get_label(k))) + account_type = frappe.db.get_value("Account", self.account, "account_type") + if account_type in ["Receivable", "Payable"] and not (self.party_type and self.party): + frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format(self.account)) + # Zero value transaction is not allowed if not (flt(self.debit) or flt(self.credit)): frappe.throw(_("Either debit or credit amount is required for {0}").format(self.account)) @@ -109,12 +113,13 @@ def check_freezing_date(posting_date, adv_adj=False): and not frozen_accounts_modifier in frappe.user.get_roles(): frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto))) -def update_outstanding_amt(account, against_voucher_type, against_voucher, on_cancel=False): +def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False): # get final outstanding amt bal = flt(frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) from `tabGL Entry` - where against_voucher_type=%s and against_voucher=%s and account = %s""", - (against_voucher_type, against_voucher, account))[0][0] or 0.0) + where against_voucher_type=%s and against_voucher=%s + and account = %s and party_type=%s and party=%s""", + (against_voucher_type, against_voucher, account, party_type, party))[0][0] or 0.0) if against_voucher_type == 'Purchase Invoice': bal = -bal @@ -122,8 +127,8 @@ def update_outstanding_amt(account, against_voucher_type, against_voucher, on_ca against_voucher_amount = flt(frappe.db.sql(""" select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s - and account = %s and ifnull(against_voucher, '') = ''""", - (against_voucher, account))[0][0]) + and account = %s and party_type=%s and party=%s and ifnull(against_voucher, '') = ''""", + (against_voucher, account, party_type, party))[0][0]) bal = against_voucher_amount + bal if against_voucher_amount < 0: bal = -bal diff --git a/erpnext/accounts/doctype/journal_voucher/journal_voucher.py b/erpnext/accounts/doctype/journal_voucher/journal_voucher.py index 538cdbb2797..df26c358c72 100644 --- a/erpnext/accounts/doctype/journal_voucher/journal_voucher.py +++ b/erpnext/accounts/doctype/journal_voucher/journal_voucher.py @@ -21,7 +21,7 @@ class JournalVoucher(AccountsController): self.clearance_date = None super(JournalVoucher, self).validate_date_with_fiscal_year() - + self.validate_party() self.validate_cheque_info() self.validate_entries_for_advance() self.validate_debit_and_credit() @@ -61,6 +61,13 @@ class JournalVoucher(AccountsController): self.make_gl_entries(1) self.update_advance_paid() + def validate_party(self): + for d in self.get("entries"): + account_type = frappe.db.get_value("Account", d.account, "account_type") + if account_type in ["Receivable", "Payable"] and not (d.party_type and d.party): + frappe.throw(_("Row{0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account)) + + def check_credit_limit(self): customers = list(set([d.party for d in self.get("entries") if d.party_type=="Customer"])) if customers: @@ -247,6 +254,7 @@ class JournalVoucher(AccountsController): for d in self.get('entries'): if d.against_invoice and d.credit: currency = frappe.db.get_value("Sales Invoice", d.against_invoice, "currency") + r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = currency), \ d.against_invoice)) @@ -260,7 +268,7 @@ class JournalVoucher(AccountsController): from `tabPurchase Invoice` where name=%s""", d.against_voucher) if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \ not in ['na', 'not applicable', 'none']: - r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), bill_no[0][2]), bill_no[0][0], + r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=bill_no[0][2]), bill_no[0][0], bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d')))) if d.against_purchase_order and d.debit: @@ -294,7 +302,7 @@ class JournalVoucher(AccountsController): self.pay_to_recd_from = frappe.db.get_value(d.party_type, d.party, "customer_name" if d.party_type=="Customer" else "supplier_name") elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]: - self.total_amount = fmt_money(d.debit or d.credit, currency) + self.total_amount = fmt_money(d.debit or d.credit, currency=currency) self.total_amount_in_words = money_in_words(self.total_amount, currency) def make_gl_entries(self, cancel=0, adv_adj=0): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 8bc3a5b318e..6a612a969bf 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -3,9 +3,7 @@ from __future__ import unicode_literals import frappe - - -from frappe.utils import cint, cstr, formatdate, flt +from frappe.utils import cint, formatdate, flt from frappe import msgprint, _, throw from erpnext.setup.utils import get_company_currency import frappe.defaults @@ -76,7 +74,7 @@ class PurchaseInvoice(BuyingController): super(PurchaseInvoice, self).set_missing_values(for_validate) def get_advances(self): - super(PurchaseInvoice, self).get_advances(self.credit_to, + super(PurchaseInvoice, self).get_advances(self.credit_to, "Supplier", self.supplier, "Purchase Invoice Advance", "advance_allocation_details", "debit", "purchase_order") def check_active_purchase_items(self): @@ -226,6 +224,8 @@ class PurchaseInvoice(BuyingController): 'against_voucher_type' : 'Purchase Invoice', 'against_voucher' : self.name, 'account' : self.credit_to, + 'party_type': 'Supplier', + 'party': self.supplier, 'is_advance' : 'Yes', 'dr_or_cr' : 'debit', 'unadjusted_amt' : flt(d.advance_amount), diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 615e14c2cf7..352dfabb7d4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -202,7 +202,7 @@ class SalesInvoice(SellingController): self.set_taxes("other_charges", "taxes_and_charges") def get_advances(self): - super(SalesInvoice, self).get_advances(self.debit_to, + super(SalesInvoice, self).get_advances(self.debit_to, "Customer", self.customer, "Sales Invoice Advance", "advance_adjustment_details", "credit", "sales_order") def get_company_abbr(self): @@ -225,6 +225,8 @@ class SalesInvoice(SellingController): 'against_voucher_type' : 'Sales Invoice', 'against_voucher' : self.name, 'account' : self.debit_to, + 'party_type': 'Customer', + 'party': self.customer, 'is_advance' : 'Yes', 'dr_or_cr' : 'credit', 'unadjusted_amt' : flt(d.advance_amount), @@ -455,7 +457,7 @@ class SalesInvoice(SellingController): if update_outstanding == "No": from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt - update_outstanding_amt(self.debit_to, self.doctype, self.name) + update_outstanding_amt(self.debit_to, "Customer", self.customer, self.doctype, self.name) if repost_future_gle and cint(self.update_stock) \ and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index cb58abe3eef..38ad73326df 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -122,5 +122,5 @@ def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None, validate_expense_against_budget(entry) if entry.get("against_voucher") and update_outstanding == 'Yes': - update_outstanding_amt(entry["account"], entry.get("against_voucher_type"), + update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"), entry.get("against_voucher"), on_cancel=True) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 64c62af6008..9b8b47af987 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -152,6 +152,7 @@ def check_if_jv_modified(args): ret = frappe.db.sql(""" select t2.{dr_or_cr} from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 where t1.name = t2.parent and t2.account = %(account)s + and t2.party_type = %(party_type)s and t2.party = %(party)s and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' and ifnull(t2.against_jv, '')='' and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s @@ -180,6 +181,8 @@ def update_against_doc(d, jv_obj): # new entry with balance amount ch = jv_obj.append("entries") ch.account = d['account'] + ch.party_type = d["party_type"] + ch.party = d["party"] ch.cost_center = cstr(jvd[0][0]) ch.balance = flt(jvd[0][1]) ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt'])) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 31f5d397ca2..b76efd8b5cf 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -364,6 +364,8 @@ class AccountsController(TransactionBase): 'debit': 0, 'credit': 0, 'is_opening': self.get("is_opening") or "No", + 'party_type': None, + 'party': None }) gl_dict.update(args) return gl_dict @@ -374,7 +376,7 @@ class AccountsController(TransactionBase): frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name)) - def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr, against_order_field): + def get_advances(self, account_head, party_type, party, child_doctype, parentfield, dr_or_cr, against_order_field): so_list = list(set([d.get(against_order_field) for d in self.get("entries") if d.get(against_order_field)])) cond = "" if so_list: @@ -386,17 +388,18 @@ class AccountsController(TransactionBase): from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 where - t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes' and t1.docstatus = 1 + t1.name = t2.parent and t2.account = %s + and t2.party_type=%s and t2.party=%s + and t2.is_advance = 'Yes' and t1.docstatus = 1 and (( ifnull(t2.against_voucher, '') = '' and ifnull(t2.against_invoice, '') = '' and ifnull(t2.against_jv, '') = '' and ifnull(t2.against_sales_order, '') = '' and ifnull(t2.against_purchase_order, '') = '' - ) %s) + ) %s) order by t1.posting_date""" % - (dr_or_cr, '%s', cond), - tuple([account_head] + so_list), as_dict= True) + (dr_or_cr, '%s', '%s', '%s', cond), tuple([account_head, party_type, party] + so_list), as_dict=1) self.set(parentfield, []) for d in res: