fix: Provision to apply early payment discount if payment is recorded late
- Party could have paid on time but payment is recorded late - Prompt for reference date so that discount is applied while mapping - Prompt only if discount in payment schedule of valid doctypes - test: Reference date and impact on PE - `make_payment_entry` (JS) must be able to access `this`
This commit is contained in:
parent
e1d28063b0
commit
d6d0163514
@ -1686,7 +1686,14 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry(
|
||||
dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None
|
||||
dt,
|
||||
dn,
|
||||
party_amount=None,
|
||||
bank_account=None,
|
||||
bank_amount=None,
|
||||
party_type=None,
|
||||
payment_type=None,
|
||||
reference_date=None,
|
||||
):
|
||||
reference_doc = None
|
||||
doc = frappe.get_doc(dt, dn)
|
||||
@ -1713,8 +1720,9 @@ def get_payment_entry(
|
||||
dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
|
||||
)
|
||||
|
||||
reference_date = getdate(reference_date)
|
||||
paid_amount, received_amount, discount_amount, valid_discounts = apply_early_payment_discount(
|
||||
paid_amount, received_amount, doc, party_account_currency
|
||||
paid_amount, received_amount, doc, party_account_currency, reference_date
|
||||
)
|
||||
|
||||
pe = frappe.new_doc("Payment Entry")
|
||||
@ -1722,6 +1730,7 @@ def get_payment_entry(
|
||||
pe.company = doc.company
|
||||
pe.cost_center = doc.get("cost_center")
|
||||
pe.posting_date = nowdate()
|
||||
pe.reference_date = reference_date
|
||||
pe.mode_of_payment = doc.get("mode_of_payment")
|
||||
pe.party_type = party_type
|
||||
pe.party = doc.get(scrub(party_type))
|
||||
@ -1931,7 +1940,9 @@ def set_paid_amount_and_received_amount(
|
||||
return paid_amount, received_amount
|
||||
|
||||
|
||||
def apply_early_payment_discount(paid_amount, received_amount, doc, party_account_currency):
|
||||
def apply_early_payment_discount(
|
||||
paid_amount, received_amount, doc, party_account_currency, reference_date
|
||||
):
|
||||
total_discount = 0
|
||||
valid_discounts = []
|
||||
eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"]
|
||||
@ -1940,7 +1951,7 @@ def apply_early_payment_discount(paid_amount, received_amount, doc, party_accoun
|
||||
|
||||
if doc.doctype in eligible_for_payments and has_payment_schedule:
|
||||
for term in doc.payment_schedule:
|
||||
if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date:
|
||||
if not term.discounted_amount and term.discount and reference_date <= term.discount_date:
|
||||
|
||||
if term.discount_type == "Percentage":
|
||||
grand_total = doc.get("grand_total") if is_multi_currency else doc.get("base_grand_total")
|
||||
|
@ -302,6 +302,15 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
# Set reference date past discount cut off date
|
||||
pe_1 = get_payment_entry(
|
||||
"Sales Invoice",
|
||||
si.name,
|
||||
bank_account="_Test Cash - _TC",
|
||||
reference_date=frappe.utils.add_days(si.posting_date, 2),
|
||||
)
|
||||
self.assertEqual(pe_1.paid_amount, 236.0) # discount not applied
|
||||
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
|
||||
self.assertEqual(pe.references[0].allocated_amount, 236.0)
|
||||
self.assertEqual(pe.paid_amount, 186)
|
||||
|
@ -82,7 +82,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
||||
|
||||
if(doc.docstatus == 1 && doc.outstanding_amount != 0
|
||||
&& !(doc.is_return && doc.return_against) && !doc.on_hold) {
|
||||
this.frm.add_custom_button(__('Payment'), this.make_payment_entry, __('Create'));
|
||||
this.frm.add_custom_button(
|
||||
__('Payment'),
|
||||
() => this.make_payment_entry(),
|
||||
__('Create')
|
||||
);
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
}
|
||||
|
||||
|
@ -93,9 +93,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
|
||||
if (doc.docstatus == 1 && doc.outstanding_amount!=0
|
||||
&& !(cint(doc.is_return) && doc.return_against)) {
|
||||
cur_frm.add_custom_button(__('Payment'),
|
||||
this.make_payment_entry, __('Create'));
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
this.frm.add_custom_button(
|
||||
__('Payment'),
|
||||
() => this.make_payment_entry(),
|
||||
__('Create')
|
||||
);
|
||||
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
}
|
||||
|
||||
if(doc.docstatus==1 && !doc.is_return) {
|
||||
|
@ -236,7 +236,11 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
|
||||
this.make_purchase_invoice, __('Create'));
|
||||
|
||||
if(flt(doc.per_billed) < 100 && doc.status != "Delivered") {
|
||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
|
||||
this.frm.add_custom_button(
|
||||
__('Payment'),
|
||||
() => this.make_payment_entry(),
|
||||
__('Create')
|
||||
);
|
||||
}
|
||||
|
||||
if(flt(doc.per_billed) < 100) {
|
||||
|
@ -1897,20 +1897,60 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
}
|
||||
|
||||
make_payment_entry() {
|
||||
let via_journal_entry = this.frm.doc.__onload && this.frm.doc.__onload.make_payment_via_journal_entry;
|
||||
if(this.has_discount_in_schedule() && !via_journal_entry) {
|
||||
// If early payment discount is applied, ask user for reference date
|
||||
this.prompt_user_for_reference_date();
|
||||
} else {
|
||||
this.make_mapped_payment_entry();
|
||||
}
|
||||
}
|
||||
|
||||
make_mapped_payment_entry(args) {
|
||||
var me = this;
|
||||
args = args || { "dt": this.frm.doc.doctype, "dn": this.frm.doc.name };
|
||||
return frappe.call({
|
||||
method: cur_frm.cscript.get_method_for_payment(),
|
||||
args: {
|
||||
"dt": cur_frm.doc.doctype,
|
||||
"dn": cur_frm.doc.name
|
||||
},
|
||||
method: me.get_method_for_payment(),
|
||||
args: args,
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
// cur_frm.refresh_fields()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
prompt_user_for_reference_date(){
|
||||
var me = this;
|
||||
frappe.prompt({
|
||||
label: __("Cheque/Reference Date"),
|
||||
fieldname: "reference_date",
|
||||
fieldtype: "Date",
|
||||
reqd: 1,
|
||||
}, (values) => {
|
||||
let args = {
|
||||
"dt": me.frm.doc.doctype,
|
||||
"dn": me.frm.doc.name,
|
||||
"reference_date": values.reference_date
|
||||
}
|
||||
me.make_mapped_payment_entry(args);
|
||||
},
|
||||
__("Reference Date for Early Payment Discount"),
|
||||
__("Continue")
|
||||
);
|
||||
}
|
||||
|
||||
has_discount_in_schedule() {
|
||||
let is_eligible = in_list(
|
||||
["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"],
|
||||
this.frm.doctype
|
||||
);
|
||||
let has_payment_schedule = this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length;
|
||||
if(!is_eligible || !has_payment_schedule) return false;
|
||||
|
||||
let has_discount = this.frm.doc.payment_schedule.some(row => row.discount_date);
|
||||
return has_discount;
|
||||
}
|
||||
|
||||
make_quality_inspection() {
|
||||
let data = [];
|
||||
const fields = [
|
||||
|
Loading…
x
Reference in New Issue
Block a user