Merge pull request #3051 from rmehta/opportunity

Opportunity
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index 5d0c20d..1baed41 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -5,12 +5,11 @@
 import frappe
 from frappe import msgprint, _
 from frappe.utils import getdate, add_days, add_years, cstr
-from datetime import timedelta
+from dateutil.relativedelta import relativedelta
 
 from frappe.model.document import Document
 
 class FiscalYear(Document):
-
 	def set_as_default(self):
 		frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name)
 		frappe.get_doc("Global Defaults").on_update()
@@ -24,18 +23,21 @@
 		year_start_end_dates = frappe.db.sql("""select year_start_date, year_end_date
 			from `tabFiscal Year` where name=%s""", (self.name))
 
+		self.validate_dates()
+
 		if year_start_end_dates:
 			if getdate(self.year_start_date) != year_start_end_dates[0][0] or getdate(self.year_end_date) != year_start_end_dates[0][1]:
 				frappe.throw(_("Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."))
 
-	def on_update(self):
-		# validate year start date and year end date
+	def validate_dates(self):
 		if getdate(self.year_start_date) > getdate(self.year_end_date):
 			frappe.throw(_("Fiscal Year Start Date should not be greater than Fiscal Year End Date"))
 
 		if (getdate(self.year_end_date) - getdate(self.year_start_date)).days > 366:
-			frappe.throw(_("Fiscal Year Start Date and Fiscal Year End Date cannot be more than a year apart."))
+			date = getdate(self.year_start_date) + relativedelta(years=1) - relativedelta(days=1)
+			self.year_end_date = date.strftime("%Y-%m-%d")
 
+	def on_update(self):
 		check_duplicate_fiscal_year(self)
 
 @frappe.whitelist()
diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
index 9d524ac..092d34a 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -3,6 +3,20 @@
 
 from __future__ import unicode_literals
 
-import frappe
+import frappe, unittest
 
-test_records = frappe.get_test_records('Fiscal Year')
\ No newline at end of file
+test_records = frappe.get_test_records('Fiscal Year')
+
+class TestFiscalYear(unittest.TestCase):
+	def test_extra_year(self):
+		if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"):
+			frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000")
+		fy = frappe.get_doc({
+			"doctype": "Fiscal Year",
+			"year": "_Test Fiscal Year 2000",
+			"year_end_date": "2002-12-31",
+			"year_start_date": "2000-04-01"
+		})
+		fy.insert()
+		self.assertEquals(fy.year_end_date, '2001-03-31')
+
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index 009cfc2..0fb8d48 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -33,6 +33,7 @@
 		})
 
 		self.set_status()
+		self.check_email_id_is_unique()
 
 		if self.source == 'Campaign' and not self.campaign_name and session['user'] != 'Guest':
 			frappe.throw(_("Campaign Name is required"))
@@ -45,7 +46,6 @@
 				self.lead_owner = None
 
 	def on_update(self):
-		self.check_email_id_is_unique()
 		self.add_calendar_event()
 
 	def add_calendar_event(self, opts=None, force=False):
@@ -62,9 +62,10 @@
 			# validate email is unique
 			email_list = frappe.db.sql("""select name from tabLead where email_id=%s""",
 				self.email_id)
+			email_list = [e[0] for e in email_list if e[0]!=self.name]
 			if len(email_list) > 1:
-				items = [e[0] for e in email_list if e[0]!=self.name]
-				frappe.throw(_("Email id must be unique, already exists for {0}").format(comma_and(items)), frappe.DuplicateEntryError)
+				frappe.throw(_("Email id must be unique, already exists for {0}").format(comma_and(email_list)),
+					frappe.DuplicateEntryError)
 
 	def on_trash(self):
 		frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index 186e866..0b25ad6 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -92,12 +92,20 @@
    "width": "50%"
   }, 
   {
+   "fieldname": "title", 
+   "fieldtype": "Data", 
+   "label": "Title", 
+   "permlevel": 0, 
+   "precision": ""
+  }, 
+  {
+   "default": "Sales", 
    "fieldname": "enquiry_type", 
    "fieldtype": "Select", 
    "label": "Opportunity Type", 
    "oldfieldname": "enquiry_type", 
    "oldfieldtype": "Select", 
-   "options": "\nSales\nMaintenance", 
+   "options": "Sales\nMaintenance", 
    "permlevel": 0, 
    "read_only": 0, 
    "reqd": 1
@@ -118,6 +126,14 @@
    "reqd": 1
   }, 
   {
+   "fieldname": "with_items", 
+   "fieldtype": "Check", 
+   "label": "With Items", 
+   "permlevel": 0, 
+   "precision": ""
+  }, 
+  {
+   "depends_on": "with_items", 
    "fieldname": "items_section", 
    "fieldtype": "Section Break", 
    "label": "", 
@@ -127,7 +143,7 @@
    "read_only": 0
   }, 
   {
-   "description": "Items which do not exist in Item master can also be entered on customer's request", 
+   "description": "", 
    "fieldname": "items", 
    "fieldtype": "Table", 
    "label": "Items", 
@@ -391,7 +407,7 @@
  "icon": "icon-info-sign", 
  "idx": 1, 
  "is_submittable": 1, 
- "modified": "2015-02-23 02:19:39.853388", 
+ "modified": "2015-04-02 22:03:52.841173", 
  "modified_by": "Administrator", 
  "module": "CRM", 
  "name": "Opportunity", 
@@ -432,5 +448,5 @@
  "search_fields": "status,transaction_date,customer,lead,enquiry_type,territory,company", 
  "sort_field": "modified", 
  "sort_order": "DESC", 
- "title_field": "customer_name"
+ "title_field": "title"
 }
\ No newline at end of file
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index b6909aa..80b35f2 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -10,6 +10,23 @@
 from erpnext.utilities.transaction_base import TransactionBase
 
 class Opportunity(TransactionBase):
+	def set_sender(self, email_id):
+		"""Set lead against new opportunity"""
+		lead_name = frappe.get_value("Lead", {"email_id": email_id})
+		if not lead_name:
+			lead = frappe.get_doc({
+				"doctype": "Lead",
+				"email_id": email_id,
+				"lead_name": email_id
+			})
+			lead.insert(ignore_permissions=True)
+			lead_name = lead.name
+
+		self.enquiry_from = "Lead"
+		self.lead = lead_name
+
+	def set_subject(self, subject):
+		self.title = subject
 
 	def validate(self):
 		self._prev = frappe._dict({
@@ -28,6 +45,9 @@
 		self.validate_lead_cust()
 		self.validate_cust_name()
 
+		if not self.title:
+			self.title = self.customer_name
+
 		from erpnext.accounts.utils import validate_fiscal_year
 		validate_fiscal_year(self.transaction_date, self.fiscal_year, _("Opportunity Date"), self)
 
@@ -118,7 +138,7 @@
 
 	def validate_item_details(self):
 		if not self.get('items'):
-			frappe.throw(_("Items required"))
+			return
 
 		# set missing values
 		item_fields = ("item_name", "description", "item_group", "brand")
diff --git a/erpnext/crm/doctype/opportunity_item/__init__.py b/erpnext/crm/doctype/opportunity_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/opportunity_item/__init__.py
diff --git a/erpnext/selling/doctype/opportunity_item/opportunity_item.json b/erpnext/crm/doctype/opportunity_item/opportunity_item.json
similarity index 97%
rename from erpnext/selling/doctype/opportunity_item/opportunity_item.json
rename to erpnext/crm/doctype/opportunity_item/opportunity_item.json
index 889b05f..afc6fcb 100644
--- a/erpnext/selling/doctype/opportunity_item/opportunity_item.json
+++ b/erpnext/crm/doctype/opportunity_item/opportunity_item.json
@@ -133,9 +133,9 @@
  ], 
  "idx": 1, 
  "istable": 1, 
- "modified": "2015-02-23 02:09:55.105233", 
+ "modified": "2015-04-02 22:04:26.867653", 
  "modified_by": "Administrator", 
- "module": "Selling", 
+ "module": "CRM", 
  "name": "Opportunity Item", 
  "owner": "Administrator", 
  "permissions": []
diff --git a/erpnext/selling/doctype/opportunity_item/opportunity_item.py b/erpnext/crm/doctype/opportunity_item/opportunity_item.py
similarity index 67%
rename from erpnext/selling/doctype/opportunity_item/opportunity_item.py
rename to erpnext/crm/doctype/opportunity_item/opportunity_item.py
index 7aff5ca..7a5ed63 100644
--- a/erpnext/selling/doctype/opportunity_item/opportunity_item.py
+++ b/erpnext/crm/doctype/opportunity_item/opportunity_item.py
@@ -1,10 +1,10 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
+# For license information, please see license.txt
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
 
 class OpportunityItem(Document):
-	pass
\ No newline at end of file
+	pass
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 5d9dad0..a6a2e25 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -25,7 +25,7 @@
 # website
 update_website_context = "erpnext.shopping_cart.utils.update_website_context"
 my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
-email_append_to = ["Lead", "Job Applicant", "Opportunity", "Issue", "Warranty Claim"]
+email_append_to = ["Job Applicant", "Opportunity", "Issue"]
 
 website_route_rules = [
 	{"from_route": "/orders", "to_route": "Sales Order"},
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 78c75fe..3a7530e 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -135,4 +135,5 @@
 erpnext.patches.v5_0.newsletter
 execute:frappe.delete_doc("DocType", "Chart of Accounts")
 execute:frappe.delete_doc("DocType", "Style Settings")
+erpnext.patches.v5_0.update_opportunity
 
diff --git a/erpnext/patches/v5_0/update_opportunity.py b/erpnext/patches/v5_0/update_opportunity.py
new file mode 100644
index 0000000..8eb45c4
--- /dev/null
+++ b/erpnext/patches/v5_0/update_opportunity.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doc('crm', 'doctype', 'opportunity')
+	frappe.reload_doc('crm', 'doctype', 'opportunity_item')
+
+	# all existing opportunities were with items
+	frappe.db.sql("update `tabDocType` set module = 'CRM' where name='Opportunity Item'")
+	frappe.db.sql("update tabOpportunity set with_items=1, title=customer_name")
+	frappe.db.sql("update `tabEmail Account` set append_to='Opportunity' where append_to='Lead'")
diff --git a/erpnext/selling/doctype/opportunity_item/README.md b/erpnext/selling/doctype/opportunity_item/README.md
deleted file mode 100644
index 810c10b..0000000
--- a/erpnext/selling/doctype/opportunity_item/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Items considered in the parent Opportunity.
\ No newline at end of file
diff --git a/erpnext/selling/doctype/opportunity_item/__init__.py b/erpnext/selling/doctype/opportunity_item/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/selling/doctype/opportunity_item/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/setup/page/setup_wizard/install_fixtures.py b/erpnext/setup/page/setup_wizard/install_fixtures.py
index 3d583aa..7bbe139 100644
--- a/erpnext/setup/page/setup_wizard/install_fixtures.py
+++ b/erpnext/setup/page/setup_wizard/install_fixtures.py
@@ -151,7 +151,7 @@
 			{"attribute_value": _("White"), "abbr": "WHI"}
 		]},
 
-		{'doctype': "Email Account", "email_id": "sales@example.com", "append_to": "Lead"},
+		{'doctype': "Email Account", "email_id": "sales@example.com", "append_to": "Opportunity"},
 		{'doctype': "Email Account", "email_id": "support@example.com", "append_to": "Issue"},
 		{'doctype': "Email Account", "email_id": "jobs@example.com", "append_to": "Job Applicant"}
 	]
diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json
index eea90fd..8ba1b5a 100644
--- a/erpnext/support/doctype/issue/issue.json
+++ b/erpnext/support/doctype/issue/issue.json
@@ -64,6 +64,7 @@
    "label": "Raised By (Email)", 
    "oldfieldname": "raised_by", 
    "oldfieldtype": "Data", 
+   "options": "Email", 
    "permlevel": 0, 
    "reqd": 1
   }, 
@@ -217,7 +218,7 @@
  ], 
  "icon": "icon-ticket", 
  "idx": 1, 
- "modified": "2015-02-05 05:11:39.362659", 
+ "modified": "2015-04-02 22:06:02.684820", 
  "modified_by": "Administrator", 
  "module": "Support", 
  "name": "Issue",