Calculate due date in Purchase Invoice on the basis of Supplier invoice date (#12913)
* pass bill_date as parameter and calculate due_date on that basis * calculate payment_schedule on the basis of bill_date if present else posting_date * add bill_date as an argument to get_party_details * pass bill_date on supplier trigger in purchase invoice
This commit is contained in:
@@ -103,10 +103,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
|
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
|
||||||
{
|
{
|
||||||
posting_date: this.frm.doc.posting_date,
|
posting_date: this.frm.doc.posting_date,
|
||||||
|
bill_date: this.frm.doc.bill_date,
|
||||||
party: this.frm.doc.supplier,
|
party: this.frm.doc.supplier,
|
||||||
party_type: "Supplier",
|
party_type: "Supplier",
|
||||||
account: this.frm.doc.credit_to,
|
account: this.frm.doc.credit_to,
|
||||||
price_list: this.frm.doc.buying_price_list,
|
price_list: this.frm.doc.buying_price_list
|
||||||
}, function() {
|
}, function() {
|
||||||
me.apply_pricing_rule();
|
me.apply_pricing_rule();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.credit_to:
|
if not self.credit_to:
|
||||||
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
||||||
if not self.due_date:
|
if not self.due_date:
|
||||||
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
|
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company, self.bill_date)
|
||||||
|
|
||||||
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
||||||
|
|
||||||
|
|||||||
@@ -23,22 +23,19 @@ class DuplicatePartyAccountError(frappe.ValidationError): pass
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_details(party=None, account=None, party_type="Customer", company=None,
|
def get_party_details(party=None, account=None, party_type="Customer", company=None,
|
||||||
posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
|
posting_date=None, bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
|
||||||
|
|
||||||
if not party:
|
if not party:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if not frappe.db.exists(party_type, party):
|
if not frappe.db.exists(party_type, party):
|
||||||
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
||||||
|
|
||||||
return _get_party_details(party, account, party_type,
|
return _get_party_details(party, account, party_type,
|
||||||
company, posting_date, price_list, currency, doctype, ignore_permissions)
|
company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions)
|
||||||
|
|
||||||
def _get_party_details(party=None, account=None, party_type="Customer", company=None,
|
def _get_party_details(party=None, account=None, party_type="Customer", company=None,
|
||||||
posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
|
posting_date=None, bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
|
||||||
|
|
||||||
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, doctype))
|
|
||||||
|
|
||||||
|
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
||||||
party = out[party_type.lower()]
|
party = out[party_type.lower()]
|
||||||
|
|
||||||
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
||||||
@@ -150,7 +147,7 @@ def set_price_list(out, party, party_type, given_price_list):
|
|||||||
out["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
out["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
||||||
|
|
||||||
|
|
||||||
def set_account_and_due_date(party, account, party_type, company, posting_date, doctype):
|
def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
|
||||||
if doctype not in ["Sales Invoice", "Purchase Invoice"]:
|
if doctype not in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
# not an invoice
|
# not an invoice
|
||||||
return {
|
return {
|
||||||
@@ -161,12 +158,12 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
|
|||||||
account = get_party_account(party_type, party, company)
|
account = get_party_account(party_type, party, company)
|
||||||
|
|
||||||
account_fieldname = "debit_to" if party_type=="Customer" else "credit_to"
|
account_fieldname = "debit_to" if party_type=="Customer" else "credit_to"
|
||||||
|
|
||||||
out = {
|
out = {
|
||||||
party_type.lower(): party,
|
party_type.lower(): party,
|
||||||
account_fieldname : account,
|
account_fieldname : account,
|
||||||
"due_date": get_due_date(posting_date, party_type, party, company)
|
"due_date": get_due_date(posting_date, party_type, party, company, bill_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -268,32 +265,34 @@ def validate_party_accounts(doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_due_date(posting_date, party_type, party, company=None):
|
def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
|
||||||
"""Get due date from `Payment Terms Template`"""
|
"""Get due date from `Payment Terms Template`"""
|
||||||
due_date = None
|
due_date = None
|
||||||
if posting_date and party:
|
if (bill_date or posting_date) and party:
|
||||||
due_date = posting_date
|
due_date = bill_date or posting_date
|
||||||
template_name = get_pyt_term_template(party, party_type, company)
|
template_name = get_pyt_term_template(party, party_type, company)
|
||||||
if template_name:
|
if template_name:
|
||||||
due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
|
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
|
||||||
else:
|
else:
|
||||||
if party_type == "Supplier":
|
if party_type == "Supplier":
|
||||||
supplier_type = frappe.db.get_value(party_type, party, fieldname="supplier_type")
|
supplier_type = frappe.db.get_value(party_type, party, fieldname="supplier_type")
|
||||||
template_name = frappe.db.get_value("Supplier Type", supplier_type, fieldname="payment_terms")
|
template_name = frappe.db.get_value("Supplier Type", supplier_type, fieldname="payment_terms")
|
||||||
if template_name:
|
if template_name:
|
||||||
due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
|
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
|
||||||
|
# If due date is calculated from bill_date, check this condition
|
||||||
|
if getdate(due_date) < getdate(posting_date):
|
||||||
|
due_date = posting_date
|
||||||
return due_date
|
return due_date
|
||||||
|
|
||||||
|
def get_due_date_from_template(template_name, posting_date, bill_date):
|
||||||
def get_due_date_from_template(template_name, posting_date):
|
|
||||||
"""
|
"""
|
||||||
Inspects all `Payment Term`s from the a `Payment Terms Template` and returns the due
|
Inspects all `Payment Term`s from the a `Payment Terms Template` and returns the due
|
||||||
date after considering all the `Payment Term`s requirements.
|
date after considering all the `Payment Term`s requirements.
|
||||||
:param template_name: Name of the `Payment Terms Template`
|
:param template_name: Name of the `Payment Terms Template`
|
||||||
:return: String representing the calculated due date
|
:return: String representing the calculated due date
|
||||||
"""
|
"""
|
||||||
due_date = getdate(posting_date)
|
due_date = getdate(bill_date or posting_date)
|
||||||
|
|
||||||
template = frappe.get_doc('Payment Terms Template', template_name)
|
template = frappe.get_doc('Payment Terms Template', template_name)
|
||||||
|
|
||||||
for term in template.terms:
|
for term in template.terms:
|
||||||
@@ -303,14 +302,13 @@ def get_due_date_from_template(template_name, posting_date):
|
|||||||
due_date = max(due_date, add_days(get_last_day(due_date), term.credit_days))
|
due_date = max(due_date, add_days(get_last_day(due_date), term.credit_days))
|
||||||
else:
|
else:
|
||||||
due_date = max(due_date, add_months(get_last_day(due_date), term.credit_months))
|
due_date = max(due_date, add_months(get_last_day(due_date), term.credit_months))
|
||||||
|
|
||||||
return due_date
|
return due_date
|
||||||
|
|
||||||
def validate_due_date(posting_date, due_date, party_type, party, company=None):
|
def validate_due_date(posting_date, due_date, party_type, party, company=None, bill_date=None):
|
||||||
if getdate(due_date) < getdate(posting_date):
|
if getdate(due_date) < getdate(posting_date):
|
||||||
frappe.throw(_("Due Date cannot be before Posting Date"))
|
frappe.throw(_("Due Date cannot be before Posting Date"))
|
||||||
else:
|
else:
|
||||||
default_due_date = get_due_date(posting_date, party_type, party, company)
|
default_due_date = get_due_date(posting_date, party_type, party, company, bill_date)
|
||||||
if not default_due_date:
|
if not default_due_date:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -363,7 +361,6 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
|
|||||||
def get_pyt_term_template(party_name, party_type, company=None):
|
def get_pyt_term_template(party_name, party_type, company=None):
|
||||||
if party_type not in ("Customer", "Supplier"):
|
if party_type not in ("Customer", "Supplier"):
|
||||||
return
|
return
|
||||||
|
|
||||||
template = None
|
template = None
|
||||||
if party_type == 'Customer':
|
if party_type == 'Customer':
|
||||||
customer = frappe.db.get_value("Customer", party_name,
|
customer = frappe.db.get_value("Customer", party_name,
|
||||||
@@ -377,13 +374,11 @@ def get_pyt_term_template(party_name, party_type, company=None):
|
|||||||
supplier = frappe.db.get_value("Supplier", party_name,
|
supplier = frappe.db.get_value("Supplier", party_name,
|
||||||
fieldname=['payment_terms', "supplier_type"], as_dict=1)
|
fieldname=['payment_terms', "supplier_type"], as_dict=1)
|
||||||
template = supplier.payment_terms
|
template = supplier.payment_terms
|
||||||
|
|
||||||
if not template and supplier.supplier_type:
|
if not template and supplier.supplier_type:
|
||||||
template = frappe.db.get_value("Supplier Type", supplier.supplier_type, fieldname='payment_terms')
|
template = frappe.db.get_value("Supplier Type", supplier.supplier_type, fieldname='payment_terms')
|
||||||
|
|
||||||
if not template and company:
|
if not template and company:
|
||||||
template = frappe.db.get_value("Company", company, fieldname='payment_terms')
|
template = frappe.db.get_value("Company", company, fieldname='payment_terms')
|
||||||
|
|
||||||
return template
|
return template
|
||||||
|
|
||||||
def validate_party_frozen_disabled(party_type, party_name):
|
def validate_party_frozen_disabled(party_type, party_name):
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company)
|
validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company)
|
||||||
elif self.doctype == "Purchase Invoice":
|
elif self.doctype == "Purchase Invoice":
|
||||||
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
|
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company, self.bill_date)
|
||||||
|
|
||||||
def set_price_list_currency(self, buying_or_selling):
|
def set_price_list_currency(self, buying_or_selling):
|
||||||
if self.meta.get_field("posting_date"):
|
if self.meta.get_field("posting_date"):
|
||||||
@@ -908,7 +908,7 @@ def update_invoice_status():
|
|||||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_terms(terms_template, posting_date=None, grand_total=None):
|
def get_payment_terms(terms_template, posting_date=None, grand_total=None, bill_date=None):
|
||||||
if not terms_template:
|
if not terms_template:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -916,13 +916,13 @@ def get_payment_terms(terms_template, posting_date=None, grand_total=None):
|
|||||||
|
|
||||||
schedule = []
|
schedule = []
|
||||||
for d in terms_doc.get("terms"):
|
for d in terms_doc.get("terms"):
|
||||||
term_details = get_payment_term_details(d, posting_date, grand_total)
|
term_details = get_payment_term_details(d, posting_date, grand_total, bill_date)
|
||||||
schedule.append(term_details)
|
schedule.append(term_details)
|
||||||
|
|
||||||
return schedule
|
return schedule
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_term_details(term, posting_date=None, grand_total=None):
|
def get_payment_term_details(term, posting_date=None, grand_total=None, bill_date=None):
|
||||||
term_details = frappe._dict()
|
term_details = frappe._dict()
|
||||||
if isinstance(term, unicode):
|
if isinstance(term, unicode):
|
||||||
term = frappe.get_doc("Payment Term", term)
|
term = frappe.get_doc("Payment Term", term)
|
||||||
@@ -931,17 +931,23 @@ def get_payment_term_details(term, posting_date=None, grand_total=None):
|
|||||||
term_details.description = term.description
|
term_details.description = term.description
|
||||||
term_details.invoice_portion = term.invoice_portion
|
term_details.invoice_portion = term.invoice_portion
|
||||||
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
||||||
if posting_date:
|
if bill_date:
|
||||||
term_details.due_date = get_due_date(posting_date, term)
|
term_details.due_date = get_due_date(term, bill_date)
|
||||||
|
elif posting_date:
|
||||||
|
term_details.due_date = get_due_date(term, posting_date)
|
||||||
|
|
||||||
|
if getdate(term_details.due_date) < getdate(posting_date):
|
||||||
|
term_details.due_date = posting_date
|
||||||
|
|
||||||
return term_details
|
return term_details
|
||||||
|
|
||||||
def get_due_date(posting_date, term):
|
def get_due_date(term, posting_date=None, bill_date=None):
|
||||||
due_date = None
|
due_date = None
|
||||||
|
date = bill_date or posting_date
|
||||||
if term.due_date_based_on == "Day(s) after invoice date":
|
if term.due_date_based_on == "Day(s) after invoice date":
|
||||||
due_date = add_days(posting_date, term.credit_days)
|
due_date = add_days(date, term.credit_days)
|
||||||
elif term.due_date_based_on == "Day(s) after the end of the invoice month":
|
elif term.due_date_based_on == "Day(s) after the end of the invoice month":
|
||||||
due_date = add_days(get_last_day(posting_date), term.credit_days)
|
due_date = add_days(get_last_day(date), term.credit_days)
|
||||||
elif term.due_date_based_on == "Month(s) after the end of the invoice month":
|
elif term.due_date_based_on == "Month(s) after the end of the invoice month":
|
||||||
due_date = add_months(get_last_day(posting_date), term.credit_months)
|
due_date = add_months(get_last_day(date), term.credit_months)
|
||||||
|
|
||||||
return due_date
|
return due_date
|
||||||
|
|||||||
@@ -518,6 +518,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
args: {
|
args: {
|
||||||
"posting_date": me.frm.doc.posting_date,
|
"posting_date": me.frm.doc.posting_date,
|
||||||
"party_type": me.frm.doc.doctype == "Sales Invoice" ? "Customer" : "Supplier",
|
"party_type": me.frm.doc.doctype == "Sales Invoice" ? "Customer" : "Supplier",
|
||||||
|
"bill_date": me.frm.doc.bill_date,
|
||||||
"party": me.frm.doc.doctype == "Sales Invoice" ? me.frm.doc.customer : me.frm.doc.supplier,
|
"party": me.frm.doc.doctype == "Sales Invoice" ? me.frm.doc.customer : me.frm.doc.supplier,
|
||||||
"company": me.frm.doc.company
|
"company": me.frm.doc.company
|
||||||
},
|
},
|
||||||
@@ -561,9 +562,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
bill_date: function() {
|
||||||
|
this.posting_date();
|
||||||
|
},
|
||||||
|
|
||||||
recalculate_terms: function() {
|
recalculate_terms: function() {
|
||||||
const doc = this.frm.doc;
|
const doc = this.frm.doc;
|
||||||
|
|
||||||
if (doc.payment_terms_template) {
|
if (doc.payment_terms_template) {
|
||||||
this.payment_terms_template();
|
this.payment_terms_template();
|
||||||
} else if (doc.payment_schedule) {
|
} else if (doc.payment_schedule) {
|
||||||
@@ -1272,14 +1276,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
var me = this;
|
var me = this;
|
||||||
const doc = this.frm.doc;
|
const doc = this.frm.doc;
|
||||||
if(doc.payment_terms_template && doc.doctype !== 'Delivery Note') {
|
if(doc.payment_terms_template && doc.doctype !== 'Delivery Note') {
|
||||||
var posting_date = doc.bill_date || doc.posting_date || doc.transaction_date;
|
var posting_date = doc.posting_date || doc.transaction_date;
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.controllers.accounts_controller.get_payment_terms",
|
method: "erpnext.controllers.accounts_controller.get_payment_terms",
|
||||||
args: {
|
args: {
|
||||||
terms_template: doc.payment_terms_template,
|
terms_template: doc.payment_terms_template,
|
||||||
posting_date: posting_date,
|
posting_date: posting_date,
|
||||||
grand_total: doc.rounded_total || doc.grand_total
|
grand_total: doc.rounded_total || doc.grand_total,
|
||||||
|
bill_date: doc.bill_date
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(r.message && !r.exc) {
|
if(r.message && !r.exc) {
|
||||||
@@ -1297,6 +1301,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
method: "erpnext.controllers.accounts_controller.get_payment_term_details",
|
method: "erpnext.controllers.accounts_controller.get_payment_term_details",
|
||||||
args: {
|
args: {
|
||||||
term: row.payment_term,
|
term: row.payment_term,
|
||||||
|
bill_date: this.frm.doc.bill_date,
|
||||||
posting_date: this.frm.doc.posting_date || this.frm.doc.transaction_date,
|
posting_date: this.frm.doc.posting_date || this.frm.doc.transaction_date,
|
||||||
grand_total: this.frm.doc.rounded_total || this.frm.doc.grand_total
|
grand_total: this.frm.doc.rounded_total || this.frm.doc.grand_total
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
|||||||
args = {
|
args = {
|
||||||
party: frm.doc.supplier,
|
party: frm.doc.supplier,
|
||||||
party_type: "Supplier",
|
party_type: "Supplier",
|
||||||
|
bill_date: frm.doc.bill_date,
|
||||||
price_list: frm.doc.buying_price_list
|
price_list: frm.doc.buying_price_list
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user