Merge branch 'develop' into fix-scrap-items-updation
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 8addbeb..01d354d 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1836,6 +1836,11 @@
 
 	for d in data:
 		new_child_flag = False
+
+		if not d.get("item_code"):
+			# ignore empty rows
+			continue
+
 		if not d.get("docname"):
 			new_child_flag = True
 			check_doc_permissions(parent, 'create')
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index f1b9235..f240b8c 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -563,7 +563,7 @@
 			},
 		],
 		primary_action: function() {
-			const trans_items = this.get_values()["trans_items"];
+			const trans_items = this.get_values()["trans_items"].filter((item) => !!item.item_code);
 			frappe.call({
 				method: 'erpnext.controllers.accounts_controller.update_child_qty_rate',
 				freeze: true,
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index e6ce3c8..2f37778 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -84,8 +84,6 @@
    "oldfieldtype": "Section Break"
   },
   {
-   "allow_on_submit": 1,
-   "default": "{purpose}",
    "fieldname": "title",
    "fieldtype": "Data",
    "hidden": 1,
@@ -630,7 +628,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-08-17 20:16:12.737743",
+ "modified": "2021-08-20 19:19:31.514846",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 90a33d3..0b4592c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -58,6 +58,7 @@
 
 		self.validate_posting_time()
 		self.validate_purpose()
+		self.set_title()
 		self.validate_item()
 		self.validate_customer_provided_item()
 		self.validate_qty()
@@ -1608,6 +1609,14 @@
 
 		return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos)))
 
+	def set_title(self):
+		if frappe.flags.in_import and self.title:
+			# Allow updating title during data import/update
+			return
+
+		self.title = self.purpose
+
+
 @frappe.whitelist()
 def move_sample_to_retention_warehouse(company, items):
 	if isinstance(items, string_types):
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js
index 6b384e2..78afe6d 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.js
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.js
@@ -37,11 +37,25 @@
 			default: "",
 		},
 		{
+			fieldname: "company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1,
+		},
+		{
 			fieldname: "warehouse",
 			label: __("Warehouse"),
 			fieldtype: "Link",
-			options:"Warehouse",
+			options: "Warehouse",
 			default: "",
+			get_query: function() {
+				const company = frappe.query_report.get_filter_value('company');
+				return {
+					filters: { 'company': company }
+				}
+			}
 		},
 		{
 			fieldname: "from_date",
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py
index d62abed..a1e1e7f 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.py
@@ -1,14 +1,15 @@
 # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
+import datetime
 
-from __future__ import unicode_literals
 import frappe
 from frappe import _, scrub
-from frappe.utils import getdate, flt
+from frappe.utils import getdate, get_quarter_start, get_first_day_of_week
+from frappe.utils import get_first_day as get_first_day_of_month
+
 from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details)
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.stock.utils import is_reposting_item_valuation_in_progress
-from six import iteritems
 
 def execute(filters=None):
 	is_reposting_item_valuation_in_progress()
@@ -71,7 +72,8 @@
 
 def get_period_date_ranges(filters):
 		from dateutil.relativedelta import relativedelta
-		from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
+		from_date = round_down_to_nearest_frequency(filters.from_date, filters.range)
+		to_date = getdate(filters.to_date)
 
 		increment = {
 			"Monthly": 1,
@@ -97,6 +99,31 @@
 
 		return periodic_daterange
 
+
+def round_down_to_nearest_frequency(date: str, frequency: str) -> datetime.datetime:
+	"""Rounds down the date to nearest frequency unit.
+	example:
+
+	>>> round_down_to_nearest_frequency("2021-02-21", "Monthly")
+	datetime.datetime(2021, 2, 1)
+
+	>>> round_down_to_nearest_frequency("2021-08-21", "Yearly")
+	datetime.datetime(2021, 1, 1)
+	"""
+
+	def _get_first_day_of_fiscal_year(date):
+		fiscal_year = get_fiscal_year(date)
+		return fiscal_year and fiscal_year[1] or date
+
+	round_down_function = {
+		"Monthly": get_first_day_of_month,
+		"Quarterly": get_quarter_start,
+		"Weekly": get_first_day_of_week,
+		"Yearly": _get_first_day_of_fiscal_year,
+	}.get(frequency, getdate)
+	return round_down_function(date)
+
+
 def get_period(posting_date, filters):
 	months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
 
@@ -177,7 +204,7 @@
 	periodic_data = get_periodic_data(sle, filters)
 	ranges = get_period_date_ranges(filters)
 
-	for dummy, item_data in iteritems(item_details):
+	for dummy, item_data in item_details.items():
 		row = {
 			"name": item_data.name,
 			"item_name": item_data.item_name,
diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
new file mode 100644
index 0000000..00e268b
--- /dev/null
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -0,0 +1,35 @@
+import datetime
+import unittest
+
+from frappe import _dict
+from erpnext.accounts.utils import get_fiscal_year
+
+from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
+
+
+class TestStockAnalyticsReport(unittest.TestCase):
+	def test_get_period_date_ranges(self):
+
+		filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
+
+		ranges = get_period_date_ranges(filters)
+
+		expected_ranges = [
+			[datetime.date(2020, 12, 1), datetime.date(2020, 12, 31)],
+			[datetime.date(2021, 1, 1), datetime.date(2021, 1, 31)],
+			[datetime.date(2021, 2, 1), datetime.date(2021, 2, 6)],
+		]
+
+		self.assertEqual(ranges, expected_ranges)
+
+	def test_get_period_date_ranges_yearly(self):
+
+		filters = _dict(range="Yearly", from_date="2021-01-28", to_date="2021-02-06")
+
+		ranges = get_period_date_ranges(filters)
+		first_date = get_fiscal_year("2021-01-28")[1]
+		expected_ranges = [
+			[first_date, datetime.date(2021, 2, 6)],
+		]
+
+		self.assertEqual(ranges, expected_ranges)