mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-14 12:25:09 +00:00
* feat: dynamic due date in payment terms when fetched from order (#48864)
* fix: dynamic due date when payment terms are fetched from order
* fix(test): use change_settings decorator for settings enable and disable
* fix(test): compare schedule for due_date dynamically
* fix: save conditions for due date at invoice level
* fix: make fields read only and on change of date unset the date condition fields
* fix: remove fetch_form
* fix: correct field assingment
* fix: revert unwanted changes
* refactor: streamline payment term field assignments and enhance discount date handling
* refactor: remove payment_term from fields_to_copy and optimize currency handling in transaction callback
* refactor: ensure default values for payment schedule and discount validity fields
(cherry picked from commit 3c70cbbaf8)
# Conflicts:
# erpnext/public/js/controllers/transaction.js
# erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
* chore: resolve conflicts
---------
Co-authored-by: Lakshit Jain <ljain112@gmail.com>
This commit is contained in:
@@ -10,14 +10,19 @@
|
|||||||
"description",
|
"description",
|
||||||
"section_break_4",
|
"section_break_4",
|
||||||
"due_date",
|
"due_date",
|
||||||
|
"invoice_portion",
|
||||||
"mode_of_payment",
|
"mode_of_payment",
|
||||||
"column_break_5",
|
"column_break_5",
|
||||||
"invoice_portion",
|
"due_date_based_on",
|
||||||
|
"credit_days",
|
||||||
|
"credit_months",
|
||||||
"section_break_6",
|
"section_break_6",
|
||||||
"discount_type",
|
|
||||||
"discount_date",
|
"discount_date",
|
||||||
"column_break_9",
|
|
||||||
"discount",
|
"discount",
|
||||||
|
"discount_type",
|
||||||
|
"column_break_9",
|
||||||
|
"discount_validity_based_on",
|
||||||
|
"discount_validity",
|
||||||
"section_break_9",
|
"section_break_9",
|
||||||
"payment_amount",
|
"payment_amount",
|
||||||
"outstanding",
|
"outstanding",
|
||||||
@@ -172,12 +177,50 @@
|
|||||||
"label": "Paid Amount (Company Currency)",
|
"label": "Paid Amount (Company Currency)",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "due_date_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Due Date Based On",
|
||||||
|
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||||
|
"fieldname": "credit_days",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Credit Days",
|
||||||
|
"non_negative": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||||
|
"fieldname": "credit_months",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Credit Months",
|
||||||
|
"non_negative": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fieldname": "discount_validity_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Discount Validity Based On",
|
||||||
|
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount_validity_based_on",
|
||||||
|
"fieldname": "discount_validity",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Discount Validity",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-03-11 11:06:51.792982",
|
"modified": "2025-07-31 08:38:25.820701",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Schedule",
|
"name": "Payment Schedule",
|
||||||
|
|||||||
@@ -17,12 +17,27 @@ class PaymentSchedule(Document):
|
|||||||
base_outstanding: DF.Currency
|
base_outstanding: DF.Currency
|
||||||
base_paid_amount: DF.Currency
|
base_paid_amount: DF.Currency
|
||||||
base_payment_amount: DF.Currency
|
base_payment_amount: DF.Currency
|
||||||
|
credit_days: DF.Int
|
||||||
|
credit_months: DF.Int
|
||||||
description: DF.SmallText | None
|
description: DF.SmallText | None
|
||||||
discount: DF.Float
|
discount: DF.Float
|
||||||
discount_date: DF.Date | None
|
discount_date: DF.Date | None
|
||||||
discount_type: DF.Literal["Percentage", "Amount"]
|
discount_type: DF.Literal["Percentage", "Amount"]
|
||||||
|
discount_validity: DF.Int
|
||||||
|
discount_validity_based_on: DF.Literal[
|
||||||
|
"",
|
||||||
|
"Day(s) after invoice date",
|
||||||
|
"Day(s) after the end of the invoice month",
|
||||||
|
"Month(s) after the end of the invoice month",
|
||||||
|
]
|
||||||
discounted_amount: DF.Currency
|
discounted_amount: DF.Currency
|
||||||
due_date: DF.Date
|
due_date: DF.Date
|
||||||
|
due_date_based_on: DF.Literal[
|
||||||
|
"",
|
||||||
|
"Day(s) after invoice date",
|
||||||
|
"Day(s) after the end of the invoice month",
|
||||||
|
"Month(s) after the end of the invoice month",
|
||||||
|
]
|
||||||
invoice_portion: DF.Percent
|
invoice_portion: DF.Percent
|
||||||
mode_of_payment: DF.Link | None
|
mode_of_payment: DF.Link | None
|
||||||
outstanding: DF.Currency
|
outstanding: DF.Currency
|
||||||
|
|||||||
@@ -2150,19 +2150,16 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
|
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
|
||||||
self.assertAlmostEqual(rate, 500)
|
self.assertAlmostEqual(rate, 500)
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||||
def test_payment_allocation_for_payment_terms(self):
|
def test_payment_allocation_for_payment_terms(self):
|
||||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
|
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
|
||||||
create_pr_against_po,
|
create_pr_against_po,
|
||||||
create_purchase_order,
|
create_purchase_order,
|
||||||
)
|
)
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
|
||||||
automatically_fetch_payment_terms,
|
|
||||||
)
|
|
||||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
||||||
make_purchase_invoice as make_pi_from_pr,
|
make_purchase_invoice as make_pi_from_pr,
|
||||||
)
|
)
|
||||||
|
|
||||||
automatically_fetch_payment_terms()
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
"Payment Terms Template",
|
"Payment Terms Template",
|
||||||
"_Test Payment Term Template",
|
"_Test Payment Term Template",
|
||||||
@@ -2188,7 +2185,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
pi = make_pi_from_pr(pr.name)
|
pi = make_pi_from_pr(pr.name)
|
||||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
|
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
|
||||||
|
|
||||||
automatically_fetch_payment_terms(enable=0)
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
"Payment Terms Template",
|
"Payment Terms Template",
|
||||||
"_Test Payment Term Template",
|
"_Test Payment Term Template",
|
||||||
|
|||||||
@@ -541,12 +541,8 @@ class TestPurchaseOrder(FrappeTestCase):
|
|||||||
self.assertRaises(frappe.ValidationError, pr.submit)
|
self.assertRaises(frappe.ValidationError, pr.submit)
|
||||||
self.assertRaises(frappe.ValidationError, pi.submit)
|
self.assertRaises(frappe.ValidationError, pi.submit)
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||||
def test_make_purchase_invoice_with_terms(self):
|
def test_make_purchase_invoice_with_terms(self):
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
|
||||||
automatically_fetch_payment_terms,
|
|
||||||
)
|
|
||||||
|
|
||||||
automatically_fetch_payment_terms()
|
|
||||||
po = create_purchase_order(do_not_save=True)
|
po = create_purchase_order(do_not_save=True)
|
||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
|
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
|
||||||
@@ -570,7 +566,6 @@ class TestPurchaseOrder(FrappeTestCase):
|
|||||||
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
|
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
|
||||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
|
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
|
||||||
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
||||||
automatically_fetch_payment_terms(enable=0)
|
|
||||||
|
|
||||||
def test_warehouse_company_validation(self):
|
def test_warehouse_company_validation(self):
|
||||||
from erpnext.stock.utils import InvalidWarehouseCompany
|
from erpnext.stock.utils import InvalidWarehouseCompany
|
||||||
@@ -718,6 +713,7 @@ class TestPurchaseOrder(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(due_date, "2023-03-31")
|
self.assertEqual(due_date, "2023-03-31")
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 0})
|
||||||
def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
|
def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
|
||||||
po = create_purchase_order(do_not_save=1)
|
po = create_purchase_order(do_not_save=1)
|
||||||
po.payment_terms_template = "_Test Payment Term Template"
|
po.payment_terms_template = "_Test Payment Term Template"
|
||||||
@@ -905,18 +901,16 @@ class TestPurchaseOrder(FrappeTestCase):
|
|||||||
bo.load_from_db()
|
bo.load_from_db()
|
||||||
self.assertEqual(bo.items[0].ordered_qty, 5)
|
self.assertEqual(bo.items[0].ordered_qty, 5)
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||||
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
|
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||||
create_payment_terms_template,
|
create_payment_terms_template,
|
||||||
)
|
)
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||||
automatically_fetch_payment_terms,
|
|
||||||
compare_payment_schedules,
|
compare_payment_schedules,
|
||||||
)
|
)
|
||||||
|
|
||||||
automatically_fetch_payment_terms()
|
|
||||||
|
|
||||||
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
|
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
|
||||||
create_payment_terms_template()
|
create_payment_terms_template()
|
||||||
po.payment_terms_template = "Test Receivable Template"
|
po.payment_terms_template = "Test Receivable Template"
|
||||||
@@ -930,8 +924,6 @@ class TestPurchaseOrder(FrappeTestCase):
|
|||||||
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
|
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
|
||||||
compare_payment_schedules(self, po, pi)
|
compare_payment_schedules(self, po, pi)
|
||||||
|
|
||||||
automatically_fetch_payment_terms(enable=0)
|
|
||||||
|
|
||||||
def test_internal_transfer_flow(self):
|
def test_internal_transfer_flow(self):
|
||||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
||||||
|
|||||||
@@ -2558,6 +2558,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
self.payment_schedule = []
|
self.payment_schedule = []
|
||||||
self.payment_terms_template = po_or_so.payment_terms_template
|
self.payment_terms_template = po_or_so.payment_terms_template
|
||||||
|
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
|
||||||
|
|
||||||
for schedule in po_or_so.payment_schedule:
|
for schedule in po_or_so.payment_schedule:
|
||||||
payment_schedule = {
|
payment_schedule = {
|
||||||
@@ -2570,6 +2571,17 @@ class AccountsController(TransactionBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if automatically_fetch_payment_terms:
|
if automatically_fetch_payment_terms:
|
||||||
|
if schedule.due_date_based_on:
|
||||||
|
payment_schedule["due_date"] = get_due_date(schedule, posting_date)
|
||||||
|
payment_schedule["due_date_based_on"] = schedule.due_date_based_on
|
||||||
|
payment_schedule["credit_days"] = cint(schedule.credit_days)
|
||||||
|
payment_schedule["credit_months"] = cint(schedule.credit_months)
|
||||||
|
|
||||||
|
if schedule.discount_validity_based_on:
|
||||||
|
payment_schedule["discount_date"] = get_discount_date(schedule, posting_date)
|
||||||
|
payment_schedule["discount_validity_based_on"] = schedule.discount_validity_based_on
|
||||||
|
payment_schedule["discount_validity"] = cint(schedule.discount_validity)
|
||||||
|
|
||||||
payment_schedule["payment_amount"] = flt(
|
payment_schedule["payment_amount"] = flt(
|
||||||
grand_total * flt(payment_schedule["invoice_portion"]) / 100,
|
grand_total * flt(payment_schedule["invoice_portion"]) / 100,
|
||||||
schedule.precision("payment_amount"),
|
schedule.precision("payment_amount"),
|
||||||
@@ -3369,14 +3381,27 @@ def get_payment_term_details(
|
|||||||
term = frappe.get_doc("Payment Term", term)
|
term = frappe.get_doc("Payment Term", term)
|
||||||
else:
|
else:
|
||||||
term_details.payment_term = term.payment_term
|
term_details.payment_term = term.payment_term
|
||||||
term_details.description = term.description
|
|
||||||
term_details.invoice_portion = term.invoice_portion
|
fields_to_copy = [
|
||||||
|
"description",
|
||||||
|
"invoice_portion",
|
||||||
|
"discount_type",
|
||||||
|
"discount",
|
||||||
|
"mode_of_payment",
|
||||||
|
"due_date_based_on",
|
||||||
|
"credit_days",
|
||||||
|
"credit_months",
|
||||||
|
"discount_validity_based_on",
|
||||||
|
"discount_validity",
|
||||||
|
]
|
||||||
|
|
||||||
|
for field in fields_to_copy:
|
||||||
|
term_details[field] = term.get(field)
|
||||||
|
|
||||||
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
||||||
term_details.base_payment_amount = flt(term.invoice_portion) * flt(base_grand_total) / 100
|
term_details.base_payment_amount = flt(term.invoice_portion) * flt(base_grand_total) / 100
|
||||||
term_details.discount_type = term.discount_type
|
|
||||||
term_details.discount = term.discount
|
|
||||||
term_details.outstanding = term_details.payment_amount
|
term_details.outstanding = term_details.payment_amount
|
||||||
term_details.mode_of_payment = term.mode_of_payment
|
term_details.base_outstanding = term_details.base_payment_amount
|
||||||
|
|
||||||
if bill_date:
|
if bill_date:
|
||||||
term_details.due_date = get_due_date(term, bill_date)
|
term_details.due_date = get_due_date(term, bill_date)
|
||||||
@@ -3395,11 +3420,11 @@ def get_due_date(term, posting_date=None, bill_date=None):
|
|||||||
due_date = None
|
due_date = None
|
||||||
date = bill_date or posting_date
|
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(date, term.credit_days)
|
due_date = add_days(date, cint(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(date), term.credit_days)
|
due_date = add_days(get_last_day(date), cint(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 = get_last_day(add_months(date, term.credit_months))
|
due_date = get_last_day(add_months(date, cint(term.credit_months)))
|
||||||
return due_date
|
return due_date
|
||||||
|
|
||||||
|
|
||||||
@@ -3407,11 +3432,11 @@ def get_discount_date(term, posting_date=None, bill_date=None):
|
|||||||
discount_validity = None
|
discount_validity = None
|
||||||
date = bill_date or posting_date
|
date = bill_date or posting_date
|
||||||
if term.discount_validity_based_on == "Day(s) after invoice date":
|
if term.discount_validity_based_on == "Day(s) after invoice date":
|
||||||
discount_validity = add_days(date, term.discount_validity)
|
discount_validity = add_days(date, cint(term.discount_validity))
|
||||||
elif term.discount_validity_based_on == "Day(s) after the end of the invoice month":
|
elif term.discount_validity_based_on == "Day(s) after the end of the invoice month":
|
||||||
discount_validity = add_days(get_last_day(date), term.discount_validity)
|
discount_validity = add_days(get_last_day(date), cint(term.discount_validity))
|
||||||
elif term.discount_validity_based_on == "Month(s) after the end of the invoice month":
|
elif term.discount_validity_based_on == "Month(s) after the end of the invoice month":
|
||||||
discount_validity = get_last_day(add_months(date, term.discount_validity))
|
discount_validity = get_last_day(add_months(date, cint(term.discount_validity)))
|
||||||
return discount_validity
|
return discount_validity
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1082,12 +1082,25 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
due_date(doc, cdt) {
|
discount_date(doc, cdt, cdn) {
|
||||||
|
// Remove fields as discount_date is auto-managed by payment terms
|
||||||
|
const row = locals[cdt][cdn];
|
||||||
|
["discount_validity", "discount_validity_based_on"].forEach((field) => {
|
||||||
|
row[field] = "";
|
||||||
|
});
|
||||||
|
this.frm.refresh_field("payment_schedule");
|
||||||
|
}
|
||||||
|
|
||||||
|
due_date(doc, cdt, cdn) {
|
||||||
// due_date is to be changed, payment terms template and/or payment schedule must
|
// due_date is to be changed, payment terms template and/or payment schedule must
|
||||||
// be removed as due_date is automatically changed based on payment terms
|
// be removed as due_date is automatically changed based on payment terms
|
||||||
if (doc.doctype !== cdt) {
|
if (doc.doctype !== cdt) {
|
||||||
// triggered by change to the due_date field in payment schedule child table
|
// Remove fields as due_date is auto-managed by payment terms
|
||||||
// do nothing to avoid infinite clearing loop
|
const row = locals[cdt][cdn];
|
||||||
|
["due_date_based_on", "credit_days", "credit_months"].forEach((field) => {
|
||||||
|
row[field] = "";
|
||||||
|
});
|
||||||
|
this.frm.refresh_field("payment_schedule");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2621,6 +2634,17 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
payment_term(doc, cdt, cdn) {
|
payment_term(doc, cdt, cdn) {
|
||||||
const me = this;
|
const me = this;
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
|
// empty date condition fields
|
||||||
|
[
|
||||||
|
"due_date_based_on",
|
||||||
|
"credit_days",
|
||||||
|
"credit_months",
|
||||||
|
"discount_validity",
|
||||||
|
"discount_validity_based_on",
|
||||||
|
].forEach(function (field) {
|
||||||
|
row[field] = "";
|
||||||
|
});
|
||||||
|
|
||||||
if(row.payment_term) {
|
if(row.payment_term) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.controllers.accounts_controller.get_payment_term_details",
|
method: "erpnext.controllers.accounts_controller.get_payment_term_details",
|
||||||
@@ -2633,14 +2657,17 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(r.message && !r.exc) {
|
if(r.message && !r.exc) {
|
||||||
for (var d in r.message) {
|
const company_currency = me.get_company_currency();
|
||||||
frappe.model.set_value(cdt, cdn, d, r.message[d]);
|
for (let d in r.message) {
|
||||||
const company_currency = me.get_company_currency();
|
row[d] = r.message[d];
|
||||||
me.update_payment_schedule_grid_labels(company_currency);
|
|
||||||
}
|
}
|
||||||
|
me.update_payment_schedule_grid_labels(company_currency)
|
||||||
|
me.frm.refresh_field("payment_schedule");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
} else {
|
||||||
|
me.frm.refresh_field("payment_schedule");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from frappe.tests.utils import FrappeTestCase, change_settings
|
|||||||
from frappe.utils import add_days, flt, getdate, nowdate, today
|
from frappe.utils import add_days, flt, getdate, nowdate, today
|
||||||
|
|
||||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||||
from erpnext.controllers.accounts_controller import InvalidQtyError, update_child_qty_rate
|
from erpnext.controllers.accounts_controller import InvalidQtyError, get_due_date, update_child_qty_rate
|
||||||
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
|
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
|
||||||
make_maintenance_schedule,
|
make_maintenance_schedule,
|
||||||
)
|
)
|
||||||
@@ -1680,14 +1680,13 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
|
|||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
self.assertRaises(frappe.LinkExistsError, so.cancel)
|
self.assertRaises(frappe.LinkExistsError, so.cancel)
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||||
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||||
create_payment_terms_template,
|
create_payment_terms_template,
|
||||||
)
|
)
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
automatically_fetch_payment_terms()
|
|
||||||
|
|
||||||
so = make_sales_order(uom="Nos", do_not_save=1)
|
so = make_sales_order(uom="Nos", do_not_save=1)
|
||||||
create_payment_terms_template()
|
create_payment_terms_template()
|
||||||
so.payment_terms_template = "Test Receivable Template"
|
so.payment_terms_template = "Test Receivable Template"
|
||||||
@@ -1701,8 +1700,6 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
|
|||||||
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
|
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
|
||||||
compare_payment_schedules(self, so, si)
|
compare_payment_schedules(self, so, si)
|
||||||
|
|
||||||
automatically_fetch_payment_terms(enable=0)
|
|
||||||
|
|
||||||
def test_zero_amount_sales_order_billing_status(self):
|
def test_zero_amount_sales_order_billing_status(self):
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
@@ -2421,16 +2418,14 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
|
|||||||
self.assertEqual(si2.items[0].qty, 20)
|
self.assertEqual(si2.items[0].qty, 20)
|
||||||
|
|
||||||
|
|
||||||
def automatically_fetch_payment_terms(enable=1):
|
|
||||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
|
||||||
accounts_settings.automatically_fetch_payment_terms = enable
|
|
||||||
accounts_settings.save()
|
|
||||||
|
|
||||||
|
|
||||||
def compare_payment_schedules(doc, doc1, doc2):
|
def compare_payment_schedules(doc, doc1, doc2):
|
||||||
for index, schedule in enumerate(doc1.get("payment_schedule")):
|
for index, schedule in enumerate(doc1.get("payment_schedule")):
|
||||||
|
posting_date = doc1.get("bill_date") or doc1.get("posting_date") or doc1.get("transaction_date")
|
||||||
|
due_date = schedule.due_date
|
||||||
|
if schedule.due_date_based_on:
|
||||||
|
due_date = get_due_date(schedule, posting_date=posting_date)
|
||||||
doc.assertEqual(schedule.payment_term, doc2.payment_schedule[index].payment_term)
|
doc.assertEqual(schedule.payment_term, doc2.payment_schedule[index].payment_term)
|
||||||
doc.assertEqual(getdate(schedule.due_date), doc2.payment_schedule[index].due_date)
|
doc.assertEqual(due_date, doc2.payment_schedule[index].due_date)
|
||||||
doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion)
|
doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion)
|
||||||
doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount)
|
doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount)
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ from erpnext.accounts.utils import get_balance_on
|
|||||||
from erpnext.controllers.accounts_controller import InvalidQtyError
|
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||||
automatically_fetch_payment_terms,
|
|
||||||
compare_payment_schedules,
|
compare_payment_schedules,
|
||||||
create_dn_against_so,
|
create_dn_against_so,
|
||||||
make_sales_order,
|
make_sales_order,
|
||||||
@@ -1300,14 +1299,13 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
|
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
|
||||||
frappe.db.set_single_value("Accounts Settings", "delete_linked_ledger_entries", 0)
|
frappe.db.set_single_value("Accounts Settings", "delete_linked_ledger_entries", 0)
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||||
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||||
create_payment_terms_template,
|
create_payment_terms_template,
|
||||||
)
|
)
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
automatically_fetch_payment_terms()
|
|
||||||
|
|
||||||
so = make_sales_order(uom="Nos", do_not_save=1)
|
so = make_sales_order(uom="Nos", do_not_save=1)
|
||||||
create_payment_terms_template()
|
create_payment_terms_template()
|
||||||
so.payment_terms_template = "Test Receivable Template"
|
so.payment_terms_template = "Test Receivable Template"
|
||||||
@@ -1327,8 +1325,6 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
|
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
|
||||||
compare_payment_schedules(self, so, si)
|
compare_payment_schedules(self, so, si)
|
||||||
|
|
||||||
automatically_fetch_payment_terms(enable=0)
|
|
||||||
|
|
||||||
def test_returned_qty_in_return_dn(self):
|
def test_returned_qty_in_return_dn(self):
|
||||||
# SO ---> SI ---> DN
|
# SO ---> SI ---> DN
|
||||||
# |
|
# |
|
||||||
|
|||||||
@@ -1180,6 +1180,7 @@ class TestPurchaseReceipt(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
|
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
|
||||||
|
|
||||||
|
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||||
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
|
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||||
create_payment_terms_template,
|
create_payment_terms_template,
|
||||||
@@ -1190,12 +1191,9 @@ class TestPurchaseReceipt(FrappeTestCase):
|
|||||||
make_pr_against_po,
|
make_pr_against_po,
|
||||||
)
|
)
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||||
automatically_fetch_payment_terms,
|
|
||||||
compare_payment_schedules,
|
compare_payment_schedules,
|
||||||
)
|
)
|
||||||
|
|
||||||
automatically_fetch_payment_terms()
|
|
||||||
|
|
||||||
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
|
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
|
||||||
create_payment_terms_template()
|
create_payment_terms_template()
|
||||||
po.payment_terms_template = "Test Receivable Template"
|
po.payment_terms_template = "Test Receivable Template"
|
||||||
@@ -1213,8 +1211,6 @@ class TestPurchaseReceipt(FrappeTestCase):
|
|||||||
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
|
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
|
||||||
compare_payment_schedules(self, po, pi)
|
compare_payment_schedules(self, po, pi)
|
||||||
|
|
||||||
automatically_fetch_payment_terms(enable=0)
|
|
||||||
|
|
||||||
@change_settings("Stock Settings", {"allow_negative_stock": 1})
|
@change_settings("Stock Settings", {"allow_negative_stock": 1})
|
||||||
def test_neg_to_positive(self):
|
def test_neg_to_positive(self):
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
|||||||
Reference in New Issue
Block a user