fix: option to limit reposting in certain timeslot (#27725)

diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 62b3a6a..d86e52f 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -7,7 +7,7 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form, now, today
+from frappe.utils import cint, get_link_to_form, get_weekday, now, nowtime, today
 from frappe.utils.user import get_users_with_role
 from rq.timeouts import JobTimeoutException
 
@@ -126,6 +126,9 @@
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
 def repost_entries():
+	if not in_configured_timeslot():
+		return
+
 	riv_entries = get_repost_item_valuation_entries()
 
 	for row in riv_entries:
@@ -144,3 +147,26 @@
 		WHERE status in ('Queued', 'In Progress') and creation <= %s and docstatus = 1
 		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
 	""", now(), as_dict=1)
+
+
+def in_configured_timeslot(repost_settings=None, current_time=None):
+	"""Check if current time is in configured timeslot for reposting."""
+
+	if repost_settings is None:
+		repost_settings = frappe.get_cached_doc("Stock Reposting Settings")
+
+	if not repost_settings.limit_reposting_timeslot:
+		return True
+
+	if get_weekday() == repost_settings.limits_dont_apply_on:
+		return True
+
+	start_time = repost_settings.start_time
+	end_time = repost_settings.end_time
+
+	now_time = current_time or nowtime()
+
+	if start_time < end_time:
+		return end_time >= now_time >= start_time
+	else:
+		return now_time >= start_time or now_time <= end_time
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index c70a9ec..c086f93 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -1,11 +1,72 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
-from __future__ import unicode_literals
 
-# import frappe
 import unittest
 
+import frappe
+
+from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
+	in_configured_timeslot,
+)
+
 
 class TestRepostItemValuation(unittest.TestCase):
-	pass
+	def test_repost_time_slot(self):
+		repost_settings = frappe.get_doc("Stock Reposting Settings")
+
+		positive_cases = [
+			{"limit_reposting_timeslot": 0},
+			{
+				"limit_reposting_timeslot": 1,
+				"start_time": "18:00:00",
+				"end_time": "09:00:00",
+				"current_time": "20:00:00",
+			},
+			{
+				"limit_reposting_timeslot": 1,
+				"start_time": "09:00:00",
+				"end_time": "18:00:00",
+				"current_time": "12:00:00",
+			},
+			{
+				"limit_reposting_timeslot": 1,
+				"start_time": "23:00:00",
+				"end_time": "09:00:00",
+				"current_time": "2:00:00",
+			},
+		]
+
+		for case in positive_cases:
+			repost_settings.update(case)
+			self.assertTrue(
+				in_configured_timeslot(repost_settings, case.get("current_time")),
+				msg=f"Exepcted true from : {case}",
+			)
+
+		negative_cases = [
+			{
+				"limit_reposting_timeslot": 1,
+				"start_time": "18:00:00",
+				"end_time": "09:00:00",
+				"current_time": "09:01:00",
+			},
+			{
+				"limit_reposting_timeslot": 1,
+				"start_time": "09:00:00",
+				"end_time": "18:00:00",
+				"current_time": "19:00:00",
+			},
+			{
+				"limit_reposting_timeslot": 1,
+				"start_time": "23:00:00",
+				"end_time": "09:00:00",
+				"current_time": "22:00:00",
+			},
+		]
+
+		for case in negative_cases:
+			repost_settings.update(case)
+			self.assertFalse(
+				in_configured_timeslot(repost_settings, case.get("current_time")),
+				msg=f"Exepcted false from : {case}",
+			)
diff --git a/erpnext/stock/doctype/stock_reposting_settings/__init__.py b/erpnext/stock/doctype/stock_reposting_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/__init__.py
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js
new file mode 100644
index 0000000..42d0723
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Stock Reposting Settings', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
new file mode 100644
index 0000000..2474059
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -0,0 +1,72 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2021-10-01 10:56:30.814787",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "scheduling_section",
+  "limit_reposting_timeslot",
+  "start_time",
+  "end_time",
+  "limits_dont_apply_on"
+ ],
+ "fields": [
+  {
+   "fieldname": "scheduling_section",
+   "fieldtype": "Section Break",
+   "label": "Scheduling"
+  },
+  {
+   "depends_on": "limit_reposting_timeslot",
+   "fieldname": "start_time",
+   "fieldtype": "Time",
+   "label": "Start Time",
+   "mandatory_depends_on": "limit_reposting_timeslot"
+  },
+  {
+   "depends_on": "limit_reposting_timeslot",
+   "fieldname": "end_time",
+   "fieldtype": "Time",
+   "label": "End Time",
+   "mandatory_depends_on": "limit_reposting_timeslot"
+  },
+  {
+   "depends_on": "limit_reposting_timeslot",
+   "fieldname": "limits_dont_apply_on",
+   "fieldtype": "Select",
+   "label": "Limits don't apply on",
+   "options": "\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday"
+  },
+  {
+   "default": "0",
+   "fieldname": "limit_reposting_timeslot",
+   "fieldtype": "Check",
+   "label": "Limit timeslot for Stock Reposting"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-10-01 11:27:28.981594",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Reposting Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py
new file mode 100644
index 0000000..bab521d
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from frappe.model.document import Document
+from frappe.utils import add_to_date, get_datetime, get_time_str, time_diff_in_hours
+
+
+class StockRepostingSettings(Document):
+
+
+	def validate(self):
+		self.set_minimum_reposting_time_slot()
+
+	def set_minimum_reposting_time_slot(self):
+		"""Ensure that timeslot for reposting is at least 12 hours."""
+		if not self.limit_reposting_timeslot:
+			return
+
+		start_time = get_datetime(self.start_time)
+		end_time = get_datetime(self.end_time)
+
+		if start_time > end_time:
+			end_time = add_to_date(end_time, days=1, as_datetime=True)
+
+		diff = time_diff_in_hours(end_time, start_time)
+
+		if diff < 10:
+			self.end_time = get_time_str(add_to_date(self.start_time, hours=10, as_datetime=True))
diff --git a/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
new file mode 100644
index 0000000..fad74d3
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestStockRepostingSettings(unittest.TestCase):
+	pass