more validation checks and test fixes
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 59e64aa..83e1181 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -607,11 +607,13 @@
pi = make_purchase_invoice(do_not_save=True, supplier="_Test Supplier P")
pi.append("payment_schedule", {
"due_date": add_days(nowdate(), 15),
- "payment_amount": 100
+ "payment_amount": 100,
+ "invoice_portion": 40.00
})
pi.append("payment_schedule", {
"due_date": add_days(nowdate(), 45),
- "payment_amount": 150
+ "payment_amount": 150,
+ "invoice_portion": 60.00
})
pi.save()
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index da33a78..235dec4 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1279,11 +1279,13 @@
si = create_sales_invoice(do_not_save=True, customer="_Test Customer P")
si.append("payment_schedule", {
"due_date": add_days(nowdate(), 15),
- "payment_amount": 20
+ "payment_amount": 20,
+ "invoice_portion": 20.00
})
si.append("payment_schedule", {
"due_date": add_days(nowdate(), 45),
- "payment_amount": 80
+ "payment_amount": 80,
+ "invoice_portion": 80.00
})
si.save()
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 0e54c4a..cc624c8 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -34,6 +34,7 @@
def validate(self):
if self.get("_action") and self._action != "update_after_submit":
self.set_missing_values(for_validate=True)
+
self.validate_date_with_fiscal_year()
if self.meta.get_field("currency"):
@@ -45,17 +46,7 @@
validate_return(self)
self.set_total_in_words()
- if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
- if self.get("payment_schedule"):
- self.set_due_date()
- self.validate_payment_schedule()
- else:
- self.set_payment_schedule()
- self.set_due_date()
- self.validate_due_date()
- self.validate_advance_entries()
- elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"):
- self.set_payment_schedule()
+ self.validate_all_documents_schedule()
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
@@ -66,6 +57,29 @@
if self.doctype == 'Purchase Invoice':
self.validate_paid_amount()
+ def validate_invoice_documents_schedule(self):
+ if self.get("payment_schedule"):
+ self.set_due_date()
+ self.validate_payment_schedule()
+ else:
+ self.set_payment_schedule()
+ self.set_due_date()
+ self.validate_due_date()
+ self.validate_advance_entries()
+
+ def validate_non_invoice_documents_schedule(self):
+ if self.get("payment_schedule"):
+ self.validate_invoice_portion()
+ self.validate_payment_schedule_amount()
+ else:
+ self.set_payment_schedule()
+
+ def validate_all_documents_schedule(self):
+ if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
+ self.validate_invoice_documents_schedule()
+ elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"):
+ self.validate_non_invoice_documents_schedule()
+
def before_print(self):
if self.doctype in ['Purchase Order', 'Sales Order']:
if self.get("group_same_items"):
@@ -85,11 +99,11 @@
self.paid_amount = 0
frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
else:
- frappe.db.set(self,'paid_amount',0)
+ frappe.db.set(self, 'paid_amount', 0)
def set_missing_values(self, for_validate=False):
if frappe.flags.in_test:
- for fieldname in ["posting_date","transaction_date"]:
+ for fieldname in ["posting_date", "transaction_date"]:
if self.meta.get_field(fieldname) and not self.get(fieldname):
self.set(fieldname, today())
break
@@ -629,19 +643,34 @@
self.due_date = max([d.due_date for d in self.get("payment_schedule")])
def validate_payment_schedule(self):
+ self.validate_payment_schedule_dates()
+ self.validate_invoice_portion()
+ self.validate_payment_schedule_amount()
+
+ def validate_payment_schedule_dates(self):
if self.due_date and getdate(self.due_date) < getdate(self.posting_date):
frappe.throw(_("Due Date cannot be before posting date"))
- total = 0
for d in self.get("payment_schedule"):
if getdate(d.due_date) < getdate(self.posting_date):
frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx))
+ def validate_payment_schedule_amount(self):
+ total = 0
+ for d in self.get("payment_schedule"):
total += flt(d.payment_amount)
if total != self.grand_total:
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand Total"))
+ def validate_invoice_portion(self):
+ total_portion = 0
+ for term in self.payment_schedule:
+ total_portion += term.invoice_portion
+
+ if flt(total_portion, 2) != 100.00:
+ frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
+
@frappe.whitelist()
def get_tax_rate(account_head):