From e9daefe07fc12de3a8385dc19ec4106289323ffd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 27 Aug 2014 16:46:33 +0530 Subject: [PATCH] Credit Limit and credit days fixes #2031 --- erpnext/accounts/doctype/account/account.json | 38 +------- erpnext/accounts/doctype/account/account.py | 45 +-------- .../journal_voucher/journal_voucher.py | 91 ++++++------------- .../purchase_invoice/purchase_invoice.py | 6 +- .../doctype/sales_invoice/sales_invoice.py | 11 +-- .../page/accounts_browser/accounts_browser.js | 1 - erpnext/accounts/party.py | 39 +++++--- erpnext/buying/doctype/supplier/supplier.py | 8 -- erpnext/controllers/accounts_controller.py | 17 ++-- erpnext/controllers/selling_controller.py | 26 +----- erpnext/selling/doctype/customer/customer.py | 69 ++++++++++++-- .../doctype/sales_order/sales_order.py | 14 +-- erpnext/setup/doctype/company/company.py | 1 - .../company/fixtures/india/__init__.py | 1 - .../doctype/delivery_note/delivery_note.py | 14 +-- 15 files changed, 138 insertions(+), 243 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index 62dde1571e..66958af41b 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -177,7 +177,7 @@ "icon": "icon-money", "idx": 1, "in_create": 1, - "modified": "2014-08-26 18:18:30.173409", + "modified": "2014-08-27 15:12:35.506765", "modified_by": "Administrator", "module": "Accounts", "name": "Account", @@ -241,18 +241,6 @@ "submit": 0, "write": 0 }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "permlevel": 2, - "read": 1, - "report": 1, - "role": "Auditor", - "submit": 0, - "write": 0 - }, { "amend": 0, "apply_user_permissions": 0, @@ -269,30 +257,6 @@ "set_user_permissions": 1, "submit": 0, "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "permlevel": 2, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "permlevel": 2, - "read": 1, - "report": 1, - "role": "Accounts User", - "submit": 0, - "write": 0 } ], "search_fields": "group_or_ledger" diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 1e03568078..c4a59a7a27 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -85,8 +85,8 @@ class Account(Document): def convert_ledger_to_group(self): if self.check_gle_exists(): throw(_("Account with existing transaction can not be converted to group.")) - elif self.master_type or self.account_type: - throw(_("Cannot covert to Group because Master Type or Account Type is selected.")) + elif self.account_type: + throw(_("Cannot covert to Group because Account Type is selected.")) else: self.group_or_ledger = 'Group' self.save() @@ -135,47 +135,6 @@ class Account(Document): def on_update(self): self.update_nsm_model() - def get_authorized_user(self): - # Check logged-in user is authorized - if frappe.db.get_value('Accounts Settings', None, 'credit_controller') \ - in frappe.user.get_roles(): - return 1 - - def check_credit_limit(self, total_outstanding): - # Get credit limit - credit_limit_from = 'Customer' - - cr_limit = frappe.db.sql("""select t1.credit_limit from tabCustomer t1, `tabAccount` t2 - where t2.name=%s and t1.name = t2.master_name""", self.name) - credit_limit = cr_limit and flt(cr_limit[0][0]) or 0 - if not credit_limit: - credit_limit = frappe.db.get_value('Company', self.company, 'credit_limit') - credit_limit_from = 'Company' - - # If outstanding greater than credit limit and not authorized person raise exception - if credit_limit > 0 and flt(total_outstanding) > credit_limit \ - and not self.get_authorized_user(): - throw(_("{0} Credit limit {1} crossed").format(_(credit_limit_from), credit_limit)) - - def validate_due_date(self, posting_date, due_date): - credit_days = (self.credit_days or frappe.db.get_value("Company", self.company, "credit_days")) - posting_date, due_date = getdate(posting_date), getdate(due_date) - diff = (due_date - posting_date).days - - if diff < 0: - frappe.throw(_("Due Date cannot be before Posting Date")) - - elif credit_days is not None and diff > credit_days: - is_credit_controller = frappe.db.get_value("Accounts Settings", None, - "credit_controller") in frappe.user.get_roles() - - if is_credit_controller: - msgprint(_("Note: Due Date exceeds the allowed credit days by {0} day(s)").format( - diff - credit_days)) - else: - max_due_date = formatdate(add_days(posting_date, credit_days)) - frappe.throw(_("Due Date cannot be after {0}").format(max_due_date)) - def validate_trash(self): """checks gl entries and if child exists""" if not self.parent_account: diff --git a/erpnext/accounts/doctype/journal_voucher/journal_voucher.py b/erpnext/accounts/doctype/journal_voucher/journal_voucher.py index 9f2385b9ae..a8365aa4b3 100644 --- a/erpnext/accounts/doctype/journal_voucher/journal_voucher.py +++ b/erpnext/accounts/doctype/journal_voucher/journal_voucher.py @@ -3,11 +3,9 @@ from __future__ import unicode_literals import frappe - -from frappe.utils import cint, cstr, flt, fmt_money, formatdate, getdate +from frappe.utils import cstr, flt, fmt_money, formatdate, getdate from frappe import msgprint, _, scrub from erpnext.setup.utils import get_company_currency - from erpnext.controllers.accounts_controller import AccountsController class JournalVoucher(AccountsController): @@ -40,12 +38,11 @@ class JournalVoucher(AccountsController): self.set_print_format_fields() self.validate_against_sales_order() self.validate_against_purchase_order() + self.check_credit_limit() + self.check_credit_days() def on_submit(self): - if self.voucher_type in ['Bank Voucher', 'Contra Voucher', 'Journal Entry']: - self.check_credit_days() self.make_gl_entries() - self.check_credit_limit() self.update_advance_paid() def update_advance_paid(self): @@ -68,6 +65,30 @@ class JournalVoucher(AccountsController): self.make_gl_entries(1) self.update_advance_paid() + def check_credit_limit(self): + customers = list(set([d.party for d in self.get("entries") if d.party_type=="Customer"])) + if customers: + from erpnext.selling.doctype.customer.customer import check_credit_limit + for customer in customers: + check_credit_limit(customer, self.company) + + def check_credit_days(self): + posting_date = self.posting_date + company_credit_days = frappe.db.get_value("Company", self.company, "credit_days") + if self.cheque_date: + for d in self.get("entries"): + if d.party_type and d.party and d.get("credit" if d.party_type=="Customer" else "debit") > 0: + if d.against_invoice: + posting_date = frappe.db.get_value("Sales Invoice", d.against_invoice, "posting_date") + elif d.against_voucher: + posting_date = frappe.db.get_value("Purchase Invoice", d.against_voucher, "posting_date") + + credit_days = frappe.db.get_value(d.party_type, d.party, "credit_days") or company_credit_days + if credit_days: + if (getdate(self.cheque_date) - getdate(posting_date)).days > flt(credit_days): + msgprint(_("Note: Reference Date is after allowed credit days {0} for {1} {2}") + .format(credit_days, d.party_type, d.party)) + def validate_cheque_info(self): if self.voucher_type in ['Bank Voucher']: if not self.cheque_no or not self.cheque_date: @@ -155,7 +176,7 @@ class JournalVoucher(AccountsController): and voucher_account != d.account: frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \ .format(d.idx, d.account, doctype, field_dict.get(doctype))) - + if against_field in ["against_sales_order", "against_purchase_order"]: if voucher_account != account_master_name: frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} Name") \ @@ -167,7 +188,7 @@ class JournalVoucher(AccountsController): def validate_against_invoice_fields(self, doctype, payment_against_voucher): for voucher_no, payment_list in payment_against_voucher.items(): - voucher_properties = frappe.db.get_value(doctype, voucher_no, + voucher_properties = frappe.db.get_value(doctype, voucher_no, ["docstatus", "outstanding_amount"]) if voucher_properties[0] != 1: @@ -179,7 +200,7 @@ class JournalVoucher(AccountsController): def validate_against_order_fields(self, doctype, payment_against_voucher): for voucher_no, payment_list in payment_against_voucher.items(): - voucher_properties = frappe.db.get_value(doctype, voucher_no, + voucher_properties = frappe.db.get_value(doctype, voucher_no, ["docstatus", "per_billed", "advance_paid", "grand_total"]) if voucher_properties[0] != 1: @@ -300,51 +321,6 @@ class JournalVoucher(AccountsController): from frappe.utils import money_in_words self.total_amount_in_words = money_in_words(amt, company_currency) - def check_credit_days(self): - date_diff = 0 - if self.cheque_date: - date_diff = (getdate(self.cheque_date)-getdate(self.posting_date)).days - - if date_diff <= 0: return - - # Get List of Customer Account - acc_list = filter(lambda d: frappe.db.get_value("Account", d.account, - "master_type")=='Customer', self.get('entries')) - - for d in acc_list: - credit_days = self.get_credit_days_for(d.account) - # Check credit days - if credit_days > 0 and not self.get_authorized_user() and cint(date_diff) > credit_days: - msgprint(_("Maximum allowed credit is {0} days after posting date").format(credit_days), - raise_exception=1) - - def get_credit_days_for(self, ac): - if not self.credit_days_for.has_key(ac): - self.credit_days_for[ac] = cint(frappe.db.get_value("Account", ac, "credit_days")) - - if not self.credit_days_for[ac]: - if self.credit_days_global==-1: - self.credit_days_global = cint(frappe.db.get_value("Company", - self.company, "credit_days")) - - return self.credit_days_global - else: - return self.credit_days_for[ac] - - def get_authorized_user(self): - if self.is_approving_authority==-1: - self.is_approving_authority = 0 - - # Fetch credit controller role - approving_authority = frappe.db.get_value("Accounts Settings", None, - "credit_controller") - - # Check logged-in user is authorized - if approving_authority in frappe.user.get_roles(): - self.is_approving_authority = 1 - - return self.is_approving_authority - def make_gl_entries(self, cancel=0, adv_adj=0): from erpnext.accounts.general_ledger import make_gl_entries @@ -372,13 +348,6 @@ class JournalVoucher(AccountsController): if gl_map: make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj) - def check_credit_limit(self): - for d in self.get("entries"): - master_type, master_name = frappe.db.get_value("Account", d.account, - ["master_type", "master_name"]) - if master_type == "Customer" and master_name: - super(JournalVoucher, self).check_credit_limit(d.account) - def get_balance(self): if not self.get('entries'): msgprint(_("'Entries' cannot be empty"), raise_exception=True) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 6c7e87f048..b34d07fa36 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -54,7 +54,6 @@ class PurchaseInvoice(BuyingController): self.validate_with_previous_doc() self.validate_uom_is_integer("uom", "qty") self.set_aging_date() - frappe.get_doc("Account", self.credit_to).validate_due_date(self.posting_date, self.due_date) self.set_against_expense_account() self.validate_write_off_account() self.update_valuation_rate("entries") @@ -73,8 +72,7 @@ class PurchaseInvoice(BuyingController): if not self.credit_to: self.credit_to = get_party_account(self.company, self.supplier, "Supplier") if not self.due_date: - self.due_date = get_due_date(self.posting_date, self.supplier, "Supplier", - self.credit_to, self.company) + self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company) super(PurchaseInvoice, self).set_missing_values(for_validate) @@ -408,8 +406,6 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): or tabAccount.account_type = "Expense Account") and tabAccount.group_or_ledger="Ledger" and tabAccount.docstatus!=2 - and ifnull(tabAccount.master_type, "")="" - and ifnull(tabAccount.master_name, "")="" and tabAccount.company = '%(company)s' and tabAccount.%(key)s LIKE '%(txt)s' %(mcond)s""" % {'company': filters['company'], 'key': searchfield, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a2bf78c449..dbb2e20d38 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -66,9 +66,6 @@ class SalesInvoice(SellingController): self.is_opening = 'No' self.set_aging_date() - - frappe.get_doc("Account", self.debit_to).validate_due_date(self.posting_date, self.due_date) - self.set_against_income_account() self.validate_c_form() self.validate_time_logs_are_submitted() @@ -91,10 +88,9 @@ class SalesInvoice(SellingController): self.update_status_updater_args() self.update_prevdoc_status() self.update_billing_status_for_zero_amount_refdoc("Sales Order") - + self.check_credit_limit() # this sequence because outstanding may get -ve self.make_gl_entries() - self.check_credit_limit(self.debit_to) if not cint(self.is_pos) == 1: self.update_against_document_in_jv() @@ -149,8 +145,7 @@ class SalesInvoice(SellingController): if not self.debit_to: self.debit_to = get_party_account(self.company, self.customer, "Customer") if not self.due_date: - self.due_date = get_due_date(self.posting_date, self.customer, "Customer", - self.debit_to, self.company) + self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company) super(SalesInvoice, self).set_missing_values(for_validate) @@ -603,8 +598,6 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters): or tabAccount.account_type = "Income Account") and tabAccount.group_or_ledger="Ledger" and tabAccount.docstatus!=2 - and ifnull(tabAccount.master_type, "")="" - and ifnull(tabAccount.master_name, "")="" and tabAccount.company = '%(company)s' and tabAccount.%(key)s LIKE '%(txt)s' %(mcond)s""" % {'company': filters['company'], 'key': searchfield, diff --git a/erpnext/accounts/page/accounts_browser/accounts_browser.js b/erpnext/accounts/page/accounts_browser/accounts_browser.js index 8802093f44..c927596d51 100644 --- a/erpnext/accounts/page/accounts_browser/accounts_browser.js +++ b/erpnext/accounts/page/accounts_browser/accounts_browser.js @@ -230,7 +230,6 @@ erpnext.AccountsChart = Class.extend({ var node = me.tree.get_selected_node(); v.parent_account = node.label; - v.master_type = ''; v.company = me.company; return frappe.call({ diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index bac13794d5..c423ea36ca 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -4,9 +4,9 @@ from __future__ import unicode_literals import frappe -from frappe import _ +from frappe import _, msgprint from frappe.defaults import get_user_permissions -from frappe.utils import add_days +from frappe.utils import add_days, getdate, formatdate, flt from erpnext.utilities.doctype.address.address import get_address_display from erpnext.utilities.doctype.contact.contact import get_contact_details @@ -124,7 +124,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date, out = { party_type.lower(): party, account_fieldname : account, - "due_date": get_due_date(posting_date, party, party_type, account, company) + "due_date": get_due_date(posting_date, party_type, party, company) } return out @@ -138,19 +138,34 @@ def get_party_account(company, party, party_type): return acc_head -def get_due_date(posting_date, party, party_type, account, company): +def get_due_date(posting_date, party_type, party, company): """Set Due Date = Posting Date + Credit Days""" due_date = None if posting_date: - credit_days = 0 - if account: - credit_days = frappe.db.get_value("Account", account, "credit_days") - if party and not credit_days: - credit_days = frappe.db.get_value(party_type, party, "credit_days") - if company and not credit_days: - credit_days = frappe.db.get_value("Company", company, "credit_days") - + credit_days = get_credit_days(party_type, party, company) due_date = add_days(posting_date, credit_days) if credit_days else posting_date return due_date +def get_credit_days(self, party_type, party, company): + return frappe.db.get_value(party_type, party, "credit_days") or \ + frappe.db.get_value("Company", company, "credit_days") if company else 0 + +def validate_due_date(posting_date, due_date, party_type, party, company): + credit_days = get_credit_days(party_type, party, company) + + posting_date, due_date = getdate(posting_date), getdate(due_date) + diff = (due_date - posting_date).days + + if diff < 0: + frappe.throw(_("Due Date cannot be before Posting Date")) + elif credit_days is not None and diff > flt(credit_days): + is_credit_controller = frappe.db.get_value("Accounts Settings", None, + "credit_controller") in frappe.user.get_roles() + + if is_credit_controller: + msgprint(_("Note: Due / Reference Date exceeds the allowed credit days by {0} day(s)").format( + diff - flt(credit_days))) + else: + max_due_date = formatdate(add_days(posting_date, credit_days)) + frappe.throw(_("Due / Reference Date cannot be after {0}").format(max_due_date)) diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index 1f3ad2b567..02983bd37b 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -61,17 +61,9 @@ class Supplier(TransactionBase): where supplier=%s""", self.name): frappe.delete_doc("Contact", contact) - def delete_supplier_account(self): - """delete supplier's ledger if exist and check balance before deletion""" - acc = frappe.db.sql("select name from `tabAccount` where master_type = 'Supplier' \ - and master_name = %s and docstatus < 2", self.name) - if acc: - frappe.delete_doc('Account', acc[0][0]) - def on_trash(self): self.delete_supplier_address() self.delete_supplier_contact() - self.delete_supplier_account() def after_rename(self, olddn, newdn, merge=False): set_field = '' diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3a4f3975e9..935adb044a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -22,6 +22,7 @@ class AccountsController(TransactionBase): self.set_total_in_words() self.validate_for_freezed_account() + self.validate_due_date() if self.meta.get_field("is_recurring"): validate_recurring_document(self) @@ -61,6 +62,13 @@ class AccountsController(TransactionBase): validate_fiscal_year(self.get(date_field), self.fiscal_year, label=self.meta.get_label(date_field)) + def validate_due_date(self): + from erpnext.accounts.party import validate_due_date + if self.doctype == "Sales Invoice": + validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company) + elif self.doctype == "Purchase Invoice": + validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company) + def validate_for_freezed_account(self): for fieldname in ["customer", "supplier"]: if self.meta.get_field(fieldname) and self.get(fieldname): @@ -515,15 +523,6 @@ class AccountsController(TransactionBase): return self._abbr - def check_credit_limit(self, account): - total_outstanding = frappe.db.sql(""" - select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) - from `tabGL Entry` where account = %s""", account) - - total_outstanding = total_outstanding[0][0] if total_outstanding else 0 - if total_outstanding: - frappe.get_doc('Account', account).check_credit_limit(total_outstanding) - @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, "tax_rate") diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 9e237e1aed..d173abeb8e 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -33,6 +33,10 @@ class SellingController(StockController): self.validate_max_discount() check_active_sales_items(self) + def check_credit_limit(self): + from erpnext.selling.doctype.customer.customer import check_credit_limit + check_credit_limit(self.customer, self.company) + def set_missing_values(self, for_validate=False): super(SellingController, self).set_missing_values(for_validate) @@ -301,28 +305,6 @@ class SellingController(StockController): elif self.order_type not in valid_types: throw(_("Order Type must be one of {0}").format(comma_or(valid_types))) - def check_credit(self, grand_total): - customer_account = frappe.db.get_value("Account", {"company": self.company, - "master_name": self.customer}, "name") - if customer_account: - invoice_outstanding = frappe.db.sql("""select - sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) - from `tabGL Entry` where account = %s""", customer_account) - invoice_outstanding = flt(invoice_outstanding[0][0]) if invoice_outstanding else 0 - - ordered_amount_to_be_billed = frappe.db.sql(""" - select sum(grand_total*(100 - ifnull(per_billed, 0))/100) - from `tabSales Order` - where customer=%s and docstatus = 1 - and ifnull(per_billed, 0) < 100 and status != 'Stopped'""", self.customer) - - ordered_amount_to_be_billed = flt(ordered_amount_to_be_billed[0][0]) \ - if ordered_amount_to_be_billed else 0.0 - - total_outstanding = invoice_outstanding + ordered_amount_to_be_billed - - frappe.get_doc('Account', customer_account).check_credit_limit(total_outstanding) - def validate_max_discount(self): for d in self.get(self.fname): discount = flt(frappe.db.get_value("Item", d.item_code, "max_discount")) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 76d78cf5e1..c6d0aaaa2b 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -4,8 +4,9 @@ from __future__ import unicode_literals import frappe from frappe.model.naming import make_autoname -from frappe import _ +from frappe import _, msgprint, throw import frappe.defaults +from frappe.utils import flt from erpnext.utilities.transaction_base import TransactionBase @@ -92,17 +93,9 @@ class Customer(TransactionBase): where customer=%s""", self.name): frappe.delete_doc("Contact", contact) - def delete_customer_account(self): - """delete customer's ledger if exist and check balance before deletion""" - acc = frappe.db.sql("select name from `tabAccount` where master_type = 'Customer' \ - and master_name = %s and docstatus < 2", self.name) - if acc: - frappe.delete_doc('Account', acc[0][0]) - def on_trash(self): self.delete_customer_address() self.delete_customer_contact() - self.delete_customer_account() if self.lead_name: frappe.db.sql("update `tabLead` set status='Interested' where name=%s",self.lead_name) @@ -154,3 +147,61 @@ def get_customer_list(doctype, txt, searchfield, start, page_len, filters): name, customer_name limit %s, %s""" % (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) + + +def check_credit_limit(customer, company): + customer_outstanding = get_customer_outstanding(customer, company) + + credit_limit = frappe.db.get_value("Customer", customer, "credit_limit") or \ + frappe.db.get_value('Company', company, 'credit_limit') + + if credit_limit > 0 and flt(customer_outstanding) > credit_limit: + msgprint(_("Credit limit has been crossed for customer {0} {1}/{2}") + .format(customer, customer_outstanding, credit_limit)) + + # If not authorized person raise exception + credit_controller = frappe.db.get_value('Accounts Settings', None, 'credit_controller') + if not credit_controller or credit_controller not in frappe.user.get_roles(): + throw(_("Please contact to the user who have Sales Master Manager {0} role") + .format(" / " + credit_controller if credit_controller else "")) + +def get_customer_outstanding(customer, company): + # Outstanding based on GL Entries + outstanding_based_on_gle = frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) + from `tabGL Entry` where party_type = 'Customer' and party = %s and company=%s""", (customer, company)) + + outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0 + + # Outstanding based on Sales Order + outstanding_based_on_so = frappe.db.sql(""" + select sum(grand_total*(100 - ifnull(per_billed, 0))/100) + from `tabSales Order` + where customer=%s and docstatus = 1 and company=%s + and ifnull(per_billed, 0) < 100 and status != 'Stopped'""", (customer, company)) + + outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0 + + # Outstanding based on Delivery Note + outstanding_based_on_dn = frappe.db.sql(""" + select + sum( + ( + (ifnull(dn_item.amount) - (select sum(ifnull(amount, 0)) + from `tabSales Invoice Item` + where ifnull(dn_detail, '') = dn_item.name and docstatus = 1) + )/dn.net_total + )*dn.grand_total + ) + from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item + where + dn.name = dn_item.parent and dn.customer=%s and dn.company=%s + and dn.docstatus = 1 and dn.status != 'Stopped' + and ifnull(dn_item.against_sales_order, '') = '' + and ifnull(dn_item.against_sales_invoice, '') = '' + and ifnull(dn_item.amount) > (select sum(ifnull(amount, 0)) + from `tabSales Invoice Item` + where ifnull(dn_detail, '') = dn_item.name and docstatus = 1)""", (customer, company)) + + outstanding_based_on_dn = flt(outstanding_based_on_dn[0][0]) if outstanding_based_on_dn else 0.0 + + return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index bffa581639..b24d297b65 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -153,10 +153,9 @@ class SalesOrder(SellingController): def on_submit(self): super(SalesOrder, self).on_submit() + self.check_credit_limit() self.update_stock_ledger(update_stock = 1) - self.check_credit(self.grand_total) - frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.grand_total, self) self.update_prevdoc_status('submit') @@ -357,17 +356,6 @@ def make_sales_invoice(source_name, target_doc=None): } }, target_doc, postprocess) - def set_advance_vouchers(source, target): - advance_voucher_list = [] - - advance_voucher = frappe.db.sql(""" - select - t1.name as voucher_no, t1.posting_date, t1.remark, t2.account, - t2.name as voucher_detail_no, {amount_query} as payment_amount, t2.is_advance - from - `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 - """) - return doclist @frappe.whitelist() diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 3ac0582f88..c3149dfae5 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -94,7 +94,6 @@ class Company(Document): account = frappe.get_doc({ "doctype": "Account", "freeze_account": "No", - "master_type": "", "company": self.name }) diff --git a/erpnext/setup/doctype/company/fixtures/india/__init__.py b/erpnext/setup/doctype/company/fixtures/india/__init__.py index 5346d74e52..3e7e973f19 100644 --- a/erpnext/setup/doctype/company/fixtures/india/__init__.py +++ b/erpnext/setup/doctype/company/fixtures/india/__init__.py @@ -73,7 +73,6 @@ def install(company): # account = frappe.get_doc({ # "doctype": "Account", # "freeze_account": "No", - # "master_type": "", # "company": company.name # }) # diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 864329d3ea..ee8dc0cce5 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -174,11 +174,11 @@ class DeliveryNote(SellingController): # update delivered qty in sales order self.update_prevdoc_status() + self.check_credit_limit() + # create stock ledger entry self.update_stock_ledger() - self.credit_limit() - self.make_gl_entries() # set DN status @@ -271,16 +271,6 @@ class DeliveryNote(SellingController): } update_bin(args) - def credit_limit(self): - """check credit limit of items in DN Detail which are not fetched from sales order""" - amount, total = 0, 0 - for d in self.get('delivery_note_details'): - if not (d.against_sales_order or d.against_sales_invoice): - amount += d.base_amount - if amount != 0: - total = (amount/self.net_total)*self.grand_total - self.check_credit(total) - def get_invoiced_qty_map(delivery_note): """returns a map: {dn_detail: invoiced_qty}""" invoiced_qty_map = {}