Credit Limit and credit days fixes #2031
This commit is contained in:
parent
be8ec39678
commit
e9daefe07f
@ -177,7 +177,7 @@
|
|||||||
"icon": "icon-money",
|
"icon": "icon-money",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"modified": "2014-08-26 18:18:30.173409",
|
"modified": "2014-08-27 15:12:35.506765",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Account",
|
"name": "Account",
|
||||||
@ -241,18 +241,6 @@
|
|||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 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,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
"apply_user_permissions": 0,
|
||||||
@ -269,30 +257,6 @@
|
|||||||
"set_user_permissions": 1,
|
"set_user_permissions": 1,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 1
|
"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"
|
"search_fields": "group_or_ledger"
|
||||||
|
@ -85,8 +85,8 @@ class Account(Document):
|
|||||||
def convert_ledger_to_group(self):
|
def convert_ledger_to_group(self):
|
||||||
if self.check_gle_exists():
|
if self.check_gle_exists():
|
||||||
throw(_("Account with existing transaction can not be converted to group."))
|
throw(_("Account with existing transaction can not be converted to group."))
|
||||||
elif self.master_type or self.account_type:
|
elif self.account_type:
|
||||||
throw(_("Cannot covert to Group because Master Type or Account Type is selected."))
|
throw(_("Cannot covert to Group because Account Type is selected."))
|
||||||
else:
|
else:
|
||||||
self.group_or_ledger = 'Group'
|
self.group_or_ledger = 'Group'
|
||||||
self.save()
|
self.save()
|
||||||
@ -135,47 +135,6 @@ class Account(Document):
|
|||||||
def on_update(self):
|
def on_update(self):
|
||||||
self.update_nsm_model()
|
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):
|
def validate_trash(self):
|
||||||
"""checks gl entries and if child exists"""
|
"""checks gl entries and if child exists"""
|
||||||
if not self.parent_account:
|
if not self.parent_account:
|
||||||
|
@ -3,11 +3,9 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.utils import cstr, flt, fmt_money, formatdate, getdate
|
||||||
from frappe.utils import cint, cstr, flt, fmt_money, formatdate, getdate
|
|
||||||
from frappe import msgprint, _, scrub
|
from frappe import msgprint, _, scrub
|
||||||
from erpnext.setup.utils import get_company_currency
|
from erpnext.setup.utils import get_company_currency
|
||||||
|
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
|
||||||
class JournalVoucher(AccountsController):
|
class JournalVoucher(AccountsController):
|
||||||
@ -40,12 +38,11 @@ class JournalVoucher(AccountsController):
|
|||||||
self.set_print_format_fields()
|
self.set_print_format_fields()
|
||||||
self.validate_against_sales_order()
|
self.validate_against_sales_order()
|
||||||
self.validate_against_purchase_order()
|
self.validate_against_purchase_order()
|
||||||
|
self.check_credit_limit()
|
||||||
|
self.check_credit_days()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if self.voucher_type in ['Bank Voucher', 'Contra Voucher', 'Journal Entry']:
|
|
||||||
self.check_credit_days()
|
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
self.check_credit_limit()
|
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
|
|
||||||
def update_advance_paid(self):
|
def update_advance_paid(self):
|
||||||
@ -68,6 +65,30 @@ class JournalVoucher(AccountsController):
|
|||||||
self.make_gl_entries(1)
|
self.make_gl_entries(1)
|
||||||
self.update_advance_paid()
|
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):
|
def validate_cheque_info(self):
|
||||||
if self.voucher_type in ['Bank Voucher']:
|
if self.voucher_type in ['Bank Voucher']:
|
||||||
if not self.cheque_no or not self.cheque_date:
|
if not self.cheque_no or not self.cheque_date:
|
||||||
@ -155,7 +176,7 @@ class JournalVoucher(AccountsController):
|
|||||||
and voucher_account != d.account:
|
and voucher_account != d.account:
|
||||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
||||||
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||||
|
|
||||||
if against_field in ["against_sales_order", "against_purchase_order"]:
|
if against_field in ["against_sales_order", "against_purchase_order"]:
|
||||||
if voucher_account != account_master_name:
|
if voucher_account != account_master_name:
|
||||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} 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):
|
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
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"])
|
["docstatus", "outstanding_amount"])
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
if voucher_properties[0] != 1:
|
||||||
@ -179,7 +200,7 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
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"])
|
["docstatus", "per_billed", "advance_paid", "grand_total"])
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
if voucher_properties[0] != 1:
|
||||||
@ -300,51 +321,6 @@ class JournalVoucher(AccountsController):
|
|||||||
from frappe.utils import money_in_words
|
from frappe.utils import money_in_words
|
||||||
self.total_amount_in_words = money_in_words(amt, company_currency)
|
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):
|
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|
||||||
@ -372,13 +348,6 @@ class JournalVoucher(AccountsController):
|
|||||||
if gl_map:
|
if gl_map:
|
||||||
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj)
|
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):
|
def get_balance(self):
|
||||||
if not self.get('entries'):
|
if not self.get('entries'):
|
||||||
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
||||||
|
@ -54,7 +54,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_uom_is_integer("uom", "qty")
|
self.validate_uom_is_integer("uom", "qty")
|
||||||
self.set_aging_date()
|
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.set_against_expense_account()
|
||||||
self.validate_write_off_account()
|
self.validate_write_off_account()
|
||||||
self.update_valuation_rate("entries")
|
self.update_valuation_rate("entries")
|
||||||
@ -73,8 +72,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.credit_to:
|
if not self.credit_to:
|
||||||
self.credit_to = get_party_account(self.company, self.supplier, "Supplier")
|
self.credit_to = get_party_account(self.company, self.supplier, "Supplier")
|
||||||
if not self.due_date:
|
if not self.due_date:
|
||||||
self.due_date = get_due_date(self.posting_date, self.supplier, "Supplier",
|
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
|
||||||
self.credit_to, self.company)
|
|
||||||
|
|
||||||
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
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")
|
or tabAccount.account_type = "Expense Account")
|
||||||
and tabAccount.group_or_ledger="Ledger"
|
and tabAccount.group_or_ledger="Ledger"
|
||||||
and tabAccount.docstatus!=2
|
and tabAccount.docstatus!=2
|
||||||
and ifnull(tabAccount.master_type, "")=""
|
|
||||||
and ifnull(tabAccount.master_name, "")=""
|
|
||||||
and tabAccount.company = '%(company)s'
|
and tabAccount.company = '%(company)s'
|
||||||
and tabAccount.%(key)s LIKE '%(txt)s'
|
and tabAccount.%(key)s LIKE '%(txt)s'
|
||||||
%(mcond)s""" % {'company': filters['company'], 'key': searchfield,
|
%(mcond)s""" % {'company': filters['company'], 'key': searchfield,
|
||||||
|
@ -66,9 +66,6 @@ class SalesInvoice(SellingController):
|
|||||||
self.is_opening = 'No'
|
self.is_opening = 'No'
|
||||||
|
|
||||||
self.set_aging_date()
|
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.set_against_income_account()
|
||||||
self.validate_c_form()
|
self.validate_c_form()
|
||||||
self.validate_time_logs_are_submitted()
|
self.validate_time_logs_are_submitted()
|
||||||
@ -91,10 +88,9 @@ class SalesInvoice(SellingController):
|
|||||||
self.update_status_updater_args()
|
self.update_status_updater_args()
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||||
|
self.check_credit_limit()
|
||||||
# this sequence because outstanding may get -ve
|
# this sequence because outstanding may get -ve
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
self.check_credit_limit(self.debit_to)
|
|
||||||
|
|
||||||
if not cint(self.is_pos) == 1:
|
if not cint(self.is_pos) == 1:
|
||||||
self.update_against_document_in_jv()
|
self.update_against_document_in_jv()
|
||||||
@ -149,8 +145,7 @@ class SalesInvoice(SellingController):
|
|||||||
if not self.debit_to:
|
if not self.debit_to:
|
||||||
self.debit_to = get_party_account(self.company, self.customer, "Customer")
|
self.debit_to = get_party_account(self.company, self.customer, "Customer")
|
||||||
if not self.due_date:
|
if not self.due_date:
|
||||||
self.due_date = get_due_date(self.posting_date, self.customer, "Customer",
|
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
|
||||||
self.debit_to, self.company)
|
|
||||||
|
|
||||||
super(SalesInvoice, self).set_missing_values(for_validate)
|
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")
|
or tabAccount.account_type = "Income Account")
|
||||||
and tabAccount.group_or_ledger="Ledger"
|
and tabAccount.group_or_ledger="Ledger"
|
||||||
and tabAccount.docstatus!=2
|
and tabAccount.docstatus!=2
|
||||||
and ifnull(tabAccount.master_type, "")=""
|
|
||||||
and ifnull(tabAccount.master_name, "")=""
|
|
||||||
and tabAccount.company = '%(company)s'
|
and tabAccount.company = '%(company)s'
|
||||||
and tabAccount.%(key)s LIKE '%(txt)s'
|
and tabAccount.%(key)s LIKE '%(txt)s'
|
||||||
%(mcond)s""" % {'company': filters['company'], 'key': searchfield,
|
%(mcond)s""" % {'company': filters['company'], 'key': searchfield,
|
||||||
|
@ -230,7 +230,6 @@ erpnext.AccountsChart = Class.extend({
|
|||||||
|
|
||||||
var node = me.tree.get_selected_node();
|
var node = me.tree.get_selected_node();
|
||||||
v.parent_account = node.label;
|
v.parent_account = node.label;
|
||||||
v.master_type = '';
|
|
||||||
v.company = me.company;
|
v.company = me.company;
|
||||||
|
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _, msgprint
|
||||||
from frappe.defaults import get_user_permissions
|
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.address.address import get_address_display
|
||||||
from erpnext.utilities.doctype.contact.contact import get_contact_details
|
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 = {
|
out = {
|
||||||
party_type.lower(): party,
|
party_type.lower(): party,
|
||||||
account_fieldname : account,
|
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
|
return out
|
||||||
|
|
||||||
@ -138,19 +138,34 @@ def get_party_account(company, party, party_type):
|
|||||||
|
|
||||||
return acc_head
|
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"""
|
"""Set Due Date = Posting Date + Credit Days"""
|
||||||
due_date = None
|
due_date = None
|
||||||
if posting_date:
|
if posting_date:
|
||||||
credit_days = 0
|
credit_days = get_credit_days(party_type, party, company)
|
||||||
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")
|
|
||||||
|
|
||||||
due_date = add_days(posting_date, credit_days) if credit_days else posting_date
|
due_date = add_days(posting_date, credit_days) if credit_days else posting_date
|
||||||
|
|
||||||
return due_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))
|
||||||
|
@ -61,17 +61,9 @@ class Supplier(TransactionBase):
|
|||||||
where supplier=%s""", self.name):
|
where supplier=%s""", self.name):
|
||||||
frappe.delete_doc("Contact", contact)
|
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):
|
def on_trash(self):
|
||||||
self.delete_supplier_address()
|
self.delete_supplier_address()
|
||||||
self.delete_supplier_contact()
|
self.delete_supplier_contact()
|
||||||
self.delete_supplier_account()
|
|
||||||
|
|
||||||
def after_rename(self, olddn, newdn, merge=False):
|
def after_rename(self, olddn, newdn, merge=False):
|
||||||
set_field = ''
|
set_field = ''
|
||||||
|
@ -22,6 +22,7 @@ class AccountsController(TransactionBase):
|
|||||||
self.set_total_in_words()
|
self.set_total_in_words()
|
||||||
|
|
||||||
self.validate_for_freezed_account()
|
self.validate_for_freezed_account()
|
||||||
|
self.validate_due_date()
|
||||||
|
|
||||||
if self.meta.get_field("is_recurring"):
|
if self.meta.get_field("is_recurring"):
|
||||||
validate_recurring_document(self)
|
validate_recurring_document(self)
|
||||||
@ -61,6 +62,13 @@ class AccountsController(TransactionBase):
|
|||||||
validate_fiscal_year(self.get(date_field), self.fiscal_year,
|
validate_fiscal_year(self.get(date_field), self.fiscal_year,
|
||||||
label=self.meta.get_label(date_field))
|
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):
|
def validate_for_freezed_account(self):
|
||||||
for fieldname in ["customer", "supplier"]:
|
for fieldname in ["customer", "supplier"]:
|
||||||
if self.meta.get_field(fieldname) and self.get(fieldname):
|
if self.meta.get_field(fieldname) and self.get(fieldname):
|
||||||
@ -515,15 +523,6 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
return self._abbr
|
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()
|
@frappe.whitelist()
|
||||||
def get_tax_rate(account_head):
|
def get_tax_rate(account_head):
|
||||||
return frappe.db.get_value("Account", account_head, "tax_rate")
|
return frappe.db.get_value("Account", account_head, "tax_rate")
|
||||||
|
@ -33,6 +33,10 @@ class SellingController(StockController):
|
|||||||
self.validate_max_discount()
|
self.validate_max_discount()
|
||||||
check_active_sales_items(self)
|
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):
|
def set_missing_values(self, for_validate=False):
|
||||||
super(SellingController, self).set_missing_values(for_validate)
|
super(SellingController, self).set_missing_values(for_validate)
|
||||||
|
|
||||||
@ -301,28 +305,6 @@ class SellingController(StockController):
|
|||||||
elif self.order_type not in valid_types:
|
elif self.order_type not in valid_types:
|
||||||
throw(_("Order Type must be one of {0}").format(comma_or(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):
|
def validate_max_discount(self):
|
||||||
for d in self.get(self.fname):
|
for d in self.get(self.fname):
|
||||||
discount = flt(frappe.db.get_value("Item", d.item_code, "max_discount"))
|
discount = flt(frappe.db.get_value("Item", d.item_code, "max_discount"))
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from frappe import _
|
from frappe import _, msgprint, throw
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
from frappe.utils import flt
|
||||||
|
|
||||||
|
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
@ -92,17 +93,9 @@ class Customer(TransactionBase):
|
|||||||
where customer=%s""", self.name):
|
where customer=%s""", self.name):
|
||||||
frappe.delete_doc("Contact", contact)
|
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):
|
def on_trash(self):
|
||||||
self.delete_customer_address()
|
self.delete_customer_address()
|
||||||
self.delete_customer_contact()
|
self.delete_customer_contact()
|
||||||
self.delete_customer_account()
|
|
||||||
if self.lead_name:
|
if self.lead_name:
|
||||||
frappe.db.sql("update `tabLead` set status='Interested' where name=%s",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""" %
|
name, customer_name limit %s, %s""" %
|
||||||
(", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"),
|
(", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"),
|
||||||
("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len))
|
("%%%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
|
||||||
|
@ -153,10 +153,9 @@ class SalesOrder(SellingController):
|
|||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
super(SalesOrder, self).on_submit()
|
super(SalesOrder, self).on_submit()
|
||||||
|
|
||||||
|
self.check_credit_limit()
|
||||||
self.update_stock_ledger(update_stock = 1)
|
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)
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.grand_total, self)
|
||||||
|
|
||||||
self.update_prevdoc_status('submit')
|
self.update_prevdoc_status('submit')
|
||||||
@ -357,17 +356,6 @@ def make_sales_invoice(source_name, target_doc=None):
|
|||||||
}
|
}
|
||||||
}, target_doc, postprocess)
|
}, 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
|
return doclist
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
@ -94,7 +94,6 @@ class Company(Document):
|
|||||||
account = frappe.get_doc({
|
account = frappe.get_doc({
|
||||||
"doctype": "Account",
|
"doctype": "Account",
|
||||||
"freeze_account": "No",
|
"freeze_account": "No",
|
||||||
"master_type": "",
|
|
||||||
"company": self.name
|
"company": self.name
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -73,7 +73,6 @@ def install(company):
|
|||||||
# account = frappe.get_doc({
|
# account = frappe.get_doc({
|
||||||
# "doctype": "Account",
|
# "doctype": "Account",
|
||||||
# "freeze_account": "No",
|
# "freeze_account": "No",
|
||||||
# "master_type": "",
|
|
||||||
# "company": company.name
|
# "company": company.name
|
||||||
# })
|
# })
|
||||||
#
|
#
|
||||||
|
@ -174,11 +174,11 @@ class DeliveryNote(SellingController):
|
|||||||
# update delivered qty in sales order
|
# update delivered qty in sales order
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
|
self.check_credit_limit()
|
||||||
|
|
||||||
# create stock ledger entry
|
# create stock ledger entry
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
self.credit_limit()
|
|
||||||
|
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
# set DN status
|
# set DN status
|
||||||
@ -271,16 +271,6 @@ class DeliveryNote(SellingController):
|
|||||||
}
|
}
|
||||||
update_bin(args)
|
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):
|
def get_invoiced_qty_map(delivery_note):
|
||||||
"""returns a map: {dn_detail: invoiced_qty}"""
|
"""returns a map: {dn_detail: invoiced_qty}"""
|
||||||
invoiced_qty_map = {}
|
invoiced_qty_map = {}
|
||||||
|
Loading…
Reference in New Issue
Block a user