This commit is contained in:
30
server/PickList/AyaPickList.cs
Normal file
30
server/PickList/AyaPickList.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using Sockeye.Biz;
|
||||
using Newtonsoft.Json.Linq;
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
/// <summary>
|
||||
/// PickList object base class
|
||||
/// </summary>
|
||||
internal abstract class AyaPickList : IAyaPickList
|
||||
{
|
||||
public AyaPickList()
|
||||
{ }
|
||||
public string SQLFrom { get; set; }
|
||||
public List<AyaPickListFieldDefinition> ColumnDefinitions { get; set; }
|
||||
public AuthorizationRoles AllowedRoles { get; set; }
|
||||
public SockType DefaultListAType { get; set; }
|
||||
public string DefaultTemplate { get; set; }
|
||||
//return array of field keys in list view
|
||||
public List<string> GetFieldListFromTemplate(JArray template)
|
||||
{
|
||||
List<string> ret = new List<string>();
|
||||
for (int i = 0; i < template.Count; i++)
|
||||
{
|
||||
var cm = template[i];
|
||||
ret.Add(cm["fld"].Value<string>());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}//eoc
|
||||
}//eons
|
||||
42
server/PickList/AyaPickListFieldDefinition.cs
Normal file
42
server/PickList/AyaPickListFieldDefinition.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Sockeye.Biz;
|
||||
using Newtonsoft.Json;
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
//This class defines a field used for pick list templating querying processing editing and returning
|
||||
public class AyaPickListFieldDefinition
|
||||
{
|
||||
//CLIENT / SERVER Unique identifier used at BOTH client and server
|
||||
//also the sql displaycolumnname if identical
|
||||
public string FieldKey { get; set; }
|
||||
//CLIENT Use only for display
|
||||
public string TKey { get; set; }
|
||||
// Used for casting query
|
||||
public UiFieldDataType ColumnDataType { get; set; }
|
||||
public bool IsRowId { get; set; }//both indicates is row ID but also that it's required as the only required field. Not technically necessary maybe but to prevent foot shooting.
|
||||
public bool IsActiveColumn { get; set; }
|
||||
[JsonIgnore]
|
||||
public string SqlIdColumnName { get; set; }
|
||||
[JsonIgnore]
|
||||
public string SqlValueColumnName { get; set; }
|
||||
|
||||
public AyaPickListFieldDefinition()
|
||||
{
|
||||
//most common defaults
|
||||
IsRowId = false;
|
||||
IsActiveColumn = false;
|
||||
}
|
||||
|
||||
//Get column to query for display name or use FieldName if there is no difference
|
||||
public string GetSqlValueColumnName()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SqlValueColumnName))
|
||||
{
|
||||
return FieldKey.ToLowerInvariant();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SqlValueColumnName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
server/PickList/CustomerPickList.cs
Normal file
86
server/PickList/CustomerPickList.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Sockeye.Biz;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal class CustomerPickList : AyaPickList, IAyaPickListVariant
|
||||
{
|
||||
public CustomerPickList()
|
||||
{
|
||||
|
||||
DefaultListAType = SockType.Customer;
|
||||
SQLFrom = "from acustomer ";
|
||||
AllowedRoles = BizRoles.GetRoleSet(DefaultListAType).Select;
|
||||
dynamic dTemplate = new JArray();
|
||||
|
||||
dynamic cm = new JObject();
|
||||
cm.fld = "customername";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
cm = new JObject();
|
||||
cm.fld = "customertags";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
base.DefaultTemplate = dTemplate.ToString(Newtonsoft.Json.Formatting.None);
|
||||
|
||||
//NOTE: Due to the join, all the sql id and name fields that can conflict with the joined table need to be specified completely
|
||||
ColumnDefinitions = new List<AyaPickListFieldDefinition>();
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Active",
|
||||
FieldKey = "customeractive",
|
||||
ColumnDataType = UiFieldDataType.Bool,
|
||||
SqlValueColumnName = "acustomer.active",
|
||||
IsActiveColumn = true
|
||||
});
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Name",
|
||||
FieldKey = "customername",
|
||||
ColumnDataType = UiFieldDataType.Text,
|
||||
SqlIdColumnName = "acustomer.id",
|
||||
SqlValueColumnName = "acustomer.name",
|
||||
IsRowId = true
|
||||
});
|
||||
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Tags",
|
||||
FieldKey = "customertags",
|
||||
ColumnDataType = UiFieldDataType.Tags,
|
||||
SqlValueColumnName = "acustomer.tags"
|
||||
});
|
||||
|
||||
}
|
||||
public string GetVariantCriteria(string variant)
|
||||
{
|
||||
//Currently the only variant is a object type and id to indicate headoffice
|
||||
|
||||
//ClientCriteria format for this list is "OBJECTID,AYATYPE"
|
||||
var crit = (variant ?? "").Split(',').Select(z => z.Trim()).ToArray();
|
||||
if (crit.Length > 1)
|
||||
{
|
||||
int nType = 0;
|
||||
if (!int.TryParse(crit[1], out nType)) return string.Empty;
|
||||
SockType forType = (SockType)nType;
|
||||
if (forType != SockType.HeadOffice) return string.Empty;
|
||||
|
||||
long lId = 0;
|
||||
if (!long.TryParse(crit[0], out lId)) return string.Empty;
|
||||
if (lId == 0) return string.Empty;
|
||||
|
||||
//Have valid type, have an id, so filter away
|
||||
switch (forType)
|
||||
{
|
||||
case SockType.HeadOffice:
|
||||
{
|
||||
return $"acustomer.headofficeid = {lId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}//eoc
|
||||
}//eons
|
||||
56
server/PickList/HeadOfficePickList.cs
Normal file
56
server/PickList/HeadOfficePickList.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Sockeye.Biz;
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal class HeadOfficePickList : AyaPickList
|
||||
{
|
||||
public HeadOfficePickList()
|
||||
{
|
||||
|
||||
DefaultListAType = SockType.HeadOffice;
|
||||
SQLFrom = "from aheadoffice";
|
||||
AllowedRoles = BizRoles.GetRoleSet(DefaultListAType).Select;
|
||||
dynamic dTemplate = new JArray();
|
||||
|
||||
dynamic cm = new JObject();
|
||||
cm.fld = "headofficename";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
cm = new JObject();
|
||||
cm.fld = "headofficetags";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
base.DefaultTemplate = dTemplate.ToString(Newtonsoft.Json.Formatting.None);
|
||||
|
||||
//NOTE: Due to the join, all the sql id and name fields that can conflict with the joined table need to be specified completely
|
||||
ColumnDefinitions = new List<AyaPickListFieldDefinition>();
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Active",
|
||||
FieldKey = "headofficeactive",
|
||||
ColumnDataType = UiFieldDataType.Bool,
|
||||
SqlValueColumnName = "aheadoffice.active",
|
||||
IsActiveColumn = true
|
||||
});
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Name",
|
||||
FieldKey = "headofficename",
|
||||
ColumnDataType = UiFieldDataType.Text,
|
||||
SqlIdColumnName = "aheadoffice.id",
|
||||
SqlValueColumnName = "aheadoffice.name",
|
||||
IsRowId = true
|
||||
});
|
||||
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Tags",
|
||||
FieldKey = "headofficetags",
|
||||
ColumnDataType = UiFieldDataType.Tags,
|
||||
SqlValueColumnName = "aheadoffice.tags"
|
||||
});
|
||||
|
||||
}
|
||||
}//eoc
|
||||
}//eons
|
||||
20
server/PickList/IAyaPickList.cs
Normal file
20
server/PickList/IAyaPickList.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Sockeye.Biz;
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal interface IAyaPickList
|
||||
{
|
||||
//sql query from fragment with table joins et
|
||||
string SQLFrom { get; set; }
|
||||
//List of fields for this object
|
||||
List<AyaPickListFieldDefinition> ColumnDefinitions { get; set; }
|
||||
//allowed roles to access this list
|
||||
AuthorizationRoles AllowedRoles { get; set; }
|
||||
//Default object type to open for rows of this list (use no object if no)
|
||||
SockType DefaultListAType { get; set; }
|
||||
//Default / STOCK template when none is specified
|
||||
string DefaultTemplate { get; set; }
|
||||
List<string> GetFieldListFromTemplate(JArray fieldListArray);
|
||||
}
|
||||
}
|
||||
7
server/PickList/IAyaPickListVariant.cs
Normal file
7
server/PickList/IAyaPickListVariant.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal interface IAyaPickListVariant
|
||||
{
|
||||
string GetVariantCriteria(string variant);
|
||||
}
|
||||
}
|
||||
71
server/PickList/PickListFactory.cs
Normal file
71
server/PickList/PickListFactory.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using Sockeye.Biz;
|
||||
using Sockeye.Models;
|
||||
|
||||
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal static class PickListFactory
|
||||
{
|
||||
|
||||
//Instantiate list object specified from type
|
||||
internal static IAyaPickList GetAyaPickList(SockType sockType)
|
||||
{
|
||||
switch (sockType)
|
||||
{
|
||||
//CoreBizObject add here if it will be "picked" on any other form
|
||||
|
||||
case SockType.Customer:
|
||||
return new CustomerPickList() as IAyaPickList;
|
||||
case SockType.HeadOffice:
|
||||
return new HeadOfficePickList() as IAyaPickList;
|
||||
case SockType.User:
|
||||
return new UserPickList() as IAyaPickList;
|
||||
|
||||
case SockType.Report:
|
||||
return new ReportPickList() as IAyaPickList;
|
||||
|
||||
//@##### WARNING: BE SURE TO ADD NEW TYPES BELOW OR USERS WON"T BE ABLE TO EDIT THE TEMPLATE FOR THEM
|
||||
|
||||
default:
|
||||
throw new System.NotImplementedException($"PICKLIST {sockType} NOT IMPLEMENTED");
|
||||
|
||||
}
|
||||
//return null;
|
||||
}
|
||||
|
||||
//List all the PickList-able object types available
|
||||
internal static List<NameIdItem> GetListOfAllPickListTypes(long TranslationId)
|
||||
{
|
||||
List<string> TranslationKeysToFetch = new List<string>();
|
||||
List<NameIdItem> ret = new List<NameIdItem>();
|
||||
|
||||
List<SockType> values = new List<SockType>();
|
||||
values.Add(SockType.Customer);
|
||||
values.Add(SockType.HeadOffice);
|
||||
values.Add(SockType.Report);
|
||||
values.Add(SockType.User);
|
||||
|
||||
|
||||
//### NEW ONES HERE
|
||||
|
||||
|
||||
|
||||
foreach (SockType t in values)
|
||||
{
|
||||
// if (t.HasAttribute(typeof(CoreBizObjectAttribute)))
|
||||
// {
|
||||
var name = t.ToString();
|
||||
TranslationKeysToFetch.Add(name);
|
||||
ret.Add(new NameIdItem() { Name = name, Id = (long)t });
|
||||
// }
|
||||
}
|
||||
var LT = TranslationBiz.GetSubsetStaticAsync(TranslationKeysToFetch, TranslationId).Result;
|
||||
foreach (NameIdItem i in ret)
|
||||
{
|
||||
i.Name = LT[i.Name];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}//eoc
|
||||
}//eons
|
||||
101
server/PickList/PickListFetcher.cs
Normal file
101
server/PickList/PickListFetcher.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Sockeye.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal static class PickListFetcher
|
||||
{
|
||||
internal static async Task<List<NameIdActiveItem>> GetResponseAsync(IAyaPickList PickList, string autoCompleteQuery,
|
||||
string tagSpecificQuery, bool includeInactive, long[] preIds, string variant, AyContext ct, ILogger log, string overrideTemplate)
|
||||
{
|
||||
|
||||
//Sort out effective Template
|
||||
string Template = null;
|
||||
if (string.IsNullOrWhiteSpace(overrideTemplate))
|
||||
{
|
||||
//Attempt to fetch custom template
|
||||
var t = await ct.PickListTemplate.FirstOrDefaultAsync(z => z.Id == ((long)PickList.DefaultListAType));
|
||||
if (t == null)
|
||||
{
|
||||
Template = PickList.DefaultTemplate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Template = t.Template;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Template = overrideTemplate;
|
||||
}
|
||||
|
||||
//parse the template
|
||||
var jTemplate = JArray.Parse(Template);
|
||||
|
||||
//Get the field key names in a list from the template
|
||||
List<string> TemplateColumnNames = PickList.GetFieldListFromTemplate(jTemplate);
|
||||
|
||||
//BUILD THE QUERY
|
||||
var q = PickListSqlBuilder.Build(PickList, TemplateColumnNames, autoCompleteQuery, tagSpecificQuery, includeInactive, preIds, variant);
|
||||
|
||||
//If want to log all queries
|
||||
//log.LogInformation(q);
|
||||
|
||||
//RETURN OBJECTS
|
||||
var ret = new List<NameIdActiveItem>();
|
||||
|
||||
//QUERY THE DB
|
||||
using (var command = ct.Database.GetDbConnection().CreateCommand())
|
||||
{
|
||||
await ct.Database.OpenConnectionAsync();
|
||||
|
||||
//GET DATA RETURN ROWS
|
||||
command.CommandText = q;
|
||||
try
|
||||
{
|
||||
using (var dr = await command.ExecuteReaderAsync())
|
||||
{
|
||||
while (dr.Read())
|
||||
{
|
||||
//query is always in the same order:
|
||||
//plId, plActive, plName
|
||||
ret.Add(new NameIdActiveItem
|
||||
{
|
||||
Id = dr.GetInt64(0),
|
||||
Active = dr.GetBoolean(1),
|
||||
Name = dr.GetString(2)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Npgsql.PostgresException e)
|
||||
{
|
||||
//log out the exception and the query
|
||||
//This may be called internally in *Biz classes who don't have a log of their own
|
||||
if (log == null)
|
||||
log = Sockeye.Util.ApplicationLogging.CreateLogger("PickListFetcher");
|
||||
log.LogError("PickList query failed unexpectedly. Query was:");
|
||||
log.LogError(q);
|
||||
log.LogError(e, "DB Exception");
|
||||
throw new System.Exception("PickListFetcher - Query failed see log");
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
//ensure any other type of exception gets surfaced properly
|
||||
//log out the exception and the query
|
||||
log.LogError("PickListFetcher unexpected failure. Query was:");
|
||||
log.LogError(q);
|
||||
|
||||
log.LogError(e, "Exception");
|
||||
throw new System.Exception("PickListFetcher - unexpected failure see log");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}//eoc
|
||||
}//eons
|
||||
73
server/PickList/PickListOptions.cs
Normal file
73
server/PickList/PickListOptions.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using Sockeye.Biz;
|
||||
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
|
||||
/*
|
||||
/// <param name="params">The SockType object type to select from</param>
|
||||
/// <param name="query">The query to filter the returned list by. Query text as provided will be case sensitively matched to all templated fields.
|
||||
/// independentely of this, if an addition space separated string that begins with two consecutive periods is encountered that will be considered a separate match to the TAGS collection of each object
|
||||
/// So a tag query might be entered as "..zon some" which would match all tags LIKE 'zon' and template fields LIKE 'some'</param>
|
||||
/// <param name="inactive">Include inactive objects in the returned list </param>
|
||||
/// <param name="preIds">Return only specific items (for pre-selected items on forms) </param>
|
||||
/// <param name="variant">Some lists optionally take a variant string, e.g. User type "inside","outside" etc </param>
|
||||
*/
|
||||
public sealed class PickListOptions
|
||||
{
|
||||
|
||||
[FromBody]
|
||||
public SockType SockType { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public string Query { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public bool Inactive { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public List<long> PreselectedIds { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public string ListVariant { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public string Template { get; set; }
|
||||
|
||||
public PickListOptions()
|
||||
{
|
||||
SockType = SockType.NoType;
|
||||
Query = string.Empty;
|
||||
Inactive = false;
|
||||
PreselectedIds = new List<long>();
|
||||
ListVariant = string.Empty;
|
||||
Template = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PickListSingleOptions
|
||||
{
|
||||
|
||||
[FromBody]
|
||||
public SockType SockType { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public long Id { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public string ListVariant { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public string Template { get; set; }
|
||||
|
||||
public PickListSingleOptions()
|
||||
{
|
||||
SockType = SockType.NoType;
|
||||
Id = 0;
|
||||
ListVariant = string.Empty;
|
||||
Template = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
318
server/PickList/PickListSqlBuilder.cs
Normal file
318
server/PickList/PickListSqlBuilder.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Sockeye.Biz;
|
||||
using Sockeye.Util;
|
||||
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
|
||||
internal static class PickListSqlBuilder
|
||||
{
|
||||
|
||||
|
||||
//Maximum number of results to return at any given time
|
||||
//did a little research and may adjust this but it can be fairly girthy in this day and age
|
||||
//and many people might not want or need to autocomplete type if we provide enough leeway.
|
||||
|
||||
const int MAXIMUM_RESULT_COUNT = 100;
|
||||
|
||||
//Build the query for a picklist request
|
||||
internal static string Build(IAyaPickList pickList, List<string> templateColumnNames, string autoCompleteQuery, string tagSpecificQuery, bool IncludeInactive, long[] preIds, string variant)
|
||||
{
|
||||
|
||||
//determine this in advance as it will be used in a loop later
|
||||
bool HasAutoCompleteQuery = !string.IsNullOrWhiteSpace(autoCompleteQuery);
|
||||
bool HasTagSpecificQuery = !string.IsNullOrWhiteSpace(tagSpecificQuery);
|
||||
|
||||
|
||||
//Variables to collect the data needed to create the actual clauses later
|
||||
List<string> lSelect = new List<string>();
|
||||
List<string> lWhere = new List<string>();
|
||||
List<string> lOrderBy = new List<string>();
|
||||
|
||||
string PlIdSelectFragment = string.Empty;
|
||||
string ActiveSelectFragment = string.Empty;
|
||||
string ActiveWhereFragment = string.Empty;
|
||||
string TagSpecificWhereFragment = string.Empty;
|
||||
string PredefinedOnlyWhereFragment = string.Empty;
|
||||
|
||||
string VariantWhereFragment = string.Empty;
|
||||
bool HasVariantWhereFragment = false;
|
||||
if (!string.IsNullOrWhiteSpace(variant) && pickList is IAyaPickListVariant)
|
||||
{
|
||||
VariantWhereFragment = ((IAyaPickListVariant)pickList).GetVariantCriteria(variant);
|
||||
HasVariantWhereFragment = !string.IsNullOrWhiteSpace(VariantWhereFragment);
|
||||
}
|
||||
|
||||
//PROCESS ROW ID "VALUE" COLUMN
|
||||
//
|
||||
AyaPickListFieldDefinition rowIdColumn = pickList.ColumnDefinitions.FirstOrDefault(z => z.IsRowId == true);
|
||||
//this should only happen with a development error
|
||||
if (rowIdColumn == null)
|
||||
throw new System.ArgumentNullException($"DEV ERROR in PickListSqlBuilder.cs: picklist for {pickList.DefaultListAType.ToString()} has no rowId column specified in columnDefinitions list");
|
||||
PlIdSelectFragment = rowIdColumn.SqlIdColumnName + " as plId";
|
||||
|
||||
|
||||
|
||||
if (preIds.Length > 0)
|
||||
{
|
||||
//select id,name from acustomer where id in(1,3,5,7)
|
||||
//string.Join(",", arr)
|
||||
// PredefinedOnlyWhereFragment = rowIdColumn.SqlIdColumnName + " = " + preId.ToString();
|
||||
PredefinedOnlyWhereFragment = $"{rowIdColumn.SqlIdColumnName} in ({string.Join(",", preIds)})";
|
||||
}
|
||||
|
||||
|
||||
//PROCESS ACTIVE COLUMN
|
||||
//
|
||||
//NOTE: default is to filter *out* inactive objects because that's the most common need at the Client
|
||||
//but we provide the override for that if necessary as there are often (management usually) cases where user needs to select inactive records
|
||||
|
||||
//add active column, fake it if necessary
|
||||
AyaPickListFieldDefinition activeColumn = pickList.ColumnDefinitions.FirstOrDefault(z => z.IsActiveColumn == true);
|
||||
if (activeColumn == null)
|
||||
{
|
||||
|
||||
//no active column which is normal for some types of objects
|
||||
//so make a fake one and return them all as active=true as all lists must return the same format
|
||||
ActiveSelectFragment = "true as plActive";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//we have an active column, set accordingly
|
||||
|
||||
//regardless of wanting to see inactive, we always want to see the column itself
|
||||
ActiveSelectFragment = activeColumn.SqlValueColumnName + " as plActive";
|
||||
|
||||
//this is the normal path unless there is an override
|
||||
//if there is an override to see inactive too then we just don't set the filter on active
|
||||
if (!IncludeInactive)
|
||||
{
|
||||
if (preIds.Length > 0)
|
||||
{
|
||||
//pre-selected need to always appear regardless of active status
|
||||
//ActiveWhereFragment = $"({rowIdColumn.SqlIdColumnName} = {preId}) or ({activeColumn.SqlValueColumnName} = true)";
|
||||
ActiveWhereFragment = $"({rowIdColumn.SqlIdColumnName} in ({string.Join(",", preIds)})) or ({activeColumn.SqlValueColumnName} = true)";
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveWhereFragment = activeColumn.SqlValueColumnName + " = true";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//PROCESS TAG SPECIFIC QUERY
|
||||
//
|
||||
if (HasTagSpecificQuery)
|
||||
{
|
||||
//get the tag column
|
||||
AyaPickListFieldDefinition tagColumn = pickList.ColumnDefinitions.FirstOrDefault(z => z.ColumnDataType == UiFieldDataType.Tags);
|
||||
TagSpecificWhereFragment = $"(array_to_string({tagColumn.GetSqlValueColumnName()},',') like '%{tagSpecificQuery}%')";
|
||||
}
|
||||
|
||||
|
||||
//PROCESS TEMPLATED COLUMNS TO BE RETURNED IN RESULTS
|
||||
//
|
||||
foreach (string ColumnName in templateColumnNames)
|
||||
{
|
||||
|
||||
AyaPickListFieldDefinition o = pickList.ColumnDefinitions.FirstOrDefault(z => z.FieldKey == ColumnName);
|
||||
#if (DEBUG)
|
||||
if (o == null)
|
||||
{
|
||||
throw new System.ArgumentNullException($"DEV ERROR in PickListSqlBuilder.cs: field {ColumnName} specified in template was NOT found in columnDefinitions list");
|
||||
}
|
||||
#endif
|
||||
if (o != null)
|
||||
{//Ignore missing fields in production
|
||||
|
||||
var valueColumnName = o.GetSqlValueColumnName();
|
||||
|
||||
string sWhere = string.Empty;
|
||||
|
||||
//TAGS COLUMN
|
||||
//
|
||||
if (o.ColumnDataType == UiFieldDataType.Tags)
|
||||
{
|
||||
lSelect.Add($"(array_to_string({valueColumnName},','))");
|
||||
//tags can order by without the arraytostring
|
||||
lOrderBy.Add(valueColumnName);
|
||||
|
||||
//THIS is the best filter method for a like comparison to each individual tag:
|
||||
//(array_to_string(acustomer.tags,',') like '%zo%')
|
||||
//Note that a tag specific query takes precendence over this which exists
|
||||
//in cases where there are tags in the template and the user has not specified a tag specific query
|
||||
//so this will handle it as a like query against all tags as a composite string of text just like
|
||||
//all the other templated fields
|
||||
if (HasAutoCompleteQuery && !HasTagSpecificQuery)
|
||||
{
|
||||
if (ServerGlobalBizSettings.Cache.FilterCaseSensitive)
|
||||
sWhere = $"(array_to_string({valueColumnName},',') like '%{autoCompleteQuery}%')";
|
||||
else
|
||||
sWhere = $"(lower(array_to_string({valueColumnName},',')) like lower('%{autoCompleteQuery}%'))";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if (o.ColumnDataType == UiFieldDataType.Text || o.ColumnDataType == UiFieldDataType.EmailAddress || o.ColumnDataType == UiFieldDataType.HTTP)
|
||||
{
|
||||
//TEXT COLUMN
|
||||
//
|
||||
lSelect.Add(valueColumnName);
|
||||
lOrderBy.Add(valueColumnName);
|
||||
if (HasAutoCompleteQuery)
|
||||
if (ServerGlobalBizSettings.Cache.FilterCaseSensitive)
|
||||
sWhere = $"({valueColumnName} like '%{autoCompleteQuery}%')";
|
||||
else
|
||||
sWhere = $"(lower({valueColumnName}) like lower('%{autoCompleteQuery}%'))";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//NON-TEXT COLUMN
|
||||
//
|
||||
|
||||
//Note: if any part of a select contatenation query using the || postgres concat is text then all fields are automatically converted to text
|
||||
//so no need to make it text here as the automatic spacing character will force the whole thing to a text concat anyway
|
||||
//ref: https://stackoverflow.com/a/19943343/8939
|
||||
lSelect.Add(valueColumnName);
|
||||
|
||||
//order by for now seems to be best as just order by it's value whatever it is
|
||||
lOrderBy.Add(valueColumnName);
|
||||
|
||||
//Where fragment is different for non text fields: it needs to be cast to text to like query on it
|
||||
if (HasAutoCompleteQuery)
|
||||
if (ServerGlobalBizSettings.Cache.FilterCaseSensitive)
|
||||
sWhere = $"(cast ({valueColumnName} as text) like '%{autoCompleteQuery}%')";
|
||||
else
|
||||
sWhere = $"(lower(cast ({valueColumnName} as text)) like lower('%{autoCompleteQuery}%'))";
|
||||
}
|
||||
|
||||
|
||||
if (HasAutoCompleteQuery && !string.IsNullOrWhiteSpace(sWhere))//swhere can be empty on a tag in a tag specific query
|
||||
lWhere.Add(sWhere);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
//SELECT
|
||||
sb.Append("select ");
|
||||
|
||||
//ID COLUMN
|
||||
sb.Append(PlIdSelectFragment);
|
||||
sb.Append(", ");
|
||||
|
||||
//ACTIVE COLUMN
|
||||
sb.Append(ActiveSelectFragment);
|
||||
sb.Append(", ");
|
||||
|
||||
//nope, this will return null if any of the values are null, very bad for this use, instead
|
||||
//select name || ' ' || serial || ' ' || array_to_string(tags,',') as display from acustomer
|
||||
//this, on the other hand will work even if all of them are null
|
||||
//concat_ws(' ', acustomer.name, acustomer.serial, auser.name)
|
||||
|
||||
sb.Append("concat_ws(' ', ");
|
||||
foreach (string s in lSelect)
|
||||
{
|
||||
sb.Append(s);
|
||||
sb.Append(",");
|
||||
}
|
||||
//clear trailing comma
|
||||
sb.Length -= 1;
|
||||
sb.Append(") as plname");
|
||||
|
||||
//FROM
|
||||
sb.Append(" ");
|
||||
sb.Append(pickList.SQLFrom);
|
||||
|
||||
|
||||
//WHERE
|
||||
//there is a condition where there is no where (inactive=true and no query of any kind)
|
||||
if (preIds.Length > 0 || lWhere.Count > 0 || HasTagSpecificQuery || HasVariantWhereFragment || IncludeInactive == false)
|
||||
{
|
||||
sb.Append(" where ");
|
||||
|
||||
if (HasVariantWhereFragment)
|
||||
{
|
||||
sb.Append($"({VariantWhereFragment}) and ");
|
||||
}
|
||||
|
||||
if (HasTagSpecificQuery)
|
||||
{
|
||||
sb.Append(TagSpecificWhereFragment);
|
||||
sb.Append(" and ");
|
||||
}
|
||||
|
||||
if (!IncludeInactive)
|
||||
{
|
||||
sb.Append(ActiveWhereFragment);
|
||||
sb.Append(" and ");
|
||||
}
|
||||
|
||||
if (preIds.Length > 0)
|
||||
{
|
||||
sb.Append(PredefinedOnlyWhereFragment);
|
||||
sb.Append(" and ");
|
||||
}
|
||||
|
||||
if (lWhere.Count > 0)
|
||||
{
|
||||
//Put all the regular query terms in parenthesis to ensure it's all treated as one criteria
|
||||
|
||||
sb.Append("(");
|
||||
foreach (string s in lWhere)
|
||||
{
|
||||
sb.Append(s);
|
||||
sb.Append(" or ");
|
||||
}
|
||||
//clear trailing or
|
||||
|
||||
sb.Length -= 4;
|
||||
//enclosing parenthesis
|
||||
|
||||
|
||||
sb.Append(")");
|
||||
}
|
||||
else
|
||||
{
|
||||
//we might have a trailing and to remove
|
||||
//{select acustomer.id as plId, acustomer.active as plActive, concat_ws(' ', acustomer.name) as plname from acustomer where (acustomer.headofficeid = 2) and }
|
||||
if(sb.ToString().EndsWith(" and "))
|
||||
sb.Length-=5;
|
||||
//
|
||||
// if (!IncludeInactive || HasTagSpecificQuery || preIds.Length > 0)
|
||||
// {
|
||||
// //trailing " and " to remove
|
||||
// sb.Length -= 5;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//ORDER BY
|
||||
sb.Append(" order by ");
|
||||
foreach (string s in lOrderBy)
|
||||
{
|
||||
sb.Append(s);
|
||||
sb.Append(",");
|
||||
}
|
||||
//clear trailing comma
|
||||
sb.Length--;
|
||||
|
||||
//LIMIT
|
||||
sb.Append($" limit {MAXIMUM_RESULT_COUNT}");
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
//"select acustomer.id as plId || ' 'acustomer.active as plActive || ' 'acustomer.name || ' 'acustomer.serial || ' 'auser.name as plname from acustomer left join auser on (acustomer.userid=auser.id)
|
||||
//where acustomer.active = true and ((acustomer.name like '%on%') or (cast (acustomer.serial as text) like '%on%') or (auser.name like '%on%')) order by acustomer.name,acustomer.serial,auser.name limit 100"
|
||||
|
||||
|
||||
}//eoc
|
||||
}//ens
|
||||
56
server/PickList/ReportPickList.cs
Normal file
56
server/PickList/ReportPickList.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Sockeye.Biz;
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal class ReportPickList : AyaPickList, IAyaPickListVariant
|
||||
{
|
||||
public ReportPickList()
|
||||
{
|
||||
|
||||
DefaultListAType = SockType.Report;
|
||||
SQLFrom = "from areport";
|
||||
AllowedRoles = BizRoles.GetRoleSet(DefaultListAType).Select;
|
||||
dynamic dTemplate = new JArray();
|
||||
|
||||
dynamic cm = new JObject();
|
||||
cm.fld = "reportname";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
base.DefaultTemplate = dTemplate.ToString(Newtonsoft.Json.Formatting.None);
|
||||
|
||||
//NOTE: Due to the join, all the sql id and name fields that can conflict with the joined table need to be specified completely
|
||||
ColumnDefinitions = new List<AyaPickListFieldDefinition>();
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Active",
|
||||
FieldKey = "reportactive",
|
||||
ColumnDataType = UiFieldDataType.Bool,
|
||||
SqlValueColumnName = "areport.active",
|
||||
IsActiveColumn = true
|
||||
});
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Name",
|
||||
FieldKey = "reportname",
|
||||
ColumnDataType = UiFieldDataType.Text,
|
||||
SqlIdColumnName = "areport.id",
|
||||
SqlValueColumnName = "areport.name",
|
||||
IsRowId = true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public string GetVariantCriteria(string variant)
|
||||
{
|
||||
//has for type variant
|
||||
if (string.IsNullOrWhiteSpace(variant))
|
||||
return string.Empty;
|
||||
|
||||
int forType;
|
||||
if (!int.TryParse(variant, out forType))
|
||||
return string.Empty;
|
||||
return $"areport.sockType={forType}";
|
||||
}
|
||||
}//eoc
|
||||
}//eons
|
||||
80
server/PickList/UserPickList.cs
Normal file
80
server/PickList/UserPickList.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Sockeye.Biz;
|
||||
namespace Sockeye.PickList
|
||||
{
|
||||
internal class UserPickList : AyaPickList, IAyaPickListVariant
|
||||
{
|
||||
public UserPickList()
|
||||
{
|
||||
|
||||
DefaultListAType = SockType.User;
|
||||
SQLFrom = "from auser";
|
||||
AllowedRoles = BizRoles.GetRoleSet(DefaultListAType).Select;
|
||||
dynamic dTemplate = new JArray();
|
||||
|
||||
dynamic cm = new JObject();
|
||||
cm.fld = "username";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
cm = new JObject();
|
||||
cm.fld = "useremployeenumber";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
cm = new JObject();
|
||||
cm.fld = "usertags";
|
||||
dTemplate.Add(cm);
|
||||
|
||||
base.DefaultTemplate = dTemplate.ToString(Newtonsoft.Json.Formatting.None);
|
||||
|
||||
//NOTE: Due to the join, all the sql id and name fields that can conflict with the joined table need to be specified completely
|
||||
ColumnDefinitions = new List<AyaPickListFieldDefinition>();
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Active",
|
||||
FieldKey = "useractive",
|
||||
ColumnDataType = UiFieldDataType.Bool,
|
||||
SqlValueColumnName = "auser.active",
|
||||
IsActiveColumn = true
|
||||
});
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Name",
|
||||
FieldKey = "username",
|
||||
ColumnDataType = UiFieldDataType.Text,
|
||||
SqlIdColumnName = "auser.id",
|
||||
SqlValueColumnName = "auser.name",
|
||||
IsRowId = true
|
||||
});
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "UserEmployeeNumber",
|
||||
FieldKey = "useremployeenumber",
|
||||
ColumnDataType = UiFieldDataType.Text,
|
||||
SqlValueColumnName = "auser.employeenumber"
|
||||
});
|
||||
|
||||
ColumnDefinitions.Add(new AyaPickListFieldDefinition
|
||||
{
|
||||
TKey = "Tags",
|
||||
FieldKey = "usertags",
|
||||
ColumnDataType = UiFieldDataType.Tags,
|
||||
SqlValueColumnName = "auser.tags"
|
||||
});
|
||||
}
|
||||
|
||||
public string GetVariantCriteria(string variant)
|
||||
{
|
||||
switch (variant)
|
||||
{
|
||||
case "inside":
|
||||
return $"auser.usertype!={(int)UserType.Customer} and auser.usertype!={(int)UserType.HeadOffice}";
|
||||
case "outside":
|
||||
return $"auser.usertype={(int)UserType.Customer} or auser.usertype={(int)UserType.HeadOffice}";
|
||||
case "tech":
|
||||
return $"auser.usertype={(int)UserType.Service} or auser.usertype={(int)UserType.ServiceContractor}";
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}//eoc
|
||||
}//eons
|
||||
Reference in New Issue
Block a user