protected virtual InventoryItem AssignRestInventoryFields(InventoryItemMaintBase graph, InventoryItem item, InventoryItem templateItem)
		{
			var copy = (InventoryItem)graph.Item.Cache.CreateCopy(item);
			graph.Item.Cache.RestoreCopy(copy, templateItem);

			var excludeFields = new Type[]
			{
				typeof(InventoryItem.inventoryID),
				typeof(InventoryItem.inventoryCD),
				typeof(InventoryItem.descr),
				typeof(InventoryItem.preferredVendorID),
				typeof(InventoryItem.preferredVendorLocationID),
				typeof(InventoryItem.templateItemID),
				typeof(InventoryItem.isTemplate),
				typeof(InventoryItem.Tstamp),
				typeof(InventoryItem.createdByID),
				typeof(InventoryItem.createdByScreenID),
				typeof(InventoryItem.createdDateTime),
				typeof(InventoryItem.lastModifiedByID),
				typeof(InventoryItem.lastModifiedByScreenID),
				typeof(InventoryItem.lastModifiedDateTime),
				typeof(InventoryItem.noteID),
			};

			foreach (Type excludeField in excludeFields)
			{
				graph.Item.Cache.SetValue(copy, excludeField.Name,
					graph.Item.Cache.GetValue(item, excludeField.Name));
			}

			return graph.Item.Update(copy);
		}
		protected virtual InventoryItem AssignInventoryField<TField>(InventoryItemMaintBase graph, InventoryItem item, object value)
			where TField : IBqlField
		{
			var copy = (InventoryItem)graph.Item.Cache.CreateCopy(item);
			graph.Item.Cache.SetValue<TField>(copy, value);
			return graph.Item.Update(copy);
		}
		protected virtual void AssignInventoryCategories(InventoryItemMaintBase graph, IEnumerable<INItemCategory> templateItemCategs)
		{
			INItemCategory[] itemCategs = graph.Category.Select().RowCast<INItemCategory>().ToArray();
			foreach (INItemCategory templateItemCateg in templateItemCategs)
			{
				INItemCategory itemCateg =
					itemCategs.FirstOrDefault(ic => ic.CategoryID == templateItemCateg.CategoryID)
					?? graph.Category.Insert(new INItemCategory { CategoryID = templateItemCateg.CategoryID });
			}
		}
		protected virtual void AssignConversionsSettings(InventoryItemMaintBase graph, InventoryItem item, InventoryItem templateItem)
		{
			//sales and purchase units must be cleared not to be added to item unit conversions on base unit change.
			PXCache cache = graph.Item.Cache;
			cache.SetValueExt<InventoryItem.baseUnit>(item, null);
			cache.SetValue<InventoryItem.salesUnit>(item, null);
			cache.SetValue<InventoryItem.purchaseUnit>(item, null);

			cache.SetValueExt<InventoryItem.baseUnit>(item, templateItem.BaseUnit);
			cache.SetValueExt<InventoryItem.salesUnit>(item, templateItem.SalesUnit);
			cache.SetValueExt<InventoryItem.purchaseUnit>(item, templateItem.PurchaseUnit);

			cache.SetValueExt<InventoryItem.decimalBaseUnit>(item, templateItem.DecimalBaseUnit);
			cache.SetValueExt<InventoryItem.decimalSalesUnit>(item, templateItem.DecimalSalesUnit);
			cache.SetValueExt<InventoryItem.decimalPurchaseUnit>(item, templateItem.DecimalPurchaseUnit);
		}
		protected virtual void AssignVendorInventory(InventoryItemMaintBase graph, IEnumerable<POVendorInventory> templateVendorInvs)
		{
			POVendorInventory[] vendorInvs = graph.VendorItems.Select().RowCast<POVendorInventory>().ToArray();
			foreach (POVendorInventory templateVendorInv in templateVendorInvs)
			{
				POVendorInventory vendorInv =
					vendorInvs.FirstOrDefault(vi
						=> vi.SubItemID == templateVendorInv.SubItemID
						&& vi.VendorID == templateVendorInv.VendorID
						&& vi.VendorLocationID == templateVendorInv.VendorLocationID
						&& vi.PurchaseUnit == templateVendorInv.PurchaseUnit)
					?? graph.VendorItems.Insert();
				var copy = (POVendorInventory)graph.VendorItems.Cache.CreateCopy(vendorInv);

				graph.VendorItems.Cache.RestoreCopy(copy, templateVendorInv);

				var excludeFields = new Type[]
				{
					typeof(POVendorInventory.recordID),
					typeof(POVendorInventory.inventoryID),
					typeof(POVendorInventory.Tstamp),
					typeof(POVendorInventory.createdByID),
					typeof(POVendorInventory.createdByScreenID),
					typeof(POVendorInventory.createdDateTime),
					typeof(POVendorInventory.lastModifiedByID),
					typeof(POVendorInventory.lastModifiedByScreenID),
					typeof(POVendorInventory.lastModifiedDateTime),
				};

				foreach (Type excludeField in excludeFields)
				{
					graph.VendorItems.Cache.SetValue(copy, excludeField.Name,
						graph.VendorItems.Cache.GetValue(vendorInv, excludeField.Name));
				}

				vendorInv = graph.VendorItems.Update(copy);
			}
		}
		protected virtual void AssignInventoryAttributes(InventoryItemMaintBase graph, MatrixInventoryItem itemToCreateUpdate, Dictionary<string, string> templateAttrValues)
		{
			CSAnswers[] answers = graph.Answers.Select().RowCast<CSAnswers>().ToArray();
			foreach (CSAnswers answer in answers)
			{
				string value = null;
				for (int i = 0; i < itemToCreateUpdate.AttributeIDs.Length; i++)
				{
					if (itemToCreateUpdate.AttributeIDs[i].Equals(answer.AttributeID, StringComparison.OrdinalIgnoreCase))
					{
						value = itemToCreateUpdate.AttributeValues[i];
						break;
					}
				}
				if (value == null)
				{
					templateAttrValues.TryGetValue(answer.AttributeID, out value);
				}

				answer.Value = value;
				graph.Answers.Update(answer);
			}
		}
		protected virtual void AssignInventoryConversions(InventoryItemMaintBase graph, IEnumerable<INUnit> templateItemConvs)
		{
			INUnit[] itemConvs = graph.itemunits.Select().RowCast<INUnit>().ToArray();
			foreach (INUnit templateItemConv in templateItemConvs)
			{
				INUnit itemConv =
					itemConvs.FirstOrDefault(ic
						=> ic.FromUnit == templateItemConv.FromUnit
						&& ic.ToUnit == templateItemConv.ToUnit)
					?? graph.itemunits.Insert(new INUnit { FromUnit = templateItemConv.FromUnit });
				var copy = (INUnit)graph.itemunits.Cache.CreateCopy(itemConv);

				graph.itemunits.Cache.RestoreCopy(copy, templateItemConv);

				var excludeFields = new Type[]
				{
					typeof(INUnit.recordID),
					typeof(INUnit.inventoryID),
					typeof(INUnit.Tstamp),
					typeof(INUnit.createdByID),
					typeof(INUnit.createdByScreenID),
					typeof(INUnit.createdDateTime),
					typeof(INUnit.lastModifiedByID),
					typeof(INUnit.lastModifiedByScreenID),
					typeof(INUnit.lastModifiedDateTime),
				};

				foreach (Type excludeField in excludeFields)
				{
					graph.itemunits.Cache.SetValue(copy, excludeField.Name,
						graph.itemunits.Cache.GetValue(itemConv, excludeField.Name));
				}

				itemConv = graph.itemunits.Update(copy);
			}
		}
		public virtual void CreateUpdateMatrixItems(InventoryItemMaintBase graph, InventoryItem templateItem, IEnumerable<MatrixInventoryItem> itemsToCreateUpdate, bool create,
			Action<MatrixInventoryItem, InventoryItem> beforeSave = null)
		{
			Dictionary<string, string> templateAttrValues =
				PXSelectReadonly<CSAnswers, Where<CSAnswers.refNoteID, Equal<Required<InventoryItem.noteID>>>>
				.Select(graph, templateItem.NoteID)
				.RowCast<CSAnswers>()
				.ToDictionary(a => a.AttributeID, a => a.Value, StringComparer.OrdinalIgnoreCase);
			IEnumerable<POVendorInventory> templateVendorInvs =
				graph.VendorItems.View.SelectMultiBound(new[] { templateItem })
				.RowCast<POVendorInventory>()
				.ToArray();
			IEnumerable<INUnit> templateItemConvs =
				graph.itemunits.View.SelectMultiBound(new[] { templateItem })
				.RowCast<INUnit>()
				.ToArray();
			IEnumerable<INItemCategory> templateItemCategs =
				graph.Category.View.SelectMultiBound(new[] { templateItem })
				.RowCast<INItemCategory>()
				.ToArray();
			IEnumerable<INItemBoxEx> templateBoxes = null;
			InventoryItemMaint stockItemGraph = null;
			if (templateItem.StkItem == true)
			{
				stockItemGraph = (InventoryItemMaint)graph;
				templateBoxes = stockItemGraph.Boxes.View.SelectMultiBound(new[] { templateItem })
					.RowCast<INItemBoxEx>()
					.ToArray();
			}

			foreach (MatrixInventoryItem itemToCreateUpdate in itemsToCreateUpdate)
			{
				graph.Clear();

				InventoryItem item;
				if (create)
				{
					item = new InventoryItem
					{
						InventoryCD = itemToCreateUpdate.InventoryCD
					};
					item = graph.Item.Insert(item);
				}
				else
				{
					item = graph.Item.Current = graph.Item.Search<InventoryItem.inventoryCD>(itemToCreateUpdate.InventoryCD);
				}
				if (item == null)
				{
					throw new PXInvalidOperationException();
				}

				if (create)
				{
					item = AssignInventoryField<InventoryItem.descr>(graph, item, itemToCreateUpdate.Descr);
					PXDBLocalizableStringAttribute.CopyTranslations<MatrixInventoryItem.descr, InventoryItem.descr>(graph, itemToCreateUpdate, item);
				}
				item = AssignInventoryField<InventoryItem.itemClassID>(graph, item, templateItem.ItemClassID);
				item = AssignInventoryField<InventoryItem.postClassID>(graph, item, templateItem.PostClassID);
				AssignConversionsSettings(graph, item, templateItem);
				item = AssignRestInventoryFields(graph, item, templateItem);
				item = AssignInventoryField<InventoryItem.templateItemID>(graph, item, templateItem.InventoryID);

				AssignInventoryAttributes(graph, itemToCreateUpdate, templateAttrValues);
				AssignVendorInventory(graph, templateVendorInvs);
				AssignInventoryConversions(graph, templateItemConvs);
				AssignInventoryCategories(graph, templateItemCategs);
				if (templateItem.StkItem == true)
					AssignInventoryBoxes(stockItemGraph, templateBoxes);

				beforeSave?.Invoke(itemToCreateUpdate, item);

				graph.Save.Press();

				itemToCreateUpdate.InventoryID = item.InventoryID;
			}
		}