diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py index 89b59cb4784..e2ff62d62a1 100644 --- a/erpnext/crm/doctype/contract/contract.py +++ b/erpnext/crm/doctype/contract/contract.py @@ -17,7 +17,12 @@ class Contract(Document): if self.contract_template: name += " - {} Agreement".format(self.contract_template) - self.name = name + # If identical, append contract name with the next number in the iteration + if frappe.db.exists("Contract", name): + count = len(frappe.get_all("Contract", filters={"name": ["like", "%{}%".format(name)]})) + name = "{} - {}".format(name, count) + + self.name = _(name) def validate(self): self.validate_dates() @@ -32,7 +37,7 @@ class Contract(Document): def validate_dates(self): if self.end_date and self.end_date < self.start_date: - frappe.throw("End Date cannot be before Start Date!") + frappe.throw(_("End Date cannot be before Start Date!")) def update_contract_status(self): if self.is_signed: @@ -49,7 +54,7 @@ class Contract(Document): if not fulfilment_progress: fulfilment_status = "Unfulfilled" elif fulfilment_progress < len(self.fulfilment_terms): - fulfilment_status = "Partially Unfulfilled" + fulfilment_status = "Partially Fulfilled" elif fulfilment_progress == len(self.fulfilment_terms): fulfilment_status = "Fulfilled" @@ -97,7 +102,10 @@ def update_status_for_contracts(): and submitted Contracts """ - contracts = frappe.get_all("Contract", filters={"is_signed": True, "docstatus": 1}, fields=["name", "start_date", "end_date"]) + contracts = frappe.get_all("Contract", + filters={"is_signed": True, + "docstatus": 1}, + fields=["name", "start_date", "end_date"]) for contract in contracts: status = get_status(contract.get("start_date"), diff --git a/erpnext/crm/doctype/contract/test_contract.py b/erpnext/crm/doctype/contract/test_contract.py index a77fc6cd9b5..2b5c875d4d9 100644 --- a/erpnext/crm/doctype/contract/test_contract.py +++ b/erpnext/crm/doctype/contract/test_contract.py @@ -3,8 +3,108 @@ # See license.txt from __future__ import unicode_literals -import frappe import unittest +import frappe +from frappe.test_runner import make_test_records +from frappe.utils import add_days, nowdate + +contract_test_records = frappe.get_test_records('Contract') +make_test_records('Contract Template') +make_test_records("Customer", force=True) + + class TestContract(unittest.TestCase): - pass + def setUp(self): + self.contract_doc = frappe.copy_doc(contract_test_records[0]) + + template_with_requirements = frappe.get_all("Contract Template", filters={"requires_fulfilment": 1}) + self.contract_template_with_requirements = template_with_requirements[0].name + + template_without_requirements = frappe.get_all("Contract Template", filters={"requires_fulfilment": 0}) + self.contract_template_without_requirements = template_without_requirements[0].name + + def test_validate_start_date_before_end_date(self): + self.contract_doc.start_date = nowdate() + self.contract_doc.end_date = add_days(nowdate(), -1) + + self.assertRaises(frappe.ValidationError, self.contract_doc.insert) + + def test_unsigned_contract_status(self): + self.contract_doc.insert() + + self.assertEqual(self.contract_doc.status, "Unsigned") + + def test_active_signed_contract_status(self): + self.contract_doc.is_signed = True + self.contract_doc.start_date = add_days(nowdate(), -1) + self.contract_doc.end_date = add_days(nowdate(), 1) + self.contract_doc.insert() + + self.assertEqual(self.contract_doc.status, "Active") + + def test_past_inactive_signed_contract_status(self): + self.contract_doc.is_signed = True + self.contract_doc.start_date = add_days(nowdate(), -2) + self.contract_doc.end_date = add_days(nowdate(), -1) + self.contract_doc.insert() + + self.assertEqual(self.contract_doc.status, "Inactive") + + def test_future_inactive_signed_contract_status(self): + self.contract_doc.is_signed = True + self.contract_doc.start_date = add_days(nowdate(), 1) + self.contract_doc.end_date = add_days(nowdate(), 2) + self.contract_doc.insert() + + self.assertEqual(self.contract_doc.status, "Inactive") + + def test_contract_status_with_no_fulfilment_terms(self): + self.contract_doc.contract_terms = self.contract_template_without_requirements + self.contract_doc.insert() + + self.assertEqual(self.contract_doc.fulfilment_status, "N/A") + + def test_unfulfilled_contract_status(self): + self.contract_doc.contract_terms = self.contract_template_with_requirements + self.contract_doc.save() + self.contract_doc.insert() + + self.contract_doc.reload() + + self.assertEqual(self.contract_doc.fulfilment_status, "Unfulfilled") + + def test_fulfilled_contract_status(self): + self.contract_doc.contract_terms = self.contract_template_with_requirements + self.contract_doc.save() + self.contract_doc.insert() + + self.contract_doc.reload() + + # Mark all the terms as fulfilled + for term in self.contract_doc.fulfilment_terms: + term.fulfilled = 1 + + self.contract_doc.save() + + self.assertEqual(self.contract_doc.fulfilment_status, "Fulfilled") + + def test_partially_fulfilled_contract_status(self): + self.contract_doc.contract_terms = self.contract_template_with_requirements + self.contract_doc.insert() + + self.contract_doc.reload() + + # Mark only the first term as fulfilled + self.contract_doc.fulfilment_terms[0].fulfilled = 1 + self.contract_doc.save() + + self.assertEqual(self.contract_doc.fulfilment_status, "Partially Fulfilled") + + def test_lapsed_contract_status(self): + self.contract_doc.contract_terms = self.contract_template_with_requirements + self.contract_doc.insert() + + self.contract_doc.reload() + + self.assertEqual(self.contract_doc.fulfilment_status, "Lapsed") diff --git a/erpnext/crm/doctype/contract/test_records.json b/erpnext/crm/doctype/contract/test_records.json new file mode 100644 index 00000000000..23cc3bb82cb --- /dev/null +++ b/erpnext/crm/doctype/contract/test_records.json @@ -0,0 +1,20 @@ +[ + { + "doctype": "Contract", + "party_type": "Customer", + "party_name": "_Test Customer", + "contract_terms": "This is a test customer contract." + }, + { + "doctype": "Contract", + "party_type": "Supplier", + "party_name": "_Test Supplier", + "contract_terms": "This is a test supplier contract." + }, + { + "doctype": "Contract", + "party_type": "Employee", + "party_name": "_Test Employee", + "contract_terms": "This is a test employee contract." + } +] \ No newline at end of file diff --git a/erpnext/crm/doctype/contract_template/test_records.json b/erpnext/crm/doctype/contract_template/test_records.json new file mode 100644 index 00000000000..341b1cf1073 --- /dev/null +++ b/erpnext/crm/doctype/contract_template/test_records.json @@ -0,0 +1,31 @@ +[ + { + "doctype": "Contract Template", + "title": "_Test Customer Contract", + "contract_terms": "This is a test customer contract." + }, + { + "doctype": "Contract Template", + "title": "_Test Customer Contract with Requirements", + "contract_terms": "This is a test customer contract.", + "requires_fulfilment": 1, + "fulfilment_terms": [ + { + "requirement": "This is a test requirement." + }, + { + "requirement": "This is another test requirement." + } + ] + }, + { + "doctype": "Contract Template", + "title": "_Test Supplier Contract", + "contract_terms": "This is a test supplier contract." + }, + { + "doctype": "Contract Template", + "title": "_Test Employee Contract", + "contract_terms": "This is a test employee contract." + } +] \ No newline at end of file