Merge branch 'develop' into help-links-fix
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 76e0092..92fa5ef 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -376,6 +376,12 @@
 				"campaign": profile.get("campaign"),
 				"allow_print_before_pay": profile.get("allow_print_before_pay")
 			}
+	
+	def reset_mode_of_payments(self):
+		if self.pos_profile:
+			pos_profile = frappe.get_cached_doc('POS Profile', self.pos_profile)
+			update_multi_mode_option(self, pos_profile)
+			self.paid_amount = 0
 
 	def set_account_for_mode_of_payment(self):
 		self.payments = [d for d in self.payments if d.amount or d.base_amount or d.default]
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 7dfce85..14efa1f 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -51,7 +51,11 @@
 			"from_date": start_date
 		})
 
-		to_date = add_months(start_date, months_to_add)
+		if i==0 and filter_based_on == 'Date Range':
+			to_date = add_months(get_first_day(start_date), months_to_add)
+		else:
+			to_date = add_months(start_date, months_to_add)
+
 		start_date = to_date
 
 		# Subtract one day from to_date, as it may be first day in next fiscal year or month
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index 47b05f3..0522ace 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -248,7 +248,6 @@
 			"doctype": "Quotation",
 			"field_map": {
 				"opportunity_from": "quotation_to",
-				"opportunity_type": "order_type",
 				"name": "enq_no",
 			}
 		},
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.json b/erpnext/education/doctype/student_attendance/student_attendance.json
index 55384b9..e6e46d1 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.json
+++ b/erpnext/education/doctype/student_attendance/student_attendance.json
@@ -10,6 +10,7 @@
   "naming_series",
   "student",
   "student_name",
+  "student_mobile_number",
   "course_schedule",
   "student_group",
   "column_break_3",
@@ -93,11 +94,19 @@
    "options": "Student Attendance",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fetch_from": "student.student_mobile_number",
+   "fieldname": "student_mobile_number",
+   "fieldtype": "Read Only",
+   "label": "Student Mobile Number",
+   "options": "Phone"
   }
  ],
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-07-08 13:55:42.580181",
+ "modified": "2021-03-24 00:02:11.005895",
  "modified_by": "Administrator",
  "module": "Education",
  "name": "Student Attendance",
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
index 5ced845..aaf0e85 100644
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
+++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
@@ -53,7 +53,7 @@
   "discharge_ordered_date",
   "discharge_practitioner",
   "discharge_encounter",
-  "discharge_date",
+  "discharge_datetime",
   "cb_discharge",
   "discharge_instructions",
   "followup_date",
@@ -404,14 +404,15 @@
    "permlevel": 1
   },
   {
-   "fieldname": "discharge_date",
-   "fieldtype": "Date",
+   "fieldname": "discharge_datetime",
+   "fieldtype": "Datetime",
    "label": "Discharge Date",
    "read_only": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-05-21 02:26:22.144575",
+ "modified": "2021-03-18 14:44:11.689956",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Inpatient Record",
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
index 88d7f0b..2934316 100644
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
@@ -151,7 +151,7 @@
 
 def discharge_patient(inpatient_record):
 	validate_inpatient_invoicing(inpatient_record)
-	inpatient_record.discharge_date = today()
+	inpatient_record.discharge_datetime = now_datetime()
 	inpatient_record.status = "Discharged"
 
 	inpatient_record.save(ignore_permissions = True)
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 4b3597a..9b9a0da 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -324,6 +324,7 @@
 		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
 		"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
 		"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
+		"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
 	],
 	"daily": [
 		"erpnext.stock.reorder_item.reorder_item",
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 13a2094..4b9a894 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -275,6 +275,11 @@
 		frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
 			where loan_security='Test Security 2'""")
 
+		create_process_loan_security_shortfall()
+		loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
+		self.assertEquals(loan_security_shortfall.status, "Completed")
+		self.assertEquals(loan_security_shortfall.shortfall_amount, 0)
+
 	def test_loan_security_unpledge(self):
 		pledge = [{
 			"loan_security": "Test Security 1",
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 6469806..b5e7898 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -55,6 +55,9 @@
 		'total_interest_payable', 'disbursed_amount', 'status'],
 		filters={'status': ('in',['Disbursed','Partially Disbursed']), 'is_secured_loan': 1})
 
+	loan_shortfall_map = frappe._dict(frappe.get_all("Loan Security Shortfall",
+		fields=["loan", "name"], filters={"status": "Pending"}, as_list=1))
+
 	loan_security_map = {}
 
 	for loan in loans:
@@ -71,14 +74,19 @@
 		for security, qty in pledged_securities.items():
 			if not ltv_ratio:
 				ltv_ratio = get_ltv_ratio(security)
-			security_value += loan_security_price_map.get(security) * qty
+			security_value += flt(loan_security_price_map.get(security)) * flt(qty)
 
-		current_ratio = (outstanding_amount/security_value) * 100
+		current_ratio = (outstanding_amount/security_value) * 100 if security_value else 0
 
 		if current_ratio > ltv_ratio:
 			shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
 			create_loan_security_shortfall(loan.name, outstanding_amount, security_value, shortfall_amount,
 				process_loan_security_shortfall)
+		elif loan_shortfall_map.get(loan.name):
+			shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
+			if shortfall_amount <= 0:
+				shortfall = loan_shortfall_map.get(loan.name)
+				update_pending_shortfall(shortfall)
 
 def create_loan_security_shortfall(loan, loan_amount, security_value, shortfall_amount, process_loan_security_shortfall):
 	existing_shortfall = frappe.db.get_value("Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name")
@@ -101,3 +109,11 @@
 	ltv_ratio = frappe.db.get_value('Loan Security Type', loan_security_type, 'loan_to_value_ratio')
 	return ltv_ratio
 
+def update_pending_shortfall(shortfall):
+	# Get all pending loan security shortfall
+	frappe.db.set_value("Loan Security Shortfall", shortfall,
+		{
+			"status": "Completed",
+			"shortfall_amount": 0
+		})
+
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 00e8c54..08291d1 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -82,7 +82,7 @@
 		wo_order.set_work_order_operations()
 		self.assertEqual(wo_order.planned_operating_cost, cost*2)
 
-	def test_resered_qty_for_partial_completion(self):
+	def test_reserved_qty_for_partial_completion(self):
 		item = "_Test Item"
 		warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC")
 
@@ -109,7 +109,7 @@
 		s.submit()
 
 		bin1_at_completion = get_bin(item, warehouse)
-		
+
 		self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
 			reserved_qty_on_submission - 1)
 
@@ -592,6 +592,55 @@
 
 		frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
 
+	def test_make_stock_entry_for_customer_provided_item(self):
+		finished_item = 'Test Item for Make Stock Entry 1'
+		make_item(finished_item, {
+				"include_item_in_manufacturing": 1,
+				"is_stock_item": 1
+			})
+
+		customer_provided_item = 'CUST-0987'
+		make_item(customer_provided_item, {
+			'is_purchase_item': 0,
+			'is_customer_provided_item': 1,
+			"is_stock_item": 1,
+			"include_item_in_manufacturing": 1,
+			'customer': '_Test Customer'
+		})
+
+		if not frappe.db.exists('BOM', {'item': finished_item}):
+			make_bom(item=finished_item, raw_materials=[customer_provided_item], rm_qty=1)
+
+		company = "_Test Company with perpetual inventory"
+		customer_warehouse = create_warehouse("Test Customer Provided Warehouse", company=company)
+		wo = make_wo_order_test_record(item=finished_item, qty=1, source_warehouse=customer_warehouse,
+			company=company)
+
+		ste = frappe.get_doc(make_stock_entry(wo.name, purpose='Material Transfer for Manufacture'))
+		ste.insert()
+
+		self.assertEqual(len(ste.items), 1)
+		for item in ste.items:
+			self.assertEqual(item.allow_zero_valuation_rate, 1)
+			self.assertEqual(item.valuation_rate, 0)
+
+	def test_valuation_rate_missing_on_make_stock_entry(self):
+		item_name = 'Test Valuation Rate Missing'
+		make_item(item_name, {
+			"is_stock_item": 1,
+			"include_item_in_manufacturing": 1,
+		})
+
+		if not frappe.db.get_value('BOM', {'item': item_name}):
+			make_bom(item=item_name, raw_materials=[item_name], rm_qty=1)
+
+		company = "_Test Company with perpetual inventory"
+		source_warehouse = create_warehouse("Test Valuation Rate Missing Warehouse", company=company)
+		wo = make_wo_order_test_record(item=item_name, qty=1, source_warehouse=source_warehouse,
+			company=company)
+
+		self.assertRaises(frappe.ValidationError, make_stock_entry, wo.name, 'Material Transfer for Manufacture')
+
 def get_scrap_item_details(bom_no):
 	scrap_items = {}
 	for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
@@ -609,6 +658,15 @@
 
 def make_wo_order_test_record(**args):
 	args = frappe._dict(args)
+	if args.company and args.company != "_Test Company":
+		warehouse_map = {
+			"fg_warehouse": "_Test FG Warehouse",
+			"wip_warehouse": "_Test WIP Warehouse"
+		}
+
+		for attr, wh_name in warehouse_map.items():
+			if not args.get(attr):
+				args[attr] = create_warehouse(wh_name, company=args.company)
 
 	wo_order = frappe.new_doc("Work Order")
 	wo_order.production_item = args.production_item or args.item or args.item_code or "_Test FG Item"
diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
index 2ca9f16..fc27d35 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
@@ -61,7 +61,7 @@
 
 		from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1)
 		self.period_list = get_period_list(from_date, self.filters.to_date,
-			from_date, self.filters.to_date, None, self.filters.periodicity, ignore_fiscal_year=True)
+			from_date, self.filters.to_date, "Date Range", self.filters.periodicity, ignore_fiscal_year=True)
 
 		order_data = self.get_data_for_forecast() or []
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 7af5ad1..46f0d4a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -759,4 +759,6 @@
 erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
 erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
 erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
+erpnext.patches.v13_0.setup_uae_vat_fields
 execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
+erpnext.patches.v13_0.rename_discharge_date_in_ip_record
diff --git a/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py b/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
new file mode 100644
index 0000000..491dc82
--- /dev/null
+++ b/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+	frappe.reload_doc("Healthcare", "doctype", "Inpatient Record")
+	if frappe.db.has_column("Inpatient Record", "discharge_date"):
+		rename_field("Inpatient Record", "discharge_date", "discharge_datetime")
diff --git a/erpnext/patches/v13_0/setup_uae_vat_fields.py b/erpnext/patches/v13_0/setup_uae_vat_fields.py
new file mode 100644
index 0000000..d7a5c68
--- /dev/null
+++ b/erpnext/patches/v13_0/setup_uae_vat_fields.py
@@ -0,0 +1,12 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from erpnext.regional.united_arab_emirates.setup import  setup
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'United Arab Emirates'})
+	if not company:
+		return
+
+	setup()
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js
index 7460c75..d527839 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.js
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js
@@ -74,43 +74,46 @@
 		if (!frm.doc.letter_head && company.default_letter_head) {
 			frm.set_value('letter_head', company.default_letter_head);
 		}
+	},
+
+	currency: function(frm) {
 		frm.trigger("set_dynamic_labels");
 	},
 
 	set_dynamic_labels: function(frm) {
 		var company_currency = frm.doc.company? erpnext.get_currency(frm.doc.company): frappe.defaults.get_default("currency");
-		frappe.run_serially([
-			() => 	frm.events.set_exchange_rate(frm, company_currency),
-			() => 	frm.events.change_form_labels(frm, company_currency),
-			() => 	frm.events.change_grid_labels(frm),
-			() => 	frm.refresh_fields()
-		]);
+		if (frm.doc.employee && frm.doc.currency) {
+			frappe.run_serially([
+				() => 	frm.events.set_exchange_rate(frm, company_currency),
+				() => 	frm.events.change_form_labels(frm, company_currency),
+				() => 	frm.events.change_grid_labels(frm),
+				() => 	frm.refresh_fields()
+			]);
+		}
 	},
 
 	set_exchange_rate: function(frm, company_currency) {
-		if (frm.doc.docstatus === 0) {
-			if (frm.doc.currency) {
-				var from_currency = frm.doc.currency;
-				if (from_currency != company_currency) {
-					frm.events.hide_loan_section(frm);
-					frappe.call({
-						method: "erpnext.setup.utils.get_exchange_rate",
-						args: {
-							from_currency: from_currency,
-							to_currency: company_currency,
-						},
-						callback: function(r) {
-							frm.set_value("exchange_rate", flt(r.message));
-							frm.set_df_property('exchange_rate', 'hidden', 0);
-							frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
-								+ " = [?] " + company_currency);
-						}
-					});
-				} else {
-					frm.set_value("exchange_rate", 1.0);
-					frm.set_df_property('exchange_rate', 'hidden', 1);
-					frm.set_df_property("exchange_rate", "description", "" );
-				}
+		if (frm.doc.currency) {
+			var from_currency = frm.doc.currency;
+			if (from_currency != company_currency) {
+				frm.events.hide_loan_section(frm);
+				frappe.call({
+					method: "erpnext.setup.utils.get_exchange_rate",
+					args: {
+						from_currency: from_currency,
+						to_currency: company_currency,
+					},
+					callback: function(r) {
+						frm.set_value("exchange_rate", flt(r.message));
+						frm.set_df_property("exchange_rate", "hidden", 0);
+						frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
+							+ " = [?] " + company_currency);
+					}
+				});
+			} else {
+				frm.set_value("exchange_rate", 1.0);
+				frm.set_df_property("exchange_rate", "hidden", 1);
+				frm.set_df_property("exchange_rate", "description", "");
 			}
 		}
 	},
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 1c0abdf..365851a 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -738,21 +738,15 @@
 			}
 			else {
 				var valid_serial_nos = [];
-
+				var serialnos = [];
 				// Replacing all occurences of comma with carriage return
-				var serial_nos = item.serial_no.trim().replace(/,/g, '\n');
-
-				serial_nos = serial_nos.trim().split('\n');
-
-				// Trim each string and push unique string to new list
-				for (var x=0; x<=serial_nos.length - 1; x++) {
-					if (serial_nos[x].trim() != "" && valid_serial_nos.indexOf(serial_nos[x].trim()) == -1) {
-						valid_serial_nos.push(serial_nos[x].trim());
+				item.serial_no = item.serial_no.replace(/,/g, '\n');
+				serialnos = item.serial_no.split("\n");
+				for (var i = 0; i < serialnos.length; i++) {
+					if (serialnos[i] != "") {
+						valid_serial_nos.push(serialnos[i]);
 					}
 				}
-
-				// Add the new list to the serial no. field in grid with each in new line
-				item.serial_no = valid_serial_nos.join('\n');
 				item.conversion_factor = item.conversion_factor || 1;
 
 				refresh_field("serial_no", item.name, item.parentfield);
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 09b04ff..62faa30 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -78,7 +78,7 @@
 				place_of_supply = invoice_details.get("place_of_supply")
 				ecommerce_gstin =  invoice_details.get("ecommerce_gstin")
 
-				b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin, inv),{
+				b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin),{
 					"place_of_supply": "",
 					"ecommerce_gstin": "",
 					"rate": "",
@@ -90,7 +90,7 @@
 					"invoice_value": invoice_details.get("base_grand_total"),
 				})
 
-				row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin, inv))
+				row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
 				row["place_of_supply"] = place_of_supply
 				row["ecommerce_gstin"] = ecommerce_gstin
 				row["rate"] = rate
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 278821e..9e3c9a5 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -397,6 +397,7 @@
 					this.recent_order_list.toggle_component(false);
 					frappe.run_serially([
 						() => this.frm.refresh(name),
+						() => this.frm.call('reset_mode_of_payments'),
 						() => this.cart.load_invoice(),
 						() => this.item_selector.toggle_component(true)
 					]);
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
index be2b769..b10a9e3 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
@@ -64,10 +64,7 @@
 				{fieldname: 'print', fieldtype: 'Data', label: 'Print Preview'}
 			],
 			primary_action: () => {
-				const frm = this.events.get_frm();
-				frm.doc = this.doc;
-				frm.print_preview.lang_code = frm.doc.language;
-				frm.print_preview.printit(true);
+				this.print_receipt();
 			},
 			primary_action_label: __('Print'),
 		});
@@ -192,13 +189,21 @@
 		});
 
 		this.$summary_container.on('click', '.print-btn', () => {
-			const frm = this.events.get_frm();
-			frm.doc = this.doc;
-			frm.print_preview.lang_code = frm.doc.language;
-			frm.print_preview.printit(true);
+			this.print_receipt();
 		});
 	}
 
+	print_receipt() {
+		const frm = this.events.get_frm();
+		frappe.utils.print(
+			frm.doctype,
+			frm.docname,
+			frm.pos_print_format,
+			frm.doc.letter_head,
+			frm.doc.language || frappe.boot.lang
+		);
+	}
+
 	attach_shortcuts() {
 		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
 		this.$summary_container.find('.print-btn').attr("title", `${ctrl_label}+P`);
diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py
index abff973..2ea0bc0 100644
--- a/erpnext/setup/doctype/naming_series/naming_series.py
+++ b/erpnext/setup/doctype/naming_series/naming_series.py
@@ -10,6 +10,7 @@
 from frappe.model.document import Document
 from frappe.model.naming import parse_naming_series
 from frappe.permissions import get_doctypes_with_read
+from frappe.core.doctype.doctype.doctype import validate_series
 
 class NamingSeriesNotSetError(frappe.ValidationError): pass
 
@@ -126,7 +127,7 @@
 		dt = frappe.get_doc("DocType", self.select_doc_for_series)
 		options = self.scrub_options_list(self.set_options.split("\n"))
 		for series in options:
-			dt.validate_series(series)
+			validate_series(dt, series)
 			for i in sr:
 				if i[0]:
 					existing_series = [d.split('.')[0] for d in i[0].split("\n")]
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 33a8fe7..6fed9ef 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -1054,6 +1054,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
    "fieldname": "website_image_alt",
    "fieldtype": "Data",
    "label": "Image Description"
@@ -1066,7 +1067,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 1,
- "modified": "2021-03-15 13:41:04.108932",
+ "modified": "2021-03-18 14:04:38.575519",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
@@ -1137,4 +1138,4 @@
  "sort_order": "DESC",
  "title_field": "item_name",
  "track_changes": 1
-}
\ No newline at end of file
+}
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 8436acb..559f9a5 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form
+from frappe.utils import cint, get_link_to_form, add_to_date, today
 from erpnext.stock.stock_ledger import repost_future_sle
 from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
 from frappe.utils.user import get_users_with_role
@@ -29,7 +29,7 @@
 			self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
 		elif self.warehouse:
 			self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company")
-	
+
 	def set_status(self, status=None):
 		if not status:
 			status = 'Queued'
@@ -54,7 +54,6 @@
 
 		repost_sl_entries(doc)
 		repost_gl_entries(doc)
-		check_if_stock_and_account_balance_synced(doc.posting_date, doc.company)
 
 		doc.set_status('Completed')
 	except Exception:
@@ -103,7 +102,7 @@
 	recipients = get_users_with_role("Stock Manager")
 	if not recipients:
 		get_users_with_role("System Manager")
-	
+
 	subject = _("Error while reposting item valuation")
 	message = (_("Hi,") + "<br>"
 		+ _("An error has been appeared while reposting item valuation via {0}")
@@ -112,4 +111,24 @@
 	)
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
+def repost_entries():
+	riv_entries = get_repost_item_valuation_entries()
 
+	for row in riv_entries:
+		doc = frappe.get_cached_doc('Repost Item Valuation', row.name)
+		repost(doc)
+
+	riv_entries = get_repost_item_valuation_entries()
+	if riv_entries:
+		return
+
+	for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+		check_if_stock_and_account_balance_synced(today(), d.company)
+
+def get_repost_item_valuation_entries():
+	date = add_to_date(today(), hours=-12)
+
+	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
+		WHERE status != 'Completed' and creation <= %s and docstatus = 1
+		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
+	""", date, as_dict=1)
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index ea1b387..b5f7e05 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -458,7 +458,7 @@
 			Set rate for outgoing, scrapped and finished items
 		"""
 		# Set rate for outgoing items
-		outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate)
+		outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate, raise_error_if_no_rate)
 		finished_item_qty = sum([d.transfer_qty for d in self.items if d.is_finished_item])
 
 		# Set basic rate for incoming items
@@ -482,13 +482,13 @@
 			d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
 			d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
 
-	def set_rate_for_outgoing_items(self, reset_outgoing_rate=True):
+	def set_rate_for_outgoing_items(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
 		outgoing_items_cost = 0.0
 		for d in self.get('items'):
 			if d.s_warehouse:
 				if reset_outgoing_rate:
 					args = self.get_args_for_incoming_rate(d)
-					rate = get_incoming_rate(args)
+					rate = get_incoming_rate(args, raise_error_if_no_rate)
 					if rate > 0:
 						d.basic_rate = rate
 
@@ -1010,7 +1010,8 @@
 
 		self.set_scrap_items()
 		self.set_actual_qty()
-		self.calculate_rate_and_amount(raise_error_if_no_rate=False)
+		self.validate_customer_provided_item()
+		self.calculate_rate_and_amount()
 
 	def set_scrap_items(self):
 		if self.purpose != "Send to Subcontractor" and self.purpose in ["Manufacture", "Repack"]: