142 lines
9.2 KiB
C#
142 lines
9.2 KiB
C#
using Sockeye.Models;
|
|
using System.Linq;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace Sockeye.Biz
|
|
{
|
|
//VALIDATE **USER DEFINED** (not stock) REQUIRED FIELDS THAT ARE NOT CUSTOM
|
|
//(fields that are stock required are validated on their own not here)
|
|
|
|
internal static class RequiredFieldsValidator
|
|
{
|
|
internal static void Validate(BizObject biz, FormCustom formCustom, object proposedObject)
|
|
{
|
|
//No form custom = no template to check against so nothing to do
|
|
if (formCustom == null || string.IsNullOrWhiteSpace(formCustom.Template))
|
|
return;
|
|
var FormTemplate = JArray.Parse(formCustom.Template);
|
|
var FormFields = Biz.FormFieldOptionalCustomizableReference.FormFieldReferenceList(formCustom.FormKey);
|
|
|
|
foreach (JObject jo in FormTemplate)
|
|
{
|
|
if (jo["required"].Value<bool>() == true)
|
|
{
|
|
//First get the LT key
|
|
var FldLtKey = jo["fld"].Value<string>();
|
|
// - e.g.: {template:[{fld:"ltkeyfieldname",hide:"true/false",required:"true/false", type:"bool"},{fld:"ltkeyfieldname",hide:"true/false",required:"true/false", type:"text"]}
|
|
|
|
//get the FormField object
|
|
FormField FF = FormFields.Where(z => z.FieldKey == FldLtKey).Single();
|
|
|
|
//get the type and because of quote and pm subsections "TKeySection" property being named "WorkOrder" in the formFieldReference due to lack of separate
|
|
//translations for quote and pm subitems it's necessary to adjust the name here first before matching
|
|
var proposedObjectType = proposedObject.GetType().ToString().Replace("Sockeye.Models.", "").Replace("QuoteItem", "WorkOrderItem").Replace("PMItem", "WorkOrderItem");
|
|
|
|
//hacky last minute work around but workorder, quote and pm header objects have no tkeysection normally which can cause interference here with duplicate fields i.e. Tags in subsections
|
|
//as they will have the header rule applied if we just leave the tkeysection as null so for here we workaround that by creating a temporary tkeysection
|
|
if (FF.TKeySection == null)
|
|
{
|
|
switch (proposedObjectType)
|
|
{
|
|
case "WorkOrder":
|
|
FF.TKeySection = proposedObjectType;
|
|
break;
|
|
case "Quote":
|
|
FF.TKeySection = proposedObjectType;
|
|
break;
|
|
case "PM":
|
|
FF.TKeySection = proposedObjectType;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//don't validate custom fields, just skip them, make sure if it's sectional it matches the section of the object type (workorder sub sections)
|
|
if (!FF.IsCustomField && (FF.TKeySection == null || proposedObjectType == FF.TKeySection))
|
|
{
|
|
|
|
//bugbug: if section is workorderitem and field is tags but there is a tags required in workorder htat has NOT tkeysection it applies that rule
|
|
|
|
|
|
//Now get the actual property name from the available fields using the lt key
|
|
string RequiredPropertyName = FF.FieldKey;
|
|
//there might be a more specific model property due to being a workorder sub item duplicate such as WorkOrderItemTags vs WorkOrderTags
|
|
if (FF.ModelProperty != null)
|
|
RequiredPropertyName = FF.ModelProperty;
|
|
|
|
//Is it an indexed collection field?
|
|
if (RequiredPropertyName.Contains("."))
|
|
{
|
|
/*
|
|
flag errors to include parent e.g in PO item validation error field name could be "Items[3].QuantityReceived" to indicate poitems collection 4th row has error in qtyreceived
|
|
Note: collections are logically never more than 3 deep so for example the deepest would be workorder granchildren: e.g. Workorder.Items.Parts and most
|
|
are two deep however like Po.PoItems.field
|
|
for purposes of rule checking they would be flagged by their immediate parent "Items[3].QuantityReceived" for po or for workorder it could be
|
|
a required UPC field in Workorder.Items.Parts.UPC would result in a target error return of "Items[2].Parts[3].UPC" would be valid
|
|
*/
|
|
var FieldKeyParts = RequiredPropertyName.Split('.');
|
|
if (FieldKeyParts.Length == 2)
|
|
{
|
|
//parent collection -> child field
|
|
//target name like "Items.FieldName"
|
|
var parentCollection = proposedObject.GetType().GetProperty(FieldKeyParts[0]).GetValue(proposedObject, null);
|
|
int index = 0;
|
|
foreach (object ChildObject in (parentCollection as System.Collections.IEnumerable))
|
|
{
|
|
var fieldValue = ChildObject.GetType().GetProperty(FieldKeyParts[1]).GetValue(ChildObject, null);
|
|
if (fieldValue == null || string.IsNullOrWhiteSpace(fieldValue.ToString()))
|
|
biz.AddError(ApiErrorCode.VALIDATION_CUSTOM_REQUIRED_EMPTY, $"{FieldKeyParts[0]}[{index}].{FieldKeyParts[1]}");
|
|
index++;
|
|
}
|
|
}
|
|
else if (FieldKeyParts.Length == 3)
|
|
{
|
|
//grandparent collection -> Parent collection -> Child field
|
|
//target name like "WorkOrderItems.WorkOrderItemParts.UPC
|
|
var GrandParentCollection = proposedObject.GetType().GetProperty(FieldKeyParts[0]).GetValue(proposedObject, null);
|
|
int GrandParentIndex = 0;
|
|
foreach (object GrandParentObject in (GrandParentCollection as System.Collections.IEnumerable))
|
|
{
|
|
var ParentCollection = GrandParentObject.GetType().GetProperty(FieldKeyParts[1]).GetValue(GrandParentObject, null);
|
|
int ParentIndex = 0;
|
|
foreach (object ChildObject in (ParentCollection as System.Collections.IEnumerable))
|
|
{
|
|
var fieldValue = ChildObject.GetType().GetProperty(FieldKeyParts[1]).GetValue(ChildObject, null);
|
|
if (fieldValue == null || string.IsNullOrWhiteSpace(fieldValue.ToString()))
|
|
biz.AddError(ApiErrorCode.VALIDATION_CUSTOM_REQUIRED_EMPTY, $"{FieldKeyParts[0]}[{GrandParentIndex}].{FieldKeyParts[1]}[{ParentIndex}].{FieldKeyParts[2]}");
|
|
ParentIndex++;
|
|
}
|
|
GrandParentIndex++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//It's a simple property on the main object
|
|
//use reflection to get the underlying value from the proposed object to be saved
|
|
//issue the error on the *Models* property name to mirror how all other server error handling and validation works
|
|
//so that client end consumes it properly (fieldkey is just for the UI and showing / hiding overall form values)
|
|
object propertyValue = proposedObject.GetType().GetProperty(RequiredPropertyName).GetValue(proposedObject, null);
|
|
if (propertyValue == null)
|
|
biz.AddError(ApiErrorCode.VALIDATION_CUSTOM_REQUIRED_EMPTY, RequiredPropertyName);
|
|
else
|
|
{
|
|
|
|
if (RequiredPropertyName == "Tags")
|
|
{
|
|
if (((System.Collections.Generic.List<string>)propertyValue).Count == 0)
|
|
{
|
|
biz.AddError(ApiErrorCode.VALIDATION_CUSTOM_REQUIRED_EMPTY, RequiredPropertyName);
|
|
}
|
|
}
|
|
else
|
|
if (string.IsNullOrWhiteSpace(propertyValue.ToString()))
|
|
biz.AddError(ApiErrorCode.VALIDATION_CUSTOM_REQUIRED_EMPTY, RequiredPropertyName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}//eoc
|
|
}//ens
|