Merge pull request #30689 from deepeshgarg007/disabled_gl_dimensions

fix: Do not show disabled dimensions in reports
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 0611f88..a8776fa 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -386,7 +386,6 @@
 				doc,
 				credit_account,
 				debit_account,
-				against,
 				amount,
 				base_amount,
 				end_date,
@@ -570,7 +569,6 @@
 	doc,
 	credit_account,
 	debit_account,
-	against,
 	amount,
 	base_amount,
 	posting_date,
@@ -591,6 +589,7 @@
 	journal_entry.voucher_type = (
 		"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
 	)
+	journal_entry.process_deferred_accounting = deferred_process
 
 	debit_entry = {
 		"account": credit_account,
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index 335fd35..4493c72 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -3,7 +3,7 @@
  "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
- "creation": "2013-03-25 10:53:52",
+ "creation": "2022-01-25 10:29:58.717206",
  "doctype": "DocType",
  "document_type": "Document",
  "engine": "InnoDB",
@@ -13,6 +13,7 @@
   "voucher_type",
   "naming_series",
   "finance_book",
+  "process_deferred_accounting",
   "reversal_of",
   "tax_withholding_category",
   "column_break1",
@@ -524,13 +525,20 @@
    "label": "Reversal Of",
    "options": "Journal Entry",
    "read_only": 1
+  },
+  {
+   "fieldname": "process_deferred_accounting",
+   "fieldtype": "Link",
+   "label": "Process Deferred Accounting",
+   "options": "Process Deferred Accounting",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 176,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-01-04 13:39:36.485954",
+ "modified": "2022-04-06 17:18:46.865259",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry",
@@ -578,6 +586,7 @@
  "search_fields": "voucher_type,posting_date, due_date, cheque_no",
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
index 08a7f41..8ec726b 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
+++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py
@@ -11,7 +11,7 @@
 	convert_deferred_expense_to_expense,
 	convert_deferred_revenue_to_income,
 )
-from erpnext.accounts.general_ledger import make_reverse_gl_entries
+from erpnext.accounts.general_ledger import make_gl_entries
 
 
 class ProcessDeferredAccounting(Document):
@@ -34,4 +34,4 @@
 			filters={"against_voucher_type": self.doctype, "against_voucher": self.name},
 		)
 
-		make_reverse_gl_entries(gl_entries=gl_entries)
+		make_gl_entries(gl_entries=gl_entries, cancel=1)
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 6c38a7e..7781fe3 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -2240,6 +2240,14 @@
 
 		check_gl_entries(self, si.name, expected_gle, "2019-01-30")
 
+	def test_deferred_revenue_missing_account(self):
+		si = create_sales_invoice(posting_date="2019-01-10", do_not_submit=True)
+		si.items[0].enable_deferred_revenue = 1
+		si.items[0].service_start_date = "2019-01-10"
+		si.items[0].service_end_date = "2019-03-15"
+
+		self.assertRaises(frappe.ValidationError, si.save)
+
 	def test_fixed_deferred_revenue(self):
 		deferred_account = create_account(
 			account_name="Deferred Revenue",
@@ -3104,7 +3112,7 @@
 
 		acc_settings = frappe.get_single("Accounts Settings")
 		acc_settings.book_deferred_entries_via_journal_entry = 0
-		acc_settings.submit_journal_entriessubmit_journal_entries = 0
+		acc_settings.submit_journal_entries = 0
 		acc_settings.save()
 
 		frappe.db.set_value("Accounts Settings", None, "acc_frozen_upto", None)
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 8a9318e..7cbd2bd 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -180,6 +180,7 @@
 			else:
 				self.validate_deferred_start_and_end_date()
 
+			self.validate_deferred_income_expense_account()
 			self.set_inter_company_account()
 
 		if self.doctype == "Purchase Invoice":
@@ -208,6 +209,27 @@
 				(self.doctype, self.name),
 			)
 
+	def validate_deferred_income_expense_account(self):
+		field_map = {
+			"Sales Invoice": "deferred_revenue_account",
+			"Purchase Invoice": "deferred_expense_account",
+		}
+
+		for item in self.get("items"):
+			if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
+				if not item.get(field_map.get(self.doctype)):
+					default_deferred_account = frappe.db.get_value(
+						"Company", self.company, "default_" + field_map.get(self.doctype)
+					)
+					if not default_deferred_account:
+						frappe.throw(
+							_(
+								"Row #{0}: Please update deferred revenue/expense account in item row or default account in company master"
+							).format(item.idx)
+						)
+					else:
+						item.set(field_map.get(self.doctype), default_deferred_account)
+
 	def validate_deferred_start_and_end_date(self):
 		for d in self.items:
 			if d.get("enable_deferred_revenue") or d.get("enable_deferred_expense"):
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index eda3686..233b476 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -22,6 +22,9 @@
 
 
 class BuyingController(StockController, Subcontracting):
+	def __setup__(self):
+		self.flags.ignore_permlevel_for_fields = ["buying_price_list", "price_list_currency"]
+
 	def get_feed(self):
 		if self.get("supplier_name"):
 			return _("From {0} | {1} {2}").format(self.supplier_name, self.currency, self.grand_total)
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 7877827..19fedb3 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -16,6 +16,9 @@
 
 
 class SellingController(StockController):
+	def __setup__(self):
+		self.flags.ignore_permlevel_for_fields = ["selling_price_list", "price_list_currency"]
+
 	def get_feed(self):
 		return _("To {0} | {1} {2}").format(self.customer_name, self.currency, self.grand_total)
 
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
index 035290d..5252798 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
@@ -140,26 +140,6 @@
 		}
 	}
 
-	start_date(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	end_date(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	periodicity(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	set_no_of_visits(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		let me = this;
-		if (item.start_date && item.periodicity) {
-			me.frm.call('validate_end_date_visits');
-
-		}
-	}
 };
 
 extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 9a23c07..04c080c 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -213,6 +213,26 @@
 				if chk:
 					throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
 
+	def validate_items_table_change(self):
+		doc_before_save = self.get_doc_before_save()
+		if not doc_before_save:
+			return
+		for prev_item, item in zip(doc_before_save.items, self.items):
+			fields = [
+				"item_code",
+				"start_date",
+				"end_date",
+				"periodicity",
+				"sales_person",
+				"no_of_visits",
+				"serial_no",
+			]
+			for field in fields:
+				b_doc = prev_item.as_dict()
+				doc = item.as_dict()
+				if cstr(b_doc[field]) != cstr(doc[field]):
+					return True
+
 	def validate_no_of_visits(self):
 		return len(self.schedules) != sum(d.no_of_visits for d in self.items)
 
@@ -221,7 +241,7 @@
 		self.validate_maintenance_detail()
 		self.validate_dates_with_periodicity()
 		self.validate_sales_order()
-		if not self.schedules or self.validate_no_of_visits():
+		if not self.schedules or self.validate_items_table_change() or self.validate_no_of_visits():
 			self.generate_schedule()
 
 	def on_update(self):
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
index a98cd10..2268e0f 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
@@ -123,6 +123,36 @@
 
 		frappe.db.rollback()
 
+	def test_schedule_with_serials(self):
+		# Checks whether serials are automatically updated when changing in items table.
+		# Also checks if other fields trigger generate schdeule if changed in items table.
+		item_code = "_Test Serial Item"
+		make_serial_item_with_serial(item_code)
+		ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002")
+		ms.save()
+
+		# Before Save
+		self.assertEqual(ms.schedules[0].serial_no, "TEST001, TEST002")
+		self.assertEqual(ms.schedules[0].sales_person, "Sales Team")
+		self.assertEqual(len(ms.schedules), 4)
+		self.assertFalse(ms.validate_items_table_change())
+		# After Save
+		ms.items[0].serial_no = "TEST001"
+		ms.items[0].sales_person = "_Test Sales Person"
+		ms.items[0].no_of_visits = 2
+		self.assertTrue(ms.validate_items_table_change())
+		ms.save()
+		self.assertEqual(ms.schedules[0].serial_no, "TEST001")
+		self.assertEqual(ms.schedules[0].sales_person, "_Test Sales Person")
+		self.assertEqual(len(ms.schedules), 2)
+		# When user manually deleted a row from schedules table.
+		ms.schedules.pop()
+		self.assertEqual(len(ms.schedules), 1)
+		ms.save()
+		self.assertEqual(len(ms.schedules), 2)
+
+		frappe.db.rollback()
+
 
 def make_serial_item_with_serial(item_code):
 	serial_item_doc = create_item(item_code, is_stock_item=1)
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
index 72686e7..e2f6cb3 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
@@ -12,6 +12,9 @@
 		// filters for serial no based on item code
 		if (frm.doc.maintenance_type === "Scheduled") {
 			let item_code = frm.doc.purposes[0].item_code;
+			if (!item_code) {
+				return;
+			}
 			frappe.call({
 				method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.get_serial_nos_from_schedule",
 				args: {
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 8934f9c..2aba482 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -1144,6 +1144,56 @@
 		for index, row in enumerate(ste_manu.get("items"), start=1):
 			self.assertEqual(index, row.idx)
 
+	@change_settings(
+		"Manufacturing Settings",
+		{"backflush_raw_materials_based_on": "Material Transferred for Manufacture"},
+	)
+	def test_work_order_multiple_material_transfer(self):
+		"""
+		Test transferring multiple RMs in separate Stock Entries.
+		"""
+		work_order = make_wo_order_test_record(planned_start_date=now(), qty=1)
+		test_stock_entry.make_stock_entry(  # stock up RM
+			item_code="_Test Item",
+			target="_Test Warehouse - _TC",
+			qty=1,
+			basic_rate=5000.0,
+		)
+		test_stock_entry.make_stock_entry(  # stock up RM
+			item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC",
+			qty=2,
+			basic_rate=1000.0,
+		)
+
+		transfer_entry = frappe.get_doc(
+			make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1)
+		)
+		del transfer_entry.get("items")[0]  # transfer only one RM
+		transfer_entry.submit()
+
+		# WO's "Material Transferred for Mfg" shows all is transferred, one RM is pending
+		work_order.reload()
+		self.assertEqual(work_order.material_transferred_for_manufacturing, 1)
+		self.assertEqual(work_order.required_items[0].transferred_qty, 0)
+		self.assertEqual(work_order.required_items[1].transferred_qty, 2)
+
+		final_transfer_entry = frappe.get_doc(  # transfer last RM with For Quantity = 0
+			make_stock_entry(work_order.name, "Material Transfer for Manufacture", 0)
+		)
+		final_transfer_entry.save()
+
+		self.assertEqual(final_transfer_entry.fg_completed_qty, 0.0)
+		self.assertEqual(final_transfer_entry.items[0].qty, 1)
+
+		final_transfer_entry.submit()
+		work_order.reload()
+
+		# WO's "Material Transferred for Mfg" shows all is transferred, no RM is pending
+		self.assertEqual(work_order.material_transferred_for_manufacturing, 1)
+		self.assertEqual(work_order.required_items[0].transferred_qty, 1)
+		self.assertEqual(work_order.required_items[1].transferred_qty, 2)
+
 
 def update_job_card(job_card, jc_qty=None):
 	employee = frappe.db.get_value("Employee", {"status": "Active"}, "name")
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 6433a99..20f1503 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -540,8 +540,10 @@
 				|| frm.doc.transfer_material_against == 'Job Card') ? 0 : 1;
 
 			if (show_start_btn) {
-				if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty))
-					&& frm.doc.status != 'Stopped') {
+				let pending_to_transfer = frm.doc.required_items.some(
+					item => flt(item.transferred_qty) < flt(item.required_qty)
+				);
+				if (pending_to_transfer && frm.doc.status != 'Stopped') {
 					frm.has_start_btn = true;
 					frm.add_custom_button(__('Create Pick List'), function() {
 						erpnext.work_order.create_pick_list(frm);
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 2ee848c..2802310 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1186,7 +1186,11 @@
 	stock_entry.from_bom = 1
 	stock_entry.bom_no = work_order.bom_no
 	stock_entry.use_multi_level_bom = work_order.use_multi_level_bom
-	stock_entry.fg_completed_qty = qty or (flt(work_order.qty) - flt(work_order.produced_qty))
+	# accept 0 qty as well
+	stock_entry.fg_completed_qty = (
+		qty if qty is not None else (flt(work_order.qty) - flt(work_order.produced_qty))
+	)
+
 	if work_order.bom_no:
 		stock_entry.inspection_required = frappe.db.get_value(
 			"BOM", work_order.bom_no, "inspection_required"
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
index 9999a6d..943bd2c 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.js
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -78,8 +78,9 @@
 			}
 		});
 
-
 		report.page.add_inner_button(__("Download as JSON"), function () {
+			let filters = report.get_values();
+
 			frappe.call({
 				method: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
 				args: {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 1e62471..c4aa8a4 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1803,7 +1803,9 @@
 				or (desire_to_transfer > 0 and backflush_based_on == "Material Transferred for Manufacture")
 				or allow_overproduction
 			):
-				item_dict[item]["qty"] = desire_to_transfer
+				# "No need for transfer but qty still pending to transfer" case can occur
+				# when transferring multiple RM in different Stock Entries
+				item_dict[item]["qty"] = desire_to_transfer if (desire_to_transfer > 0) else pending_to_issue
 			elif pending_to_issue > 0:
 				item_dict[item]["qty"] = pending_to_issue
 			else: