398 lines
22 KiB
C#
398 lines
22 KiB
C#
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using Newtonsoft.Json.Linq;
|
|
using System.Text;
|
|
|
|
namespace AyaNova.Biz
|
|
{
|
|
//************************************************
|
|
// This contains all the fields that are:
|
|
// - Customizable on forms
|
|
// - In grid list templates
|
|
//In addition it serves as a source for valid object keys in AvailableObjectKeys
|
|
//SQL code uses this:
|
|
// - a source of database column names and ID column names
|
|
// - Essentially this replaces all the attribute decoration in v7 and the individual classes with objects
|
|
|
|
|
|
public static class ObjectFields
|
|
{
|
|
|
|
//DEFINE VALID KEYS HERE
|
|
//For objects that are both list and edit form it's just the name of the object
|
|
//For objects that are compound list objects or reporting objects it's whatever uniquely but clearly identifies that and ends in _LIST
|
|
public const string WIDGET_KEY = "widget";
|
|
public const string USER_KEY = "user";
|
|
public const string TEST_WIDGET_USER_EMAIL_ADDRESS_LIST_KEY = "TEST_WIDGET_USER_EMAIL_ADDRESS_LIST";//for development testing, not a real thing going forward
|
|
|
|
|
|
public static List<string> AvailableObjectKeys
|
|
{
|
|
get
|
|
{
|
|
List<string> l = new List<string>{
|
|
WIDGET_KEY, USER_KEY
|
|
};
|
|
return l;
|
|
}
|
|
}
|
|
|
|
public static bool IsValidObjectKey(string key)
|
|
{
|
|
return AvailableObjectKeys.Contains(key);
|
|
}
|
|
|
|
public static List<ObjectField> ObjectFieldsList(string key)
|
|
{
|
|
/*
|
|
***************************** WARNING: Be careful here, if a standard field is hideable and also it's DB SCHEMA is set to NON NULLABLE then the CLIENT end needs to set a default
|
|
***************************** Otherwise the hidden field can't be set and the object can't be saved EVER
|
|
Defaults of paramterless constructor:
|
|
SharedLTKey = false;
|
|
Hideable = true;
|
|
Custom = false;
|
|
Filterable = true;
|
|
Sortable = true;
|
|
MiniAvailable = true;
|
|
*/
|
|
List<ObjectField> l = new List<ObjectField>();
|
|
switch (key)
|
|
{
|
|
/*
|
|
TODO: going to need some SQL hints added here
|
|
- SqlIDColumn indicating what the ID column is to fetch the underlying value from
|
|
- SqlColumn indicating an alternative column name as known to the sql server if it varies from the propertyname (in lowercase)
|
|
|
|
Also: going to need a definition of the table name and the default identity column
|
|
Maybe decorate with an INTERNAL ONLY bool so that we see it at the server but not at the client or when fetched as possible fields
|
|
|
|
hypothetical to help develop this:
|
|
Client and head office list:
|
|
{key=ClientName, propertyname=Name, idcolumn=id,}
|
|
|
|
In v7 this is what the attribute decorations looked like:
|
|
[SqlColumnNameAttribute("aClient.aName", "aClient.aID")]
|
|
public GridNameValueCellItem LT_O_Client
|
|
{
|
|
get
|
|
{
|
|
return mClient;
|
|
}
|
|
}
|
|
|
|
[SqlColumnNameAttribute("aHeadOffice.aName","aHeadOffice.aID")]
|
|
public GridNameValueCellItem LT_O_HeadOffice
|
|
{
|
|
get
|
|
{
|
|
return mHeadOffice;
|
|
}
|
|
}
|
|
|
|
And the query (from clientlistdetailed)
|
|
string q =
|
|
"SELECT ~MAXRECS~ ACLIENT.aID, ACLIENT.aName, ACLIENT.aModified, " +
|
|
" ACLIENT.AACTIVE, ACLIENT.aUsesBanking, " +
|
|
" ACLIENT.aBillHeadOffice, ACLIENT.aWebAddress, " +
|
|
" ACLIENT.aDispatchZoneID, ACLIENT.aClientGroupID, " +
|
|
" ACLIENT.aLastWorkorderID, " +
|
|
" ACLIENT.aLastServiceDate, (SELECT TOP 1 aSBANK.aHoursBalance " +
|
|
"FROM aServiceBank aSBANK WHERE aSBANK.AAPPLIESTOROOTOBJECTID " +
|
|
"= ACLIENT.aID ORDER " +
|
|
"BY aSBANK.aCreated DESC) AS HOURSBAL, (SELECT TOP " +
|
|
"1 aSBANK.aIncidentsBalance FROM aServiceBank aSBANK " +
|
|
"WHERE aSBANK.AAPPLIESTOROOTOBJECTID = ACLIENT.aID " +
|
|
"ORDER BY aSBANK.aCreated DESC) AS INCIDENTSBAL, " +
|
|
" (SELECT TOP 1 aSBANK.aCurrencyBalance FROM " +
|
|
"aServiceBank aSBANK WHERE aSBANK.AAPPLIESTOROOTOBJECTID " +
|
|
"= ACLIENT.aID ORDER BY aSBANK.aCreated DESC) " +
|
|
"AS CURRENCYBAL, " +
|
|
" ACLIENT.AACCOUNTNUMBER, aHeadOffice.aName " +
|
|
"AS aHeadOfficeName, aHeadOffice.aID " +
|
|
"AS aHeadOfficeID, aDispatchZone.aName AS aDispatchZoneName, " +
|
|
" ACLIENT.aRegionID, aRegion.aName " +
|
|
"AS aRegionName, aClientGroup.aName AS " +
|
|
"aClientGroupName, aPHYSICAL.aDeliveryAddress, " +
|
|
"aPHYSICAL.aCity, aPHYSICAL.aStateProv, aPHYSICAL.aCountry, " +
|
|
" aPHYSICAL.aPostal, aPHYSICAL.AADDRESSTYPE, " +
|
|
" aPHYSICAL.aLongitude, aPHYSICAL.aLatitude, " +
|
|
" aWorkorderService.aServiceNumber, " +
|
|
" ACLIENT.aContractID, aContract.aName AS " +
|
|
"aContractName, ACLIENT.aContractExpires, aPHYSICAL.aCountryCode, " +
|
|
" aPOSTAL.AADDRESSTYPE AS aPAddressType, " +
|
|
" aPOSTAL.aDeliveryAddress AS aPDeliveryAddress, " +
|
|
" aPOSTAL.aCity AS aPCity, aPOSTAL.aStateProv " +
|
|
"AS aPStateProv, aPOSTAL.aCountryCode AS aPCountryCode, " +
|
|
" aPOSTAL.aCountry AS aPCountry, aPOSTAL.aPostal " +
|
|
"AS aPPostal, ACLIENT.aCustom1, ACLIENT.aCustom2, " +
|
|
" ACLIENT.aCustom3, ACLIENT.aCustom4, " +
|
|
" ACLIENT.aCustom5, ACLIENT.aCustom6, " +
|
|
" ACLIENT.aCustom7, ACLIENT.aCustom8, ACLIENT.aCustom9, " +
|
|
" ACLIENT.aCustom0, ACLIENT.aTechNotes, " +
|
|
" ACLIENT.aNotes, ACLIENT.aPopUpNotes, " +
|
|
" ACLIENT.ACONTACT, ACLIENT.AEMAIL, ACLIENT.APHONE1, ACLIENT.APHONE2, ACLIENT.APHONE3, ACLIENT.APHONE4, ACLIENT.APHONE5, ACLIENT.ACONTACTNOTES " +
|
|
|
|
|
|
|
|
"FROM " +
|
|
" ACLIENT ACLIENT " +
|
|
" LEFT OUTER JOIN ADISPATCHZONE ON (ACLIENT.ADISPATCHZONEID=ADISPATCHZONE.AID) " +
|
|
" LEFT OUTER JOIN ACLIENTGROUP ON (ACLIENT.ACLIENTGROUPID=ACLIENTGROUP.AID) " +
|
|
" LEFT OUTER JOIN AADDRESS aPHYSICAL ON (ACLIENT.AID=aPHYSICAL.AROOTOBJECTID) " +
|
|
" LEFT OUTER JOIN AADDRESS aPOSTAL ON (ACLIENT.AID=aPOSTAL.AROOTOBJECTID) " +
|
|
" LEFT OUTER JOIN ACONTRACT ON (ACLIENT.ACONTRACTID=ACONTRACT.AID) " +
|
|
" LEFT OUTER JOIN AHEADOFFICE ON (ACLIENT.AHEADOFFICEID=AHEADOFFICE.AID) " +
|
|
" LEFT OUTER JOIN AWORKORDERSERVICE ON (ACLIENT.ALASTWORKORDERID=AWORKORDERSERVICE.AWORKORDERID) " +
|
|
" LEFT OUTER JOIN AREGION ON (ACLIENT.AREGIONID=AREGION.AID) " +
|
|
|
|
"WHERE (aPHYSICAL.AADDRESSTYPE " +
|
|
"IS NULL OR aPHYSICAL.AADDRESSTYPE " +
|
|
"= 2) AND (aPOSTAL.AADDRESSTYPE IS NULL OR aPOSTAL.AADDRESSTYPE " +
|
|
"= 1) \r\n ";
|
|
|
|
|
|
*/
|
|
case WIDGET_KEY:
|
|
#region WIDGET_KEY
|
|
|
|
//first column is the non visible Default Id column so that the client knows what to open when there is no field with ID selected that matches underlying record type
|
|
//in this case the default of id is sufficient for this list
|
|
l.Add(new ObjectField { Key = "df", AyObjectType = (int)AyaType.Widget });
|
|
l.Add(new ObjectField { Key = "WidgetName", PropertyName = "Name", DataType = (int)AyaDataType.Text, Hideable = false });
|
|
l.Add(new ObjectField { Key = "WidgetSerial", PropertyName = "Serial", DataType = (int)AyaDataType.Integer });
|
|
l.Add(new ObjectField { Key = "WidgetDollarAmount", PropertyName = "DollarAmount", DataType = (int)AyaDataType.Currency });
|
|
l.Add(new ObjectField { Key = "WidgetCount", PropertyName = "Count", DataType = (int)AyaDataType.Integer });
|
|
l.Add(new ObjectField { Key = "WidgetRoles", PropertyName = "Roles", DataType = (int)AyaDataType.Enum, EnumType = typeof(AuthorizationRoles).ToString() });
|
|
l.Add(new ObjectField { Key = "WidgetStartDate", PropertyName = "StartDate", DataType = (int)AyaDataType.DateTime });
|
|
l.Add(new ObjectField { Key = "WidgetEndDate", PropertyName = "EndDate", DataType = (int)AyaDataType.DateTime });
|
|
l.Add(new ObjectField { Key = "WidgetNotes", PropertyName = "Notes", DataType = (int)AyaDataType.Text });
|
|
//More to do on this, maybe the datatype should be a LINK or something for UI purposes
|
|
//circle back on this when there is enough infrastructure to test
|
|
l.Add(new ObjectField { Key = "User", PropertyName = "userid", DataType = (int)AyaDataType.Text, AyObjectType = (int)AyaType.User });
|
|
l.Add(new ObjectField { Key = "Active", PropertyName = "Active", DataType = (int)AyaDataType.Bool, Hideable = false, SharedLTKey = true });
|
|
l.Add(new ObjectField { Key = "Tags", PropertyName = "Tags", DataType = (int)AyaDataType.Tags, SharedLTKey = true });
|
|
|
|
l.Add(new ObjectField { Key = "WidgetCustom1", PropertyName = "WidgetCustom1", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom2", PropertyName = "WidgetCustom2", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom3", PropertyName = "WidgetCustom3", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom4", PropertyName = "WidgetCustom4", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom5", PropertyName = "WidgetCustom5", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom6", PropertyName = "WidgetCustom6", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom7", PropertyName = "WidgetCustom7", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom8", PropertyName = "WidgetCustom8", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom9", PropertyName = "WidgetCustom9", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom10", PropertyName = "WidgetCustom10", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom11", PropertyName = "WidgetCustom11", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom12", PropertyName = "WidgetCustom12", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom13", PropertyName = "WidgetCustom13", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom14", PropertyName = "WidgetCustom14", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom15", PropertyName = "WidgetCustom15", Custom = true });
|
|
l.Add(new ObjectField { Key = "WidgetCustom16", PropertyName = "WidgetCustom16", Custom = true });
|
|
break;
|
|
#endregion
|
|
case USER_KEY:
|
|
#region USER_KEY
|
|
l.Add(new ObjectField { Key = "df", AyObjectType = (int)AyaType.User });
|
|
l.Add(new ObjectField { Key = "Name", PropertyName = "Name", SharedLTKey = true, DataType = (int)AyaDataType.Text, Hideable = false });
|
|
l.Add(new ObjectField { Key = "UserEmployeeNumber", PropertyName = "EmployeeNumber", DataType = (int)AyaDataType.Text });
|
|
l.Add(new ObjectField { Key = "AuthorizationRoles", PropertyName = "Roles", Hideable = false, DataType = (int)AyaDataType.Enum, EnumType = typeof(AuthorizationRoles).ToString() });
|
|
l.Add(new ObjectField { Key = "UserNotes", PropertyName = "Notes", DataType = (int)AyaDataType.Text });
|
|
l.Add(new ObjectField { Key = "UserUserType", PropertyName = "UserType", Hideable = false, DataType = (int)AyaDataType.Enum, EnumType = typeof(UserType).ToString() });
|
|
l.Add(new ObjectField { Key = "Active", PropertyName = "Active", DataType = (int)AyaDataType.Bool, Hideable = false, SharedLTKey = true });
|
|
l.Add(new ObjectField { Key = "Tags", PropertyName = "Tags", DataType = (int)AyaDataType.Tags, SharedLTKey = true });
|
|
|
|
l.Add(new ObjectField { Key = "UserCustom1", PropertyName = "UserCustom1", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom2", PropertyName = "UserCustom2", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom3", PropertyName = "UserCustom3", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom4", PropertyName = "UserCustom4", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom5", PropertyName = "UserCustom5", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom6", PropertyName = "UserCustom6", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom7", PropertyName = "UserCustom7", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom8", PropertyName = "UserCustom8", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom9", PropertyName = "UserCustom9", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom10", PropertyName = "UserCustom10", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom11", PropertyName = "UserCustom11", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom12", PropertyName = "UserCustom12", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom13", PropertyName = "UserCustom13", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom14", PropertyName = "UserCustom14", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom15", PropertyName = "UserCustom15", Custom = true });
|
|
l.Add(new ObjectField { Key = "UserCustom16", PropertyName = "UserCustom16", Custom = true });
|
|
break;
|
|
#endregion
|
|
case TEST_WIDGET_USER_EMAIL_ADDRESS_LIST_KEY:
|
|
#region WIDGET_USER_EMAIL_ADDRESS_LIST_KEY
|
|
/*
|
|
Select awidget.id as widgetid, awidget.name as widgetname, auser.name as username, auser.id as userid, auseroptions.emailaddress
|
|
from awidget
|
|
left outer join auser on (awidget.userid=auser.id)
|
|
left outer join auseroptions on (auser.id=auseroptions.userid)
|
|
|
|
public string Key { get; set; }
|
|
public string PropertyName { get; set; }
|
|
public bool Hideable { get; set; }
|
|
public bool SharedLTKey { get; set; }
|
|
public bool Custom { get; set; }
|
|
public bool Filterable { get; set; }
|
|
public bool Sortable { get; set; }
|
|
public bool MiniAvailable { get; set; }
|
|
public int DataType { get; set; }
|
|
//If it's an enum DataType then this is the specific enum type which sb the name of the class that holds the enum in the server project
|
|
public string EnumType { get; set; }
|
|
//if field is a reference to another object (i.e. a client in a workorders list)
|
|
//then the type to open is set here
|
|
public int AyObjectType { get; set; }
|
|
|
|
//For query building
|
|
//This is the equivalent of the v7 SqlColumnNameAttribute
|
|
public string SqlIdColumn { get; set; }
|
|
public string SqlColumn { get; set; }
|
|
*/
|
|
l.Add(new ObjectField { Key = "df", AyObjectType = (int)AyaType.Widget });
|
|
l.Add(new ObjectField { Key = "WidgetName", PropertyName = "Name", DataType = (int)AyaDataType.Text, Hideable = false, });//not hideable for strictly list must mean it has to be in template?
|
|
|
|
l.Add(new ObjectField { Key = "User", PropertyName = "userid", DataType = (int)AyaDataType.Text, AyObjectType = (int)AyaType.User });
|
|
l.Add(new ObjectField { Key = "Name", PropertyName = "Name", SharedLTKey = true, DataType = (int)AyaDataType.Text, Hideable = false });
|
|
l.Add(new ObjectField { Key = "UserEmailAddress", PropertyName = "Emailaddress", DataType = (int)AyaDataType.EmailAddress, SqlColumn = "emailaddress" });
|
|
break;
|
|
#endregion
|
|
|
|
|
|
default:
|
|
throw new System.ArgumentOutOfRangeException($"ObjectFields: {key} is not a valid object key");
|
|
}
|
|
return l;
|
|
}
|
|
|
|
|
|
public static string TranslateLTCustomFieldToInternalCustomFieldName(string lTCustomFieldName)
|
|
{
|
|
var i = System.Convert.ToInt32(System.Text.RegularExpressions.Regex.Replace(
|
|
lTCustomFieldName, // Our input
|
|
"[^0-9]", // Select everything that is not in the range of 0-9
|
|
"" // Replace that with an empty string.
|
|
));
|
|
|
|
return $"c{i}";
|
|
}
|
|
|
|
|
|
|
|
//Standard mini COLUMN definition
|
|
public static string GenerateMINIListColumnsJSON(AyaType defaultLinkType)
|
|
{
|
|
return $"[ {{\"cm\":\"df\",\"dt\":0,\"ay\":{(int)defaultLinkType}}},{{\"cm\":\"Widget\",\"dt\":{(int)AyaDataType.Text},\"ay\":{(int)defaultLinkType}}}]";
|
|
}
|
|
|
|
|
|
//Accept a json template
|
|
//return a column list suitable for api list return
|
|
public static string GenerateListColumnsJSONFromTemplate(AyaType defaultLinkType, string ObjectKey, string template)
|
|
{
|
|
//parse the template
|
|
var jtemplate = JObject.Parse(template);
|
|
|
|
//get the fields list
|
|
var fields = ObjectFieldsList(ObjectKey);
|
|
|
|
//convert to strings (https://stackoverflow.com/a/33836599/8939)
|
|
var fullFields = ((JArray)jtemplate["full"]).ToObject<string[]>();
|
|
|
|
//Generate JSON fragment to return with column definitions
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.Append("[");
|
|
//df First column is always the df column
|
|
sb.Append($"{{\"cm\":\"df\",\"dt\":0,\"ay\":{(int)defaultLinkType}}}");
|
|
|
|
foreach (string s in fullFields)
|
|
{
|
|
ObjectField o = fields.FirstOrDefault(x => x.Key == s);
|
|
#if (DEBUG)
|
|
//Developers little helper
|
|
if (o == null)
|
|
{
|
|
throw new System.ArgumentNullException($"DEV ERROR in objectFields::GenerateListColumnsJSONFromTemplate - field {s} specified in template was NOT found in ObjectFields list for key \"{ObjectKey}\"");
|
|
}
|
|
#endif
|
|
|
|
if (o != null)
|
|
{//Here is where we can vet the field name, if it doesn't exist. For production we'll just ignore those ones
|
|
|
|
sb.Append(",");
|
|
sb.Append("{");
|
|
//Build required part of column definition
|
|
sb.Append($"\"cm\":\"{o.Key}\",\"dt\":{(int)o.DataType}");
|
|
|
|
//Has a AyObjectType? (linkable / openable)
|
|
if (o.AyObjectType != 0)
|
|
sb.Append($",\"ay\":{(int)o.AyObjectType}");
|
|
|
|
sb.Append("}");
|
|
|
|
}
|
|
}
|
|
sb.Append("]");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
|
|
|
|
}//eoc ObjectFields
|
|
|
|
public class ObjectField
|
|
{
|
|
public string Key { get; set; }
|
|
public string PropertyName { get; set; }
|
|
public bool Hideable { get; set; }
|
|
public bool SharedLTKey { get; set; }
|
|
public bool Custom { get; set; }
|
|
public bool Filterable { get; set; }
|
|
public bool Sortable { get; set; }
|
|
public bool MiniAvailable { get; set; }
|
|
public int DataType { get; set; }
|
|
//If it's an enum DataType then this is the specific enum type which sb the name of the class that holds the enum in the server project
|
|
public string EnumType { get; set; }
|
|
//if field is a reference to another object (i.e. a client in a workorders list)
|
|
//then the type to open is set here
|
|
public int AyObjectType { get; set; }
|
|
|
|
//For query building
|
|
//This is the equivalent of the v7 SqlColumnNameAttribute
|
|
public string SqlIdColumn { get; set; }
|
|
public string SqlColumn { get; set; }
|
|
|
|
|
|
public ObjectField()
|
|
{
|
|
//most common defaults
|
|
SharedLTKey = false;
|
|
Hideable = true;
|
|
Custom = false;
|
|
Filterable = true;
|
|
Sortable = true;
|
|
MiniAvailable = true;
|
|
//Set openable object type to no type which is the default and means it's not a link to another object
|
|
AyObjectType = (int)AyaType.NoType;
|
|
|
|
}
|
|
|
|
public string GetSqlColumnName()
|
|
{
|
|
if (string.IsNullOrEmpty(SqlColumn))
|
|
{
|
|
return PropertyName.ToLowerInvariant();
|
|
}
|
|
else
|
|
{
|
|
return SqlColumn;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}//ens
|