This commit is contained in:
@@ -30,6 +30,12 @@ UI FEATURE - The ability to customize forms:
|
||||
if nothing has changed the server returns the unchanged code or instead returns all the data if it is changed to save bandwidth
|
||||
- RAVEN custom fields start with 1 not zero like in v7
|
||||
- Import v7 custom0 to RAVEN custom1
|
||||
- SERVER HANDLING
|
||||
- Custom fields are stored *with* their associated record in a single TEXT column as a serialized JSON object
|
||||
- {c1:"blah",c2:"blah",c3:"blah".....c16:"blah"}
|
||||
- According to the docs https://www.postgresql.org/docs/9.1/datatype-character.html this is pretty efficient
|
||||
- I don't like the idea of an outside table holding all custom values, it would be hammered pretty hard, this way the data stays with it's record
|
||||
- The server doesn't validate it or anything, just stores what the client provides and retrieves what the client needs and the client co-erces the value to the correct type
|
||||
|
||||
|
||||
## TODO
|
||||
@@ -50,7 +56,7 @@ UI FEATURE - The ability to customize forms:
|
||||
- Method: IsValidForm(string formKey) bool exists or not
|
||||
|
||||
|
||||
- Need a FormCustom controller that supports routes for:
|
||||
- #DONE# Need a FormCustom controller that supports routes for:
|
||||
- #DONE# Routes for customization form populating and receiving
|
||||
- GET (formkey), gets a FormOptions object that provides the basis for building the form customization view
|
||||
- Also required at the client is the current form customizations if any which can be gotten from the other route for day to day ops below
|
||||
@@ -59,14 +65,16 @@ UI FEATURE - The ability to customize forms:
|
||||
- Localization changes are NOT handled here even though it's the same client end form, that is handled at the CLIENT end after it has updated this part separately through regular localized text routes
|
||||
- To ensure proper separation of concerns
|
||||
|
||||
- Routes for the day to day form display purposes that efficiently fetch the form customization to use on demand and caches at the client
|
||||
- #DONE# Routes for the day to day form display purposes that efficiently fetch the form customization to use on demand and caches at the client
|
||||
- (GET formkey, token): Given a formkey and an *optional* concurrency token, returns one of the following:
|
||||
- If no concurrency token or doesn't match the current db one then:
|
||||
- a list of customized fields (not the non customized ones) so the client can display the UI correctly:
|
||||
- Basically whatever is in the FormCustom table record for that formkey
|
||||
- If concurrency token provided and is unchanged then simply returns a code 304 (NOT MODIFIED)
|
||||
|
||||
- TESTS for the above!!!
|
||||
- #DONE# TESTS for the above!!!
|
||||
|
||||
- Put custom fields into widget object or figure out how we will handle that aspect
|
||||
|
||||
|
||||
EXISTING v7:
|
||||
|
||||
@@ -19,6 +19,8 @@ SERVER
|
||||
|
||||
- PickListFetcher: WTF? It has a reference to widgetbiz in it, isn't this a generic class??
|
||||
|
||||
- AySchema: License table extra fields, remove any not required (also in model and code)
|
||||
|
||||
- Resource localization edit all Custom field locale keys for all langauges and change to be 1 to 16 (remove 0 and add 6 more)
|
||||
- So, for example ClientCustom0 becomes ClientCustom1 and make sure the display values are identical as right now there are "My Custom0" and "Custom Field 8" in the same bunch
|
||||
- Copy from WidgetCustom1 to 16 if necessary or for reference
|
||||
@@ -50,8 +52,11 @@ SERVER
|
||||
- PASETO instead of JWT??
|
||||
- https://paseto.io/
|
||||
|
||||
- UPDATE: Update all modules at server and re-test
|
||||
- UPDATE: Update all 3rd party libs in use with server and re-test
|
||||
- It's been a while, some of the modules date to last fall
|
||||
- Test on OPS server
|
||||
|
||||
|
||||
|
||||
DOCS / MANUAL
|
||||
- Disable google fonts for manual / docs generator:
|
||||
|
||||
50
server/AyaNova/biz/CustomFieldsValidator.cs
Normal file
50
server/AyaNova/biz/CustomFieldsValidator.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using AyaNova.Models;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
namespace AyaNova.Biz
|
||||
{
|
||||
internal static class CustomFieldsValidator
|
||||
{
|
||||
internal static void Validate(BizObject biz, FormCustom formCustom, string customFields)
|
||||
{
|
||||
bool hasCustomData = !string.IsNullOrWhiteSpace(customFields);
|
||||
|
||||
//No form custom = no template to check against so nothing to do
|
||||
if (formCustom == null)
|
||||
return;
|
||||
|
||||
|
||||
var FormTemplate = JArray.Parse(formCustom.Template);
|
||||
|
||||
var ThisFormCustomFieldsList = FormAvailableFields.FormFields(formCustom.FormKey).Where(x => x.Custom == true).Select(x => x.Key).ToList();
|
||||
|
||||
//If the customFields string is empty then only validation is if any of the fields are required to be filled in
|
||||
if (!hasCustomData)
|
||||
{
|
||||
//iterate the template
|
||||
for (int i = 0; i < FormTemplate.Count; i++)
|
||||
{
|
||||
//get the field customization
|
||||
var fldKey = FormTemplate["fld"].Value<string>();
|
||||
var fldRequired = FormTemplate["required"].Value<bool>();
|
||||
//Check if this is an expected custom field and that it was set to required
|
||||
if (ThisFormCustomFieldsList.Contains(fldKey) && fldRequired == true)
|
||||
{
|
||||
//Ok, this field is required but custom fields are all empty so add this error
|
||||
biz.AddError(ValidationErrorType.RequiredPropertyEmpty, fldKey);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//here we have both a bunch of custom fields presumeably and a form customization so let's get cracking...
|
||||
var CustomFieldData = JObject.Parse(customFields);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@ namespace AyaNova.Biz
|
||||
public static class FormAvailableFields
|
||||
{
|
||||
|
||||
private const string WIDGET = "widget";
|
||||
public const string WIDGET_FORM_KEY = "widget";
|
||||
public const string USER_FORM_KEY = "user";
|
||||
|
||||
|
||||
public static List<string> AvailableFormKeys
|
||||
@@ -13,7 +14,7 @@ namespace AyaNova.Biz
|
||||
get
|
||||
{
|
||||
List<string> l = new List<string>{
|
||||
WIDGET//,USER_CRUD, WIDGET etc
|
||||
WIDGET_FORM_KEY//,USER_CRUD, WIDGET etc
|
||||
};
|
||||
return l;
|
||||
}
|
||||
@@ -30,7 +31,7 @@ namespace AyaNova.Biz
|
||||
List<FormField> l = new List<FormField>();
|
||||
switch (key)
|
||||
{
|
||||
case WIDGET:
|
||||
case WIDGET_FORM_KEY:
|
||||
l.Add(new FormField("WidgetName", false, false));//is not shared localized text key and not hideable as it is in the validation rules for widget
|
||||
l.Add(new FormField("WidgetSerial"));//not in validation rules...you get the idea
|
||||
l.Add(new FormField("WidgetDollarAmount"));
|
||||
@@ -59,6 +60,35 @@ namespace AyaNova.Biz
|
||||
l.Add(new FormField("WidgetCustom16", false, true, true));
|
||||
break;
|
||||
|
||||
// case USER_FORM_KEY:
|
||||
// l.Add(new FormField("WidgetName", false, false));//is not shared localized text key and not hideable as it is in the validation rules for widget
|
||||
// l.Add(new FormField("WidgetSerial"));//not in validation rules...you get the idea
|
||||
// l.Add(new FormField("WidgetDollarAmount"));
|
||||
// l.Add(new FormField("WidgetCount"));
|
||||
// l.Add(new FormField("WidgetRoles"));
|
||||
// l.Add(new FormField("WidgetStartDate", false, false));
|
||||
// l.Add(new FormField("WidgetEndDate", false, false));
|
||||
// l.Add(new FormField("WidgetNotes"));
|
||||
// l.Add(new FormField("CommonActive", true));
|
||||
// l.Add(new FormField("Tags", true));
|
||||
// l.Add(new FormField("WidgetCustom1", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom2", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom3", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom4", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom5", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom6", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom7", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom8", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom9", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom10", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom11", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom12", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom13", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom14", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom15", false, true, true));
|
||||
// l.Add(new FormField("WidgetCustom16", false, true, true));
|
||||
// break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
|
||||
@@ -361,9 +361,9 @@ namespace AyaNova.Biz
|
||||
//USEROPTIONS
|
||||
ct.Database.ExecuteSqlCommand($"delete from auseroptions where userid={dbObj.Id}");
|
||||
|
||||
|
||||
|
||||
EventLogProcessor.DeleteObject(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||
ct.SaveChanges();
|
||||
ct.SaveChanges();
|
||||
Search.ProcessDeletedObjectKeywords(dbObj.Id, BizType);
|
||||
TagUtil.ProcessDeleteTagsInRepository(ct, dbObj.Tags);
|
||||
|
||||
@@ -521,7 +521,7 @@ namespace AyaNova.Biz
|
||||
if (!string.IsNullOrWhiteSpace(proposedObj.EmployeeNumber) && proposedObj.EmployeeNumber.Length > 255)
|
||||
AddError(ValidationErrorType.LengthExceeded, "EmployeeNumber", "255 max");
|
||||
|
||||
|
||||
//CustomFieldsValidator.Validate(this, "user", proposedObj.CustomFields);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -399,6 +399,8 @@ namespace AyaNova.Biz
|
||||
AddError(ValidationErrorType.InvalidValue, "Roles");
|
||||
}
|
||||
|
||||
CustomFieldsValidator.Validate(this, ct.FormCustom.SingleOrDefault(x => x.FormKey == FormAvailableFields.WIDGET_FORM_KEY), proposedObj.CustomFields);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace AyaNova.Models
|
||||
public long? ClientId { get; set; }
|
||||
public long? HeadOfficeId { get; set; }
|
||||
public long? SubVendorId { get; set; }
|
||||
public string CustomFields { get; set; }
|
||||
|
||||
public List<string> Tags { get; set; }
|
||||
|
||||
|
||||
@@ -23,11 +23,13 @@ namespace AyaNova.Models
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public int Count {get;set;}
|
||||
public int Count { get; set; }
|
||||
public string CustomFields { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
|
||||
public Widget(){
|
||||
Tags=new List<string>();
|
||||
public Widget()
|
||||
{
|
||||
Tags = new List<string>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace AyaNova.Util
|
||||
exec("CREATE TABLE auser (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, active bool not null, name varchar(255) not null, " +
|
||||
"login text not null, password text not null, salt text not null, roles integer not null, localeid bigint not null REFERENCES alocale (id), " +
|
||||
"dlkey text, dlkeyexpire timestamp, usertype integer not null, employeenumber varchar(255), notes text, clientid bigint, " +
|
||||
"headofficeid bigint, subvendorid bigint, tags varchar(255) ARRAY)");
|
||||
"headofficeid bigint, subvendorid bigint, customfields text, tags varchar(255) ARRAY)");
|
||||
|
||||
//Index for name fetching
|
||||
exec("CREATE UNIQUE INDEX auser_name_id_idx ON auser (id, name);");
|
||||
@@ -206,7 +206,7 @@ namespace AyaNova.Util
|
||||
//Add widget table
|
||||
//id, text, longtext, boolean, currency,
|
||||
exec("CREATE TABLE awidget (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, name varchar(255) not null, serial bigint not null," +
|
||||
"startdate timestamp, enddate timestamp, dollaramount decimal(19,5), active bool, roles int4, count integer, notes text, tags varchar(255) ARRAY)");
|
||||
"startdate timestamp, enddate timestamp, dollaramount decimal(19,5), active bool, roles int4, count integer, notes text, customfields text, tags varchar(255) ARRAY)");
|
||||
|
||||
//TEST TEST TEST ONLY FOR DEVELOPMENT TESTING TO ENSURE UNIQUENESS
|
||||
//exec("CREATE UNIQUE INDEX awidget_serial_idx ON awidget (serial);");
|
||||
|
||||
Reference in New Issue
Block a user