Merge branch 'develop' into fix/subcontracting/additional-cost
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 98f3420..1d596c1 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -36,6 +36,15 @@
 		});
 
 		set_html_data(frm);
+
+		if (frm.doc.docstatus == 1) {
+			if (!frm.doc.posting_date) {
+				frm.set_value("posting_date", frappe.datetime.nowdate());
+			}
+			if (!frm.doc.posting_time) {
+				frm.set_value("posting_time", frappe.datetime.now_time());
+			}
+		}
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
index d6e35c6..9d15e6c 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
@@ -11,6 +11,7 @@
   "period_end_date",
   "column_break_3",
   "posting_date",
+  "posting_time",
   "pos_opening_entry",
   "status",
   "section_break_5",
@@ -51,7 +52,6 @@
    "fieldtype": "Datetime",
    "in_list_view": 1,
    "label": "Period End Date",
-   "read_only": 1,
    "reqd": 1
   },
   {
@@ -219,6 +219,13 @@
    "fieldtype": "Small Text",
    "label": "Error",
    "read_only": 1
+  },
+  {
+   "fieldname": "posting_time",
+   "fieldtype": "Time",
+   "label": "Posting Time",
+   "no_copy": 1,
+   "reqd": 1
   }
  ],
  "is_submittable": 1,
@@ -228,10 +235,11 @@
    "link_fieldname": "pos_closing_entry"
   }
  ],
- "modified": "2021-10-20 16:19:25.340565",
+ "modified": "2022-08-01 11:37:14.991228",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Closing Entry",
+ "naming_rule": "Expression (old style)",
  "owner": "Administrator",
  "permissions": [
   {
@@ -278,5 +286,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 49aab0d..655c4ec 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -15,6 +15,9 @@
 
 class POSClosingEntry(StatusUpdater):
 	def validate(self):
+		self.posting_date = self.posting_date or frappe.utils.nowdate()
+		self.posting_time = self.posting_time or frappe.utils.nowtime()
+
 		if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
 			frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
 
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
index d762087..a059455 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
@@ -6,6 +6,7 @@
  "engine": "InnoDB",
  "field_order": [
   "posting_date",
+  "posting_time",
   "merge_invoices_based_on",
   "column_break_3",
   "pos_closing_entry",
@@ -105,12 +106,19 @@
    "label": "Customer Group",
    "mandatory_depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
    "options": "Customer Group"
+  },
+  {
+   "fieldname": "posting_time",
+   "fieldtype": "Time",
+   "label": "Posting Time",
+   "no_copy": 1,
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-14 11:17:19.001142",
+ "modified": "2022-08-01 11:36:42.456429",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Merge Log",
@@ -173,5 +181,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 5003a1d..81a234a 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -9,7 +9,7 @@
 from frappe.core.page.background_jobs.background_jobs import get_info
 from frappe.model.document import Document
 from frappe.model.mapper import map_child_doc, map_doc
-from frappe.utils import cint, flt, getdate, nowdate
+from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
 from frappe.utils.background_jobs import enqueue
 from frappe.utils.scheduler import is_scheduler_inactive
 
@@ -99,6 +99,7 @@
 		sales_invoice.is_consolidated = 1
 		sales_invoice.set_posting_time = 1
 		sales_invoice.posting_date = getdate(self.posting_date)
+		sales_invoice.posting_time = get_time(self.posting_time)
 		sales_invoice.save()
 		sales_invoice.submit()
 
@@ -115,6 +116,7 @@
 		credit_note.is_consolidated = 1
 		credit_note.set_posting_time = 1
 		credit_note.posting_date = getdate(self.posting_date)
+		credit_note.posting_time = get_time(self.posting_time)
 		# TODO: return could be against multiple sales invoice which could also have been consolidated?
 		# credit_note.return_against = self.consolidated_invoice
 		credit_note.save()
@@ -402,6 +404,9 @@
 				merge_log.posting_date = (
 					getdate(closing_entry.get("posting_date")) if closing_entry else nowdate()
 				)
+				merge_log.posting_time = (
+					get_time(closing_entry.get("posting_time")) if closing_entry else nowtime()
+				)
 				merge_log.customer = customer
 				merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None
 
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 526ea9d..54af225 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -616,7 +616,7 @@
 						previous_stock_value = len(my_sle) > i + 1 and flt(my_sle[i + 1].stock_value) or 0.0
 
 						if previous_stock_value:
-							return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
+							return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
 						else:
 							return flt(row.qty) * self.get_average_buying_rate(row, item_code)
 			else:
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
index 7d676e4..cd4aaee 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
@@ -12,7 +12,9 @@
 import frappe
 from bs4 import BeautifulSoup as bs
 from frappe import _
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.custom.doctype.custom_field.custom_field import (
+	create_custom_fields as _create_custom_fields,
+)
 from frappe.model.document import Document
 from frappe.utils.data import format_datetime
 
@@ -577,22 +579,25 @@
 				new_year.save()
 				oldest_year = new_year
 
-		def create_custom_fields(doctypes):
-			tally_guid_df = {
-				"fieldtype": "Data",
-				"fieldname": "tally_guid",
-				"read_only": 1,
-				"label": "Tally GUID",
-			}
-			tally_voucher_no_df = {
-				"fieldtype": "Data",
-				"fieldname": "tally_voucher_no",
-				"read_only": 1,
-				"label": "Tally Voucher Number",
-			}
-			for df in [tally_guid_df, tally_voucher_no_df]:
-				for doctype in doctypes:
-					create_custom_field(doctype, df)
+		def create_custom_fields():
+			_create_custom_fields(
+				{
+					("Journal Entry", "Purchase Invoice", "Sales Invoice"): [
+						{
+							"fieldtype": "Data",
+							"fieldname": "tally_guid",
+							"read_only": 1,
+							"label": "Tally GUID",
+						},
+						{
+							"fieldtype": "Data",
+							"fieldname": "tally_voucher_no",
+							"read_only": 1,
+							"label": "Tally Voucher Number",
+						},
+					]
+				}
+			)
 
 		def create_price_list():
 			frappe.get_doc(
@@ -628,7 +633,7 @@
 
 			create_fiscal_years(vouchers)
 			create_price_list()
-			create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"])
+			create_custom_fields()
 
 			total = len(vouchers)
 			is_last = False
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
index 2e18776..4aa98aa 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
@@ -6,7 +6,7 @@
 
 import frappe
 from frappe import _
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.model.document import Document
 from frappe.utils.nestedset import get_root_of
 
@@ -19,27 +19,24 @@
 
 	def create_delete_custom_fields(self):
 		if self.enable_sync:
-			custom_fields = {}
-			# create
-			for doctype in ["Customer", "Sales Order", "Item", "Address"]:
-				df = dict(
-					fieldname="woocommerce_id",
-					label="Woocommerce ID",
-					fieldtype="Data",
-					read_only=1,
-					print_hide=1,
-				)
-				create_custom_field(doctype, df)
-
-			for doctype in ["Customer", "Address"]:
-				df = dict(
-					fieldname="woocommerce_email",
-					label="Woocommerce Email",
-					fieldtype="Data",
-					read_only=1,
-					print_hide=1,
-				)
-				create_custom_field(doctype, df)
+			create_custom_fields(
+				{
+					("Customer", "Sales Order", "Item", "Address"): dict(
+						fieldname="woocommerce_id",
+						label="Woocommerce ID",
+						fieldtype="Data",
+						read_only=1,
+						print_hide=1,
+					),
+					("Customer", "Address"): dict(
+						fieldname="woocommerce_email",
+						label="Woocommerce Email",
+						fieldtype="Data",
+						read_only=1,
+						print_hide=1,
+					),
+				}
+			)
 
 			if not frappe.get_value("Item Group", {"name": _("WooCommerce Products")}):
 				item_group = frappe.new_doc("Item Group")
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 7d7e6b5..2076dde 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -4,7 +4,7 @@
 
 import frappe
 from frappe import _
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
 from frappe.utils import cint
 
@@ -83,35 +83,32 @@
 
 
 def create_print_setting_custom_fields():
-	create_custom_field(
-		"Print Settings",
+	create_custom_fields(
 		{
-			"label": _("Compact Item Print"),
-			"fieldname": "compact_item_print",
-			"fieldtype": "Check",
-			"default": 1,
-			"insert_after": "with_letterhead",
-		},
-	)
-	create_custom_field(
-		"Print Settings",
-		{
-			"label": _("Print UOM after Quantity"),
-			"fieldname": "print_uom_after_quantity",
-			"fieldtype": "Check",
-			"default": 0,
-			"insert_after": "compact_item_print",
-		},
-	)
-	create_custom_field(
-		"Print Settings",
-		{
-			"label": _("Print taxes with zero amount"),
-			"fieldname": "print_taxes_with_zero_amount",
-			"fieldtype": "Check",
-			"default": 0,
-			"insert_after": "allow_print_for_cancelled",
-		},
+			"Print Settings": [
+				{
+					"label": _("Compact Item Print"),
+					"fieldname": "compact_item_print",
+					"fieldtype": "Check",
+					"default": "1",
+					"insert_after": "with_letterhead",
+				},
+				{
+					"label": _("Print UOM after Quantity"),
+					"fieldname": "print_uom_after_quantity",
+					"fieldtype": "Check",
+					"default": "0",
+					"insert_after": "compact_item_print",
+				},
+				{
+					"label": _("Print taxes with zero amount"),
+					"fieldname": "print_taxes_with_zero_amount",
+					"fieldtype": "Check",
+					"default": "0",
+					"insert_after": "allow_print_for_cancelled",
+				},
+			]
+		}
 	)
 
 
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
index 9221dbf..3aa8e61 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
@@ -55,8 +55,6 @@
         "in_words",
         "bill_no",
         "bill_date",
-        "accounting_details_section",
-        "provisional_expense_account",
         "more_info",
         "status",
         "column_break_39",
@@ -529,19 +527,6 @@
             "read_only": 1
         },
         {
-            "collapsible": 1,
-            "fieldname": "accounting_details_section",
-            "fieldtype": "Section Break",
-            "label": "Accounting Details"
-        },
-        {
-            "fieldname": "provisional_expense_account",
-            "fieldtype": "Link",
-            "hidden": 1,
-            "label": "Provisional Expense Account",
-            "options": "Account"
-        },
-        {
             "default": "0",
             "fieldname": "is_return",
             "fieldtype": "Check",