Merge pull request #19979 from deepeshgarg007/sales_register_upgrade

fix: Update sales register report
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index f73fb10..29d8378 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -1,5 +1,4 @@
 {
- "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "autoname": "field:title",
@@ -390,8 +389,7 @@
    "fieldname": "rate_or_discount",
    "fieldtype": "Select",
    "label": "Rate or Discount",
-   "options": "\nRate\nDiscount Percentage\nDiscount Amount",
-   "reqd": 1
+   "options": "\nRate\nDiscount Percentage\nDiscount Amount"
   },
   {
    "default": "Grand Total",
@@ -440,7 +438,7 @@
   },
   {
    "default": "0",
-   "depends_on": "eval:!doc.mixed_conditions && doc.price_or_product_discount == 'Price'",
+   "depends_on": "eval:!doc.mixed_conditions && doc.apply_on != 'Transaction'",
    "fieldname": "same_item",
    "fieldtype": "Check",
    "label": "Same Item"
@@ -556,8 +554,7 @@
  ],
  "icon": "fa fa-gift",
  "idx": 1,
- "links": [],
- "modified": "2019-12-13 15:48:48.331495",
+ "modified": "2019-12-18 17:29:22.957077",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Pricing Rule",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index b99c07e..3c14819 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -47,6 +47,9 @@
 		if tocheck and not self.get(tocheck):
 			throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
 
+		if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
+			throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
+
 	def validate_applicable_for_selling_or_buying(self):
 		if not self.selling and not self.buying:
 			throw(_("Atleast one of the Selling or Buying must be selected"))
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index feb598a..bb1b7e3 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -90,8 +90,12 @@
 		else:
 			merged_gl_map.append(entry)
 
+	company = gl_map[0].company if gl_map else erpnext.get_default_company()
+	company_currency = erpnext.get_company_currency(company)
+	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
+
 	# filter zero debit and credit entries
-	merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map)
+	merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
 	merged_gl_map = list(merged_gl_map)
 
 	return merged_gl_map
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 2c53f6e..c70a2cd 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -171,7 +171,7 @@
 			row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
 			row.invoice_grand_total = row.invoiced
 
-			if abs(row.outstanding) > 0.1/10 ** self.currency_precision:
+			if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
 				# non-zero oustanding, we must consider this row
 
 				if self.is_invoice(row) and self.filters.based_on_payment_terms:
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.html b/erpnext/hr/notification/training_scheduled/training_scheduled.html
index b1aeb2c..374038a 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.html
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.html
@@ -1,9 +1,44 @@
-<h3>{{_("Training Event")}}</h3>
+<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
+    <tr height="10"></tr>
+    <tr>
+        <td width="15"></td>
+        <td>
+            <div class="text-medium text-muted">
+                <span>{{_("Training Event:")}} {{ doc.event_name }}</span>
+            </div>
+        </td>
+        <td width="15"></td>
+    </tr>
+    <tr height="10"></tr>
+</table>
 
-<p>{{ doc.introduction }}</p>
-
-<h4>{{_("Details")}}</h4>
-{{_("Event Name")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
-<br>{{_("Event Location")}}: {{ doc.location }}
-<br>{{_("Start Time")}}: {{ doc.start_time }}
-<br>{{_("End Time")}}: {{ doc.end_time }}
+<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
+    <tr height="10"></tr>
+    <tr>
+        <td width="15"></td>
+        <td>
+            <div>
+                {{ doc.introduction }}
+                <ul class="list-unstyled" style="line-height: 1.7">
+                    <li>{{_("Event Location")}}: <b>{{ doc.location }}</b></li>
+                    {% set start = frappe.utils.get_datetime(doc.start_time) %}
+                    {% set end = frappe.utils.get_datetime(doc.end_time) %}
+                    {% if start.date() == end.date() %}
+                    <li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
+                    <li>
+                        {{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
+                    </li>
+                    {% else %}
+                    <li>{{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+                    </li>
+                    <li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+                    </li>
+                    {% endif %}
+                    <li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
+                </ul>
+            </div>
+        </td>
+        <td width="15"></td>
+    </tr>
+    <tr height="10"></tr>
+</table>
\ No newline at end of file
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.json b/erpnext/hr/notification/training_scheduled/training_scheduled.json
index c07e1a6..966b887 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.json
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.json
@@ -1,5 +1,7 @@
 {
  "attach_print": 0,
+ "channel": "Email",
+ "condition": "",
  "creation": "2017-08-11 03:13:40.519614",
  "days_in_advance": 0,
  "docstatus": 0,
@@ -9,8 +11,8 @@
  "event": "Submit",
  "idx": 0,
  "is_standard": 1,
- "message": "<h3>{{_(\"Training Event\")}}</h3>\n\n<p>{{ doc.introduction }}</p>\n\n<h4>{{_(\"Details\")}}</h4>\n{{_(\"Event Name\")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n<br>{{_(\"Event Location\")}}: {{ doc.location }}\n<br>{{_(\"Start Time\")}}: {{ doc.start_time }}\n<br>{{_(\"End Time\")}}: {{ doc.end_time }}\n",
- "modified": "2017-08-13 22:49:42.338881",
+ "message": "<table class=\"panel-header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n    <tr height=\"10\"></tr>\n    <tr>\n        <td width=\"15\"></td>\n        <td>\n            <div class=\"text-medium text-muted\">\n                <span>{{_(\"Training Event:\")}} {{ doc.event_name }}</span>\n            </div>\n        </td>\n        <td width=\"15\"></td>\n    </tr>\n    <tr height=\"10\"></tr>\n</table>\n\n<table class=\"panel-body\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n    <tr height=\"10\"></tr>\n    <tr>\n        <td width=\"15\"></td>\n        <td>\n            <div>\n                <ul class=\"list-unstyled\" style=\"line-height: 1.7\">\n                    <li>{{ doc.introduction }}</li>\n                    <li>{{_(\"Event Location\")}}: <b>{{ doc.location }}</b></li>\n                    {% set start = frappe.utils.get_datetime(doc.start_time) %}\n                    {% set end = frappe.utils.get_datetime(doc.end_time) %}\n                    {% if start.date() == end.date() %}\n                    <li>{{_(\"Date\")}}: <b>{{ start.strftime(\"%A, %d %b %Y\") }}</b></li>\n                    <li>\n                        {{_(\"Timing\")}}: <b>{{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}</b>\n                    </li>\n                    {% else %}\n                    <li>{{_(\"Start Time\")}}: <b>{{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n                    </li>\n                    <li>{{_(\"End Time\")}}: <b>{{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n                    </li>\n                    {% endif %}\n                </ul>\n                {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n            </div>\n        </td>\n        <td width=\"15\"></td>\n    </tr>\n    <tr height=\"10\"></tr>\n</table>",
+ "modified": "2019-11-29 15:38:31.805409",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Training Scheduled",
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.md b/erpnext/hr/notification/training_scheduled/training_scheduled.md
index bcadf7d..374038a 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.md
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.md
@@ -1,9 +1,44 @@
-<h3>{{_("Training Event")}}</h3>
-<p>{{ message }}</p>
+<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
+    <tr height="10"></tr>
+    <tr>
+        <td width="15"></td>
+        <td>
+            <div class="text-medium text-muted">
+                <span>{{_("Training Event:")}} {{ doc.event_name }}</span>
+            </div>
+        </td>
+        <td width="15"></td>
+    </tr>
+    <tr height="10"></tr>
+</table>
 
-<h4>{{_("Details")}}</h4>
-{{_("Event Name")}}: <a href="{{ event_link }}">{{ name }}</a>
-<br>{{_("Event Location")}}: {{ location }}
-<br>{{_("Start Time")}}: {{ start_time }}
-<br>{{_("End Time")}}: {{ end_time }}
-<br>{{_("Attendance")}}: {{ attendance }}
+<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
+    <tr height="10"></tr>
+    <tr>
+        <td width="15"></td>
+        <td>
+            <div>
+                {{ doc.introduction }}
+                <ul class="list-unstyled" style="line-height: 1.7">
+                    <li>{{_("Event Location")}}: <b>{{ doc.location }}</b></li>
+                    {% set start = frappe.utils.get_datetime(doc.start_time) %}
+                    {% set end = frappe.utils.get_datetime(doc.end_time) %}
+                    {% if start.date() == end.date() %}
+                    <li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
+                    <li>
+                        {{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
+                    </li>
+                    {% else %}
+                    <li>{{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+                    </li>
+                    <li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+                    </li>
+                    {% endif %}
+                    <li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
+                </ul>
+            </div>
+        </td>
+        <td width="15"></td>
+    </tr>
+    <tr height="10"></tr>
+</table>
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 8ca8917..176ca2e 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -605,6 +605,8 @@
 				description: __('Max: {0}', [max]),
 				default: max
 			}, data => {
+				max += (max * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100;
+
 				if (data.qty > max) {
 					frappe.msgprint(__('Quantity must not be more than {0}', [max]));
 					reject();
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 227ef78..c4238ac 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -38,7 +38,7 @@
 		ms = frappe.get_doc("Manufacturing Settings")
 		self.set_onload("material_consumption", ms.material_consumption)
 		self.set_onload("backflush_raw_materials_based_on", ms.backflush_raw_materials_based_on)
-
+		self.set_onload("overproduction_percentage", ms.overproduction_percentage_for_work_order)
 
 	def validate(self):
 		self.validate_production_item()
@@ -657,8 +657,9 @@
 	wo_doc = frappe.new_doc("Work Order")
 	wo_doc.production_item = item
 	wo_doc.update(item_details)
-	if qty > 0:
-		wo_doc.qty = qty
+
+	if flt(qty) > 0:
+		wo_doc.qty = flt(qty)
 		wo_doc.get_items_and_operations_from_bom()
 
 	return wo_doc
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 7083d69..45f2681 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -47,11 +47,11 @@
 		if not self.project or frappe.flags.in_test:
 			return
 
-		expected_end_date = getdate(frappe.db.get_value("Project", self.project, "expected_end_date"))
+		expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
 
 		if expected_end_date:
-			validate_project_dates(expected_end_date, self, "exp_start_date", "exp_end_date", "Expected")
-			validate_project_dates(expected_end_date, self, "act_start_date", "act_end_date", "Actual")
+			validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
+			validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
 
 	def validate_status(self):
 		if self.status!=self.get_db_value("status") and self.status == "Completed":
@@ -278,4 +278,4 @@
 		frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
 
 	if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
-		frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
\ No newline at end of file
+		frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 1be4f27..6db849a 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -500,6 +500,9 @@
 								() => {
 									var d = locals[cdt][cdn];
 									me.add_taxes_from_item_tax_template(d.item_tax_rate);
+									if (d.free_item_data) {
+										me.apply_product_discount(d.free_item_data);
+									}
 								},
 								() => me.frm.script_manager.trigger("price_list_rate", cdt, cdn),
 								() => me.toggle_conversion_factor(item),
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index cca8efe..aa1b92f 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -5,13 +5,13 @@
 	setup: function(frm) {
 
 		frm.make_methods = {
-			'Quotation': () => erpnext.utils.create_new_doc('Quotation', {
-				'quotation_to': frm.doc.doctype,
-				'party_name': frm.doc.name
+			'Quotation': () => frappe.model.open_mapped_doc({
+				method: "erpnext.selling.doctype.customer.customer.make_quotation",
+				frm: cur_frm
 			}),
-			'Opportunity': () => erpnext.utils.create_new_doc('Opportunity', {
-				'opportunity_from': frm.doc.doctype,
-				'party_name': frm.doc.name
+			'Opportunity': () => frappe.model.open_mapped_doc({
+				method: "erpnext.selling.doctype.customer.customer.make_opportunity",
+				frm: cur_frm
 			})
 		}
 
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 57308ce..136236c 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -12,6 +12,7 @@
 from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
 from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
 from frappe.model.rename_doc import update_linked_doctypes
+from frappe.model.mapper import get_mapped_doc
 
 class Customer(TransactionBase):
 	def get_feed(self):
@@ -239,6 +240,66 @@
 	contact.insert()
 
 @frappe.whitelist()
+def make_quotation(source_name, target_doc=None):
+
+	def set_missing_values(source, target):
+		_set_missing_values(source, target)
+
+	target_doc = get_mapped_doc("Customer", source_name,
+		{"Customer": {
+			"doctype": "Quotation",
+			"field_map": {
+				"name":"party_name"
+			}
+		}}, target_doc, set_missing_values)
+
+	target_doc.quotation_to = "Customer"
+	target_doc.run_method("set_missing_values")
+	target_doc.run_method("set_other_charges")
+	target_doc.run_method("calculate_taxes_and_totals")
+
+	price_list = frappe.get_value("Customer", source_name, 'default_price_list')
+	if price_list:
+		target_doc.selling_price_list = price_list
+
+	return target_doc
+
+@frappe.whitelist()
+def make_opportunity(source_name, target_doc=None):
+	def set_missing_values(source, target):
+		_set_missing_values(source, target)
+
+	target_doc = get_mapped_doc("Customer", source_name,
+		{"Customer": {
+			"doctype": "Opportunity",
+			"field_map": {
+				"name": "party_name",
+				"doctype": "opportunity_from",
+			}
+		}}, target_doc, set_missing_values)
+
+	return target_doc
+
+def _set_missing_values(source, target):
+	address = frappe.get_all('Dynamic Link', {
+			'link_doctype': source.doctype,
+			'link_name': source.name,
+			'parenttype': 'Address',
+		}, ['parent'], limit=1)
+
+	contact = frappe.get_all('Dynamic Link', {
+			'link_doctype': source.doctype,
+			'link_name': source.name,
+			'parenttype': 'Contact',
+		}, ['parent'], limit=1)
+
+	if address:
+		target.customer_address = address[0].parent
+
+	if contact:
+		target.contact_person = contact[0].parent
+
+@frappe.whitelist()
 def get_loyalty_programs(doc):
 	''' returns applicable loyalty programs for a customer '''
 	from frappe.desk.treeview import get_children