Credit Limit and credit days fixes #2031

This commit is contained in:
Nabin Hait 2014-08-27 16:46:33 +05:30
parent be8ec39678
commit e9daefe07f
15 changed files with 138 additions and 243 deletions

View File

@ -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"

View File

@ -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:

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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({

View File

@ -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))

View File

@ -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 = ''

View File

@ -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")

View File

@ -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"))

View File

@ -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

View File

@ -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()

View File

@ -94,7 +94,6 @@ class Company(Document):
account = frappe.get_doc({
"doctype": "Account",
"freeze_account": "No",
"master_type": "",
"company": self.name
})

View File

@ -73,7 +73,6 @@ def install(company):
# account = frappe.get_doc({
# "doctype": "Account",
# "freeze_account": "No",
# "master_type": "",
# "company": company.name
# })
#

View File

@ -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 = {}