Final (?) de-widgetification

This commit is contained in:
2021-09-07 14:33:48 +00:00
parent 898daac02c
commit 316c8aa5bd
27 changed files with 45 additions and 1560 deletions

View File

@@ -23,7 +23,7 @@ namespace AyaNova.Api.Controllers
public class SearchController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger<WidgetController> log;
private readonly ILogger<SearchController> log;
private readonly ApiServerState serverState;
/// <summary>
@@ -32,7 +32,7 @@ namespace AyaNova.Api.Controllers
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public SearchController(AyContext dbcontext, ILogger<WidgetController> logger, ApiServerState apiServerState)
public SearchController(AyContext dbcontext, ILogger<SearchController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;

View File

@@ -22,7 +22,7 @@ namespace AyaNova.Api.Controllers
public class TrialController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger<WidgetController> log;
private readonly ILogger<TrialController> log;
private readonly ApiServerState serverState;
/// <summary>
@@ -31,7 +31,7 @@ namespace AyaNova.Api.Controllers
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public TrialController(AyContext dbcontext, ILogger<WidgetController> logger, ApiServerState apiServerState)
public TrialController(AyContext dbcontext, ILogger<TrialController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;

View File

@@ -1,221 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
//DOCUMENTATING THE API
//https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/recommended-tags-for-documentation-comments
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore#include-descriptions-from-xml-comments
/// <summary>
/// Sample controller class used during development for testing purposes
/// </summary>
[ApiController]
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/widget")]
[Produces("application/json")]
[Authorize]
public class WidgetController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger<WidgetController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public WidgetController(AyContext dbcontext, ILogger<WidgetController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Create Widget
/// </summary>
/// <param name="newObject"></param>
/// <param name="apiVersion">From route path</param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> PostWidget([FromBody] Widget newObject, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
Widget o = await biz.CreateAsync(newObject);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return CreatedAtAction(nameof(WidgetController.GetWidget), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
}
// /// <summary>
// /// Duplicate Widget
// /// (Wiki and Attachments are not duplicated)
// /// </summary>
// /// <param name="id">Source object id</param>
// /// <param name="apiVersion">From route path</param>
// /// <returns>Widget</returns>
// [HttpPost("duplicate/{id}")]
// public async Task<IActionResult> DuplicateWidget([FromRoute] long id, ApiVersion apiVersion)
// {
// if (!serverState.IsOpen)
// return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
// WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
// if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
// return StatusCode(403, new ApiNotAuthorizedResponse());
// if (!ModelState.IsValid)
// return BadRequest(new ApiErrorResponse(ModelState));
// Widget o = await biz.DuplicateAsync(id);
// if (o == null)
// return BadRequest(new ApiErrorResponse(biz.Errors));
// else
// return CreatedAtAction(nameof(WidgetController.GetWidget), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
// }
/// <summary>
/// Get Widget
/// </summary>
/// <param name="id"></param>
/// <returns>Widget</returns>
[HttpGet("{id}")]
public async Task<IActionResult> GetWidget([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
var o = await biz.GetAsync(id);
if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
return Ok(ApiOkResponse.Response(o));
}
/// <summary>
/// Update Widget
/// </summary>
/// <param name="updatedObject"></param>
/// <returns></returns>
[HttpPut]
public async Task<IActionResult> PutWidget([FromBody] Widget updatedObject)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await biz.PutAsync(updatedObject);
if (o == null)
{
if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT))
return StatusCode(409, new ApiErrorResponse(biz.Errors));
else
return BadRequest(new ApiErrorResponse(biz.Errors));
}
return Ok(ApiOkResponse.Response(new { Concurrency = o.Concurrency }));
}
/// <summary>
/// Delete Widget
/// </summary>
/// <param name="id"></param>
/// <returns>NoContent</returns>
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteWidget([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!await biz.DeleteAsync(id))
return BadRequest(new ApiErrorResponse(biz.Errors));
return NoContent();
}
/// <summary>
/// ReStart serial number
///
/// </summary>
/// <param name="newSerial">Next starting value for auto generated serial numbers</param>
/// <param name="apiVersion">From route path</param>
/// <returns>Widget</returns>
[HttpPost("restart-serial/{newSerial}")]
public async Task<IActionResult> ReStartSerial([FromRoute] long newSerial, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
bool result=await biz.RestartSerial(newSerial);
if (!result)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return NoContent();
}
///////////////////////////////////////////////
//TEST ROUTES
//
/// <summary>
/// Get route that triggers exception for testing
/// </summary>
/// <returns>Nothing, triggers exception</returns>
[HttpGet("exception")]
public ActionResult GetException()
{
//log.LogInformation("Widget::getexception-> Test exception and log from controller test");
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
throw new System.NotSupportedException("Test exception from widget controller");
}
/// <summary>
/// Get route that triggers an alternate type of exception for testing
/// </summary>
/// <returns>Nothing, triggers exception</returns>
[HttpGet("altexception")]
public ActionResult GetAltException()
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
throw new System.ArgumentException("Test exception (ALT) from widget controller");
}
//------------
}//eoc
}//eons

View File

@@ -1,129 +0,0 @@
using System.Collections.Generic;
using AyaNova.Biz;
namespace AyaNova.DataList
{
internal class TestWidgetDataList : DataListProcessingBase
{
public TestWidgetDataList()
{
DefaultListAType = AyaType.Widget;
SQLFrom = "from awidget left join auser on (awidget.userid=auser.id)";
var RoleSet = BizRoles.GetRoleSet(DefaultListAType);
AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change;
DefaultColumns = new List<string>() { "widgetname", "widgetserial", "widgetdollaramount", "widgetusertype", "widgetstartdate", "widgetactive", "username" };
DefaultSortBy = new Dictionary<string, string>() { { "widgetname", "+" } };
FieldDefinitions = new List<DataListFieldDefinition>();
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "WidgetName",
FieldKey = "widgetname",
AType = (int)AyaType.Widget,
UiFieldDataType = (int)UiFieldDataType.Text,
SqlIdColumnName = "awidget.id",
SqlValueColumnName = "awidget.name",
IsRowId = true
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "WidgetSerial",
FieldKey = "widgetserial",
UiFieldDataType = (int)UiFieldDataType.Integer,
SqlValueColumnName = "awidget.serial"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "WidgetDollarAmount",
FieldKey = "widgetdollaramount",
UiFieldDataType = (int)UiFieldDataType.Currency,
SqlValueColumnName = "awidget.dollaramount"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "WidgetCount",
FieldKey = "widgetcount",
UiFieldDataType = (int)UiFieldDataType.Integer,
SqlValueColumnName = "awidget.count"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "UserType",
FieldKey = "widgetusertype",
UiFieldDataType = (int)UiFieldDataType.Enum,
EnumType = AyaNova.Util.StringUtil.TrimTypeName(typeof(UserType).ToString()),
SqlValueColumnName = "awidget.usertype"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "WidgetStartDate",
FieldKey = "widgetstartdate",
UiFieldDataType = (int)UiFieldDataType.DateTime,
SqlValueColumnName = "awidget.startdate"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "WidgetEndDate",
FieldKey = "widgetenddate",
UiFieldDataType = (int)UiFieldDataType.DateTime,
SqlValueColumnName = "awidget.enddate"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "WidgetNotes",
FieldKey = "widgetnotes",
UiFieldDataType = (int)UiFieldDataType.Text,
SqlValueColumnName = "awidget.notes"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
FieldKey = "username",
TKey = "User",
UiFieldDataType = (int)UiFieldDataType.Text,
AType = (int)AyaType.User,
SqlIdColumnName = "auser.id",
SqlValueColumnName = "auser.name"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "Active",
FieldKey = "widgetactive",
UiFieldDataType = (int)UiFieldDataType.Bool,
SqlValueColumnName = "awidget.active"
});
FieldDefinitions.Add(new DataListFieldDefinition
{
TKey = "Tags",
FieldKey = "widgettags",
UiFieldDataType = (int)UiFieldDataType.Tags,
SqlValueColumnName = "awidget.tags"
});
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom1", FieldKey = "widgetcustom1", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom2", FieldKey = "widgetcustom2", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom3", FieldKey = "widgetcustom3", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom4", FieldKey = "widgetcustom4", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom5", FieldKey = "widgetcustom5", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom6", FieldKey = "widgetcustom6", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom7", FieldKey = "widgetcustom7", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom8", FieldKey = "widgetcustom8", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom9", FieldKey = "widgetcustom9", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom10", FieldKey = "widgetcustom10", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom11", FieldKey = "widgetcustom11", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom12", FieldKey = "widgetcustom12", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom13", FieldKey = "widgetcustom13", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom14", FieldKey = "widgetcustom14", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom15", FieldKey = "widgetcustom15", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WidgetCustom16", FieldKey = "widgetcustom16", IsCustomField = true, IsFilterable = false, IsSortable = false, SqlValueColumnName = "awidget.customfields" });
}
}//eoc
}//eons

View File

@@ -9,14 +9,7 @@ namespace AyaNova.PickList
internal static class PickListSqlBuilder
{
/*Example
select awidget.id as plId, awidget.active as plActive, awidget.name || ' ' || awidget.serial || ' ' || auser.name as plname, awidget.tags
from awidget left join auser on (awidget.userid=auser.id)
where array_to_string(awidget.tags,',') like '%zone-8%'
and awidget.active = true
and ((awidget.name like '%we%') or (cast (awidget.serial as text) like '%we%') or (auser.name like '%we%'))
order by awidget.name,awidget.serial,auser.name limit 100
*/
//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
@@ -149,7 +142,7 @@ namespace AyaNova.PickList
lOrderBy.Add(valueColumnName);
//THIS is the best filter method for a like comparison to each individual tag:
//(array_to_string(awidget.tags,',') like '%zo%')
//(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
@@ -191,7 +184,7 @@ namespace AyaNova.PickList
lOrderBy.Add(valueColumnName);
//Where fragment is different for non text fields: it needs to be cast to text to like query on it
//(cast (awidget.serial as text) like '%some%')
//(cast (aworkorder.serial as text) like '%some%')
if (HasAutoCompleteQuery)
if (ServerGlobalBizSettings.Cache.SearchCaseSensitiveOnly)
sWhere = $"(cast ({valueColumnName} as text) like '%{autoCompleteQuery}%')";
@@ -221,9 +214,9 @@ namespace AyaNova.PickList
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 awidget
//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(' ', awidget.name, awidget.serial, auser.name)
//concat_ws(' ', acustomer.name, acustomer.serial, auser.name)
sb.Append("concat_ws(' ', ");
foreach (string s in lSelect)
@@ -315,8 +308,8 @@ namespace AyaNova.PickList
return sb.ToString();
}
//"select awidget.id as plId || ' 'awidget.active as plActive || ' 'awidget.name || ' 'awidget.serial || ' 'auser.name as plname from awidget left join auser on (awidget.userid=auser.id)
//where awidget.active = true and ((awidget.name like '%on%') or (cast (awidget.serial as text) like '%on%') or (auser.name like '%on%')) order by awidget.name,awidget.serial,auser.name limit 100"
//"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

View File

@@ -1,83 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using AyaNova.Biz;
namespace AyaNova.PickList
{
internal class WidgetPickList : AyaPickList
{
public WidgetPickList()
{
DefaultListAType = AyaType.Widget;
SQLFrom = "from awidget left join auser on (awidget.userid=auser.id)";
AllowedRoles = BizRoles.GetRoleSet(DefaultListAType).Select;
//Default template
dynamic dTemplate = new JArray();
dynamic cm = new JObject();
cm.fld = "widgetname";
dTemplate.Add(cm);
cm = new JObject();
cm.fld = "widgetserial";
dTemplate.Add(cm);
cm = new JObject();
cm.fld = "username";
dTemplate.Add(cm);
cm = new JObject();
cm.fld = "widgettags";
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 (in this case User) table need to be specified completely
ColumnDefinitions = new List<AyaPickListFieldDefinition>();
ColumnDefinitions.Add(new AyaPickListFieldDefinition
{
TKey = "Active",
FieldKey = "widgetactive",
ColumnDataType = UiFieldDataType.Bool,
SqlValueColumnName = "awidget.active",
IsActiveColumn = true
});
ColumnDefinitions.Add(new AyaPickListFieldDefinition
{
TKey = "WidgetName",
FieldKey = "widgetname",
//AType = AyaType.Widget,
ColumnDataType = UiFieldDataType.Text,
SqlIdColumnName = "awidget.id",
SqlValueColumnName = "awidget.name",
IsRowId = true
});
ColumnDefinitions.Add(new AyaPickListFieldDefinition
{
TKey = "WidgetSerial",
FieldKey = "widgetserial",
ColumnDataType = UiFieldDataType.Integer,
SqlValueColumnName = "awidget.serial"
});
ColumnDefinitions.Add(new AyaPickListFieldDefinition
{
FieldKey = "username",
TKey = "User",
ColumnDataType = UiFieldDataType.Text,
SqlIdColumnName = "auser.id",
SqlValueColumnName = "auser.name"
});
ColumnDefinitions.Add(new AyaPickListFieldDefinition
{
TKey = "Tags",
FieldKey = "widgettags",
ColumnDataType = UiFieldDataType.Tags,
SqlValueColumnName = "awidget.tags"
});
}
}//eoc
}//eons

View File

@@ -22,9 +22,8 @@ namespace AyaNova.Biz
//because enumlist gets it that way, i.e. "Global" would be the expected key
NoType = 0,
Global = 1,
[CoreBizObject,ReportableBizObject]
Widget = 2,
Global = 1,
UNUSED_2 = 2,
[CoreBizObject,ReportableBizObject]
User = 3,
ServerState = 4,

View File

@@ -31,9 +31,7 @@ namespace AyaNova.Biz
case AyaType.FormCustom:
return await ct.FormCustom.AnyAsync(z => z.Id == id);
case AyaType.User:
return await ct.User.AnyAsync(z => z.Id == id);
case AyaType.Widget:
return await ct.Widget.AnyAsync(z => z.Id == id);
return await ct.User.AnyAsync(z => z.Id == id);
case AyaType.Customer:
return await ct.Customer.AnyAsync(z => z.Id == id);
case AyaType.CustomerNote:

View File

@@ -33,9 +33,7 @@ namespace AyaNova.Biz
case AyaType.DataListSavedFilter:
return new DataListSavedFilterBiz(ct, userId, translationId, roles);
case AyaType.FormCustom:
return new FormCustomBiz(ct, userId, translationId, roles);
case AyaType.Widget:
return new WidgetBiz(ct, userId, translationId, roles);
return new FormCustomBiz(ct, userId, translationId, roles);
case AyaType.FileAttachment:
return new AttachmentBiz(ct, userId, roles);
case AyaType.Customer:

File diff suppressed because one or more lines are too long

View File

@@ -60,40 +60,6 @@ namespace AyaNova.Biz
/* ***************************** 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
*/
#region WIDGET_KEY
{
List<FormField> l = new List<FormField>();
l.Add(new FormField { TKey = "WidgetSerial", FieldKey = "Serial" });
l.Add(new FormField { TKey = "WidgetCount", FieldKey = "Count" });
l.Add(new FormField { TKey = "WidgetDollarAmount", FieldKey = "DollarAmount" });
l.Add(new FormField { TKey = "WidgetStartDate", FieldKey = "StartDate" });
l.Add(new FormField { TKey = "WidgetEndDate", FieldKey = "EndDate" });
l.Add(new FormField { TKey = "User", FieldKey = "UserId" });
l.Add(new FormField { TKey = "UserType", FieldKey = "UserType" });
l.Add(new FormField { TKey = "WidgetNotes", FieldKey = "Notes" });
l.Add(new FormField { TKey = "Tags", FieldKey = "Tags" });
l.Add(new FormField { TKey = "Wiki", FieldKey = "Wiki" });
l.Add(new FormField { TKey = "Attachments", FieldKey = "Attachments" });
l.Add(new FormField { TKey = "WidgetCustom1", FieldKey = "WidgetCustom1", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom2", FieldKey = "WidgetCustom2", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom3", FieldKey = "WidgetCustom3", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom4", FieldKey = "WidgetCustom4", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom5", FieldKey = "WidgetCustom5", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom6", FieldKey = "WidgetCustom6", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom7", FieldKey = "WidgetCustom7", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom8", FieldKey = "WidgetCustom8", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom9", FieldKey = "WidgetCustom9", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom10", FieldKey = "WidgetCustom10", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom11", FieldKey = "WidgetCustom11", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom12", FieldKey = "WidgetCustom12", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom13", FieldKey = "WidgetCustom13", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom14", FieldKey = "WidgetCustom14", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom15", FieldKey = "WidgetCustom15", IsCustomField = true });
l.Add(new FormField { TKey = "WidgetCustom16", FieldKey = "WidgetCustom16", IsCustomField = true });
_formFields.Add(AyaType.Widget.ToString(), l);
}
#endregion widget
#region USER_KEY
{

View File

@@ -931,7 +931,7 @@ namespace AyaNova.Biz
if (jobData.ContainsKey("idList"))
idList = ((JArray)jobData["idList"]).ToObject<List<long>>();
else
idList = await ct.Widget.Select(z => z.Id).ToListAsync();
idList = await ct.PM.Select(z => z.Id).ToListAsync();
bool SaveIt = false;
foreach (long id in idList)
{

View File

@@ -912,7 +912,7 @@ namespace AyaNova.Biz
if (jobData.ContainsKey("idList"))
idList = ((JArray)jobData["idList"]).ToObject<List<long>>();
else
idList = await ct.Widget.Select(z => z.Id).ToListAsync();
idList = await ct.Quote.Select(z => z.Id).ToListAsync();
bool SaveIt = false;
foreach (long id in idList)
{

View File

@@ -1,552 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Linq;
using EnumsNET;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Models;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace AyaNova.Biz
{
internal class WidgetBiz : BizObject, IJobObject, ISearchAbleObject, IReportAbleObject, IExportAbleObject, IImportAbleObject, INotifiableObject
{
internal WidgetBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
ct = dbcontext;
UserId = currentUserId;
UserTranslationId = userTranslationId;
CurrentUserRoles = UserRoles;
BizType = AyaType.Widget;
}
internal static WidgetBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
{
if (httpContext != null)
return new WidgetBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
else
return new WidgetBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
internal async Task<bool> ExistsAsync(long id)
{
return await ct.Widget.AnyAsync(z => z.Id == id);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//
internal async Task<Widget> CreateAsync(Widget newObject)
{
await ValidateAsync(newObject);
if (HasErrors)
return null;
else
{
newObject.Tags = TagBiz.NormalizeTags(newObject.Tags);
newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields);
await ct.Widget.AddAsync(newObject);
await ct.SaveChangesAsync();
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(newObject, true);
await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null);
await HandlePotentialNotificationEvent(AyaEvent.Created, newObject);
return newObject;
}
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// //DUPLICATE
// //
// internal async Task<Widget> DuplicateAsync(long id)
// {
// var dbObject = await GetAsync(id, false);
// if (dbObject == null)
// {
// AddError(ApiErrorCode.NOT_FOUND, "id");
// return null;
// }
// var newObject = new Widget();
// CopyObject.Copy(dbObject, newObject, "Wiki,Serial");
// string newUniqueName = string.Empty;
// bool NotUnique = true;
// long l = 1;
// do
// {
// newUniqueName = Util.StringUtil.UniqueNameBuilder(dbObject.Name, l++, 255);
// NotUnique = await ct.Widget.AnyAsync(z => z.Name == newUniqueName);
// } while (NotUnique);
// newObject.Name = newUniqueName;
// newObject.Id = 0;
// newObject.Concurrency = 0;
// await ct.Widget.AddAsync(newObject);
// await ct.SaveChangesAsync();
// await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct);
// await SearchIndexAsync(newObject, true);
// await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null);
// await HandlePotentialNotificationEvent(AyaEvent.Created, newObject);
// return newObject;
// }
////////////////////////////////////////////////////////////////////////////////////////////////
// GET
//
internal async Task<Widget> GetAsync(long id, bool logTheGetEvent = true)
{
var ret = await ct.Widget.AsNoTracking().SingleOrDefaultAsync(z => z.Id == id);
if (logTheGetEvent && ret != null)
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, BizType, AyaEvent.Retrieved), ct);
return ret;
}
/*
OLD PUT CODE
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
internal async Task<Widget> PutAsync(Widget putObject)
{
Widget dbObject = await ct.Widget.SingleOrDefaultAsync(z => z.Id == putObject.Id);
if (dbObject == null)
{
AddError(ApiErrorCode.NOT_FOUND, "id");
return null;
}
Widget SnapshotOfOriginalDBObj = new Widget();
CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj);
CopyObject.Copy(putObject, dbObject, "Id");//can update serial
dbObject.Tags = TagBiz.NormalizeTags(dbObject.Tags);
dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields);
ct.Entry(dbObject).OriginalValues["Concurrency"] = putObject.Concurrency;
await ValidateAsync(dbObject);
if (HasErrors) return null;
try
{
await ct.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!await ExistsAsync(putObject.Id))
AddError(ApiErrorCode.NOT_FOUND);
else
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
return null;
}
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
await SearchIndexAsync(dbObject, false);
await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags);
await NotifyEventProcessor.HandlePotentialNotificationEvent(AyaEvent.Modified, dbObject, SnapshotOfOriginalDBObj);
return dbObject;
}
*/
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
internal async Task<Widget> PutAsync(Widget putObject)
{
var dbObject = await GetAsync(putObject.Id, false);
if (dbObject == null)
{
AddError(ApiErrorCode.NOT_FOUND, "id");
return null;
}
if (dbObject.Concurrency != putObject.Concurrency)
{
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
return null;
}
putObject.Tags = TagBiz.NormalizeTags(putObject.Tags);
putObject.CustomFields = JsonUtil.CompactJson(putObject.CustomFields);
await ValidateAsync(putObject);
if (HasErrors) return null;
ct.Replace(dbObject, putObject);
try
{
await ct.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!await ExistsAsync(putObject.Id))
AddError(ApiErrorCode.NOT_FOUND);
else
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
return null;
}
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
await SearchIndexAsync(putObject, false);
await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, putObject.Tags, dbObject.Tags);
await HandlePotentialNotificationEvent(AyaEvent.Modified, putObject, dbObject);
return putObject;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal async Task<bool> DeleteAsync(long id)
{
using (var transaction = await ct.Database.BeginTransactionAsync())
{
var dbObject = await GetAsync(id, false);
if (dbObject == null)
{
AddError(ApiErrorCode.NOT_FOUND);
return false;
}
ValidateCanDelete(dbObject);
if (HasErrors)
return false;
if (HasErrors)
return false;
ct.Widget.Remove(dbObject);
await ct.SaveChangesAsync();
//Log event
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Name, ct);
await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct);
await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags);
await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct);
//all good do the commit
await transaction.CommitAsync();
await HandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject);
return true;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//RESTART SERIAL
//
internal async Task<bool> RestartSerial(long newSerial)
{
using (var command = ct.Database.GetDbConnection().CreateCommand())
{
command.CommandText = $"alter table awidget alter column serial restart with {newSerial}";
await ct.Database.OpenConnectionAsync();
await command.ExecuteNonQueryAsync();
await ct.Database.CloseConnectionAsync();
}
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, 0, BizType, AyaEvent.ResetSerial, newSerial.ToString()), ct);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//SEARCH
//
private async Task SearchIndexAsync(Widget obj, bool isNew)
{
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
DigestSearchText(obj, SearchParams);
if (isNew)
await Search.ProcessNewObjectKeywordsAsync(SearchParams);
else
await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams);
}
public async Task<Search.SearchIndexProcessObjectParameters> GetSearchResultSummary(long id, AyaType specificType)
{
var obj = await GetAsync(id, false);
var SearchParams = new Search.SearchIndexProcessObjectParameters();
DigestSearchText(obj, SearchParams);
return SearchParams;
}
public void DigestSearchText(Widget obj, Search.SearchIndexProcessObjectParameters searchParams)
{
if (obj != null)
searchParams.AddText(obj.Notes)
.AddText(obj.Name)
.AddText(obj.Wiki)
.AddText(obj.Tags)
.AddText(obj.Serial)
.AddText(obj.DollarAmount)
.AddText(obj.Count)
.AddCustomFields(obj.CustomFields);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
//
private async Task ValidateAsync(Widget proposedObj)
{
//skip validation if seeding
if(ServerBootConfig.SEEDING) return;
//NOTE: In DB schema only name and serial are not nullable
//run validation and biz rules
//Name required
if (string.IsNullOrWhiteSpace(proposedObj.Name))
AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name");
//If name is otherwise OK, check that name is unique
if (!PropertyHasErrors("Name"))
{
//Use Any command is efficient way to check existance, it doesn't return the record, just a true or false
if (await ct.Widget.AnyAsync(z => z.Name == proposedObj.Name && z.Id != proposedObj.Id))
{
AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name");
}
}
//Start date AND end date must both be null or both contain values
if (proposedObj.StartDate == null && proposedObj.EndDate != null)
AddError(ApiErrorCode.VALIDATION_REQUIRED, "StartDate");
if (proposedObj.StartDate != null && proposedObj.EndDate == null)
AddError(ApiErrorCode.VALIDATION_REQUIRED, "EndDate");
//Start date before end date
if (proposedObj.StartDate != null && proposedObj.EndDate != null)
if (proposedObj.StartDate > proposedObj.EndDate)
AddError(ApiErrorCode.VALIDATION_STARTDATE_AFTER_ENDDATE, "StartDate");
//Enum is valid value
if (!proposedObj.UserType.IsValid())
{
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "UserType");
}
//Any form customizations to validate?
var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == AyaType.Widget.ToString());
if (FormCustomization != null)
{
//Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required
//validate users choices for required non custom fields
RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);
//validate custom fields
CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields);
}
}
private void ValidateCanDelete(Widget inObj)
{
//whatever needs to be check to delete this object
}
//Fulfil report request for data
public async Task<JArray> GetReportData(DataListSelectedRequest dataListSelectedRequest)
{
//NOTE: Report widget is a superset of biz object widget
//Biz objects will add and needed linked records here as extra fields with the data included
//for example instead of a userid only there will be username added to the record
//so the report designer can just select it as a field, no need to query seperately for it etc
//REMEMBER: there is a name display format system and it should honour that so that the report
//displays a user the same as it would display in the UI in an input form, no surprises
var idList = dataListSelectedRequest.SelectedRowIds;
JArray ReportData = new JArray();
while (idList.Any())
{
var batch = idList.Take(IReportAbleObject.REPORT_DATA_BATCH_SIZE);
idList = idList.Skip(IReportAbleObject.REPORT_DATA_BATCH_SIZE).ToArray();
//query for this batch, comes back in db natural order unfortunately
var batchResults = await ct.Widget.AsNoTracking().Where(z => batch.Contains(z.Id)).ToArrayAsync();
//order the results back into original
var orderedList = from id in batch join z in batchResults on id equals z.Id select z;
//cache frequent viz data
//usertypes
var UserTypesEnumList = await AyaNova.Api.Controllers.EnumListController.GetEnumList(
StringUtil.TrimTypeName(typeof(UserType).ToString()),
UserTranslationId,
CurrentUserRoles);
foreach (Widget w in orderedList)
{
await PopulateVizFields(w, UserTypesEnumList);
var jo = JObject.FromObject(w);
if (!JsonUtil.JTokenIsNullOrEmpty(jo["CustomFields"]))
jo["CustomFields"] = JObject.Parse((string)jo["CustomFields"]);
ReportData.Add(jo);
}
}
return ReportData;
}
//populate viz fields from provided object
private async Task PopulateVizFields(Widget o, List<NameIdItem> userTypesEnumList)
{
if (o.UserId != null)
o.UserViz = await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync();
o.UserTypeViz = userTypesEnumList.Where(x => x.Id == (long)o.UserType).Select(x => x.Name).First();
}
////////////////////////////////////////////////////////////////////////////////////////////////
// IMPORT EXPORT
//
public async Task<JArray> GetExportData(DataListSelectedRequest dataListSelectedRequest)
{
//for now just re-use the report data code
//this may turn out to be the pattern for most biz object types but keeping it seperate allows for custom usage from time to time
return await GetReportData(dataListSelectedRequest);
}
public async Task<List<string>> ImportData(JArray ja)
{
List<string> ImportResult = new List<string>();
string ImportTag = $"imported-{FileUtil.GetSafeDateFileName()}";
var jsset = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = new AyaNova.Util.JsonUtil.ShouldSerializeContractResolver(new string[] { "Concurrency", "Id", "CustomFields" }) });
foreach (JObject j in ja)
{
var w = j.ToObject<Widget>(jsset);
if (j["CustomFields"] != null)
w.CustomFields = j["CustomFields"].ToString();
w.Tags.Add(ImportTag);//so user can find them all and revert later if necessary
var res = await CreateAsync(w);
if (res == null)
{
ImportResult.Add($"* {w.Name} - {this.GetErrorsAsString()}");
this.ClearErrors();
}
else
{
ImportResult.Add($"{w.Name} - ok");
}
}
return ImportResult;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//JOB / OPERATIONS
//
public async Task HandleJobAsync(OpsJob job)
{
//Hand off the particular job to the corresponding processing code
//NOTE: If this code throws an exception the caller (JobsBiz::ProcessJobsAsync) will automatically set the job to failed and log the exeption so
//basically any error condition during job processing should throw up an exception if it can't be handled
switch (job.JobType)
{
case JobType.BatchCoreObjectOperation:
await ProcessBatchJobAsync(job);
break;
default:
throw new System.ArgumentOutOfRangeException($"WidgetBiz.HandleJob-> Invalid job type{job.JobType.ToString()}");
}
}
private async Task ProcessBatchJobAsync(OpsJob job)
{
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Running);
await JobsBiz.LogJobAsync(job.GId, $"LT:StartJob {job.SubType}");
List<long> idList = new List<long>();
long FailedObjectCount = 0;
JObject jobData = JObject.Parse(job.JobInfo);
if (jobData.ContainsKey("idList"))
idList = ((JArray)jobData["idList"]).ToObject<List<long>>();
else
idList = await ct.Widget.AsNoTracking().Select(z => z.Id).ToListAsync();
bool SaveIt = false;
foreach (long id in idList)
{
try
{
SaveIt = false;
ClearErrors();
Widget o = null;
//save a fetch if it's a delete
if (job.SubType != JobSubType.Delete)
o = await GetAsync(id, false);
switch (job.SubType)
{
case JobSubType.TagAddAny:
case JobSubType.TagAdd:
case JobSubType.TagRemoveAny:
case JobSubType.TagRemove:
case JobSubType.TagReplaceAny:
case JobSubType.TagReplace:
SaveIt = TagBiz.ProcessBatchTagOperation(o.Tags, (string)jobData["tag"], jobData.ContainsKey("toTag") ? (string)jobData["toTag"] : null, job.SubType);
break;
case JobSubType.Delete:
if (!await DeleteAsync(id))
{
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}");
FailedObjectCount++;
}
break;
default:
throw new System.ArgumentOutOfRangeException($"ProcessBatchJobAsync -> Invalid job Subtype{job.SubType}");
}
if (SaveIt)
{
o = await PutAsync(o);
if (o == null)
{
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}");
FailedObjectCount++;
}
}
}
catch (Exception ex)
{
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors id({id})");
await JobsBiz.LogJobAsync(job.GId, ExceptionUtil.ExtractAllExceptionMessages(ex));
}
}
await JobsBiz.LogJobAsync(job.GId, $"LT:BatchJob {job.SubType} {idList.Count}{(FailedObjectCount > 0 ? " - LT:Failed " + FailedObjectCount : "")}");
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Completed);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// NOTIFICATION PROCESSING
//
public async Task HandlePotentialNotificationEvent(AyaEvent ayaEvent, ICoreBizObjectModel proposedObj, ICoreBizObjectModel currentObj = null)
{
ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger<WidgetBiz>();
if(ServerBootConfig.SEEDING || ServerBootConfig.MIGRATING) return;
log.LogDebug($"HandlePotentialNotificationEvent processing: [AyaType:{this.BizType}, AyaEvent:{ayaEvent}]");
bool isNew = currentObj == null;
//STANDARD EVENTS FOR ALL OBJECTS
await NotifyEventHelper.ProcessStandardObjectEvents(ayaEvent, proposedObj, ct);
//SPECIFIC EVENTS FOR THIS OBJECT
}//end of process notifications
/////////////////////////////////////////////////////////////////////
}//eoc
}//eons

View File

@@ -1029,7 +1029,7 @@ namespace AyaNova.Biz
if (jobData.ContainsKey("idList"))
idList = ((JArray)jobData["idList"]).ToObject<List<long>>();
else
idList = await ct.Widget.Select(z => z.Id).ToListAsync();
idList = await ct.WorkOrder.Select(z => z.Id).ToListAsync();
bool SaveIt = false;
foreach (long id in idList)
{

View File

@@ -10,7 +10,6 @@ namespace AyaNova.Models
public virtual DbSet<MetricDD> MetricDD { get; set; }
public virtual DbSet<User> User { get; set; }
public virtual DbSet<UserOptions> UserOptions { get; set; }
public virtual DbSet<Widget> Widget { get; set; }
public virtual DbSet<GlobalBizSettings> GlobalBizSettings { get; set; }
public virtual DbSet<GlobalOpsBackupSettings> GlobalOpsBackupSettings { get; set; }
public virtual DbSet<GlobalOpsNotificationSettings> GlobalOpsNotificationSettings { get; set; }
@@ -186,7 +185,6 @@ namespace AyaNova.Models
//SERIALIZED OBJECTS
//
modelBuilder.Entity<PurchaseOrder>().Property(z => z.Serial).UseIdentityByDefaultColumn();
modelBuilder.Entity<Widget>().Property(z => z.Serial).UseIdentityByDefaultColumn();
modelBuilder.Entity<WorkOrder>().Property(z => z.Serial).UseIdentityByDefaultColumn();
modelBuilder.Entity<Quote>().Property(z => z.Serial).UseIdentityByDefaultColumn();
modelBuilder.Entity<PM>().Property(z => z.Serial).UseIdentityByDefaultColumn();

View File

@@ -116,8 +116,7 @@ namespace AyaNova.Models
[JsonIgnore]
public Vendor Vendor { get; set; }
[JsonIgnore]//hide from being returned (as null anyway) with User object in routes
public Widget Widget { get; set; }
public User()
{

View File

@@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
namespace AyaNova.Models
{
//NOTE: Any non required field (nullable in DB) sb nullable here, i.e. decimal? not decimal, otherwise the server will call it an invalid record if the field isn't sent from client
//NOTE: In Widget DB schema only name and serial are not nullable
public class Widget : ICoreBizObjectModel
{
public long Id { get; set; }
public uint Concurrency { get; set; }
[Required]
public string Name { get; set; }
public long Serial { get; set; }
public decimal? DollarAmount { get; set; }
public bool? Active { get; set; }
public UserType UserType { get; set; }
[NotMapped]
public string UserTypeViz { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public string Notes { get; set; }
public int? Count { get; set; }
public string Wiki { get; set; }
public string CustomFields { get; set; }
public List<string> Tags { get; set; }
//relations
//https://docs.microsoft.com/en-us/ef/core/modeling/relationships#other-relationship-patterns
[JsonIgnore]//hide from being returned (as null anyway) with User object in routes
public User User { get; set; }
public long? UserId { get; set; }
[NotMapped]
public string UserViz { get; set; }
public Widget()
{
Tags = new List<string>();
}
[NotMapped, JsonIgnore]
public AyaType AyaType { get => AyaType.Widget; }
}
}

View File

@@ -1663,31 +1663,6 @@
"SupportedUntil": "Support- und Aktualisierungsdatum",
"LicensedOptions": "Lizenzoptionen",
"Log": "Protokoll",
"Widget": "Widget",
"WidgetList": "Widgets",
"WidgetName": "Name",
"WidgetSerial": "Seriennummer",
"WidgetDollarAmount": "Betrag",
"WidgetCount": "Anzahl",
"WidgetStartDate": "Startdatum",
"WidgetEndDate": "Enddatum",
"WidgetNotes": "Notizen",
"WidgetCustom1": "Angepasstes Feld 1",
"WidgetCustom2": "Angepasstes Feld 2",
"WidgetCustom3": "Angepasstes Feld 3",
"WidgetCustom4": "Angepasstes Feld 4",
"WidgetCustom5": "Angepasstes Feld 5",
"WidgetCustom6": "Angepasstes Feld 6",
"WidgetCustom7": "Angepasstes Feld 7",
"WidgetCustom8": "Angepasstes Feld 8",
"WidgetCustom9": "Angepasstes Feld 9",
"WidgetCustom10": "Angepasstes Feld 10",
"WidgetCustom11": "Angepasstes Feld 11",
"WidgetCustom12": "Angepasstes Feld 12",
"WidgetCustom13": "Angepasstes Feld 13",
"WidgetCustom14": "Angepasstes Feld 14",
"WidgetCustom15": "Angepasstes Feld 15",
"WidgetCustom16": "Angepasstes Feld 16",
"RowsPerPage": "Zeilen pro Seite",
"PageOfPageText": "{0}-{1} von {2}",
"Loading": "Wird geladen...",

View File

@@ -1663,31 +1663,6 @@
"SupportedUntil": "Support and updates expiration date",
"LicensedOptions": "Licensed options",
"Log": "Log",
"Widget": "Widget",
"WidgetList": "Widgets",
"WidgetName": "Name",
"WidgetSerial": "Serial #",
"WidgetDollarAmount": "Price",
"WidgetCount": "Count",
"WidgetStartDate": "Start",
"WidgetEndDate": "End",
"WidgetNotes": "Notes",
"WidgetCustom1": "Custom1",
"WidgetCustom2": "Custom2",
"WidgetCustom3": "Custom3",
"WidgetCustom4": "Custom4",
"WidgetCustom5": "Custom5",
"WidgetCustom6": "Custom6",
"WidgetCustom7": "Custom7",
"WidgetCustom8": "Custom8",
"WidgetCustom9": "Custom9",
"WidgetCustom10": "Custom10",
"WidgetCustom11": "Custom11",
"WidgetCustom12": "Custom12",
"WidgetCustom13": "Custom13",
"WidgetCustom14": "Custom14",
"WidgetCustom15": "Custom15",
"WidgetCustom16": "Custom16",
"RowsPerPage": "Rows per page",
"PageOfPageText": "{0}-{1} of {2}",
"Loading": "Loading...",

View File

@@ -1663,31 +1663,6 @@
"SupportedUntil": "Soporte y actualizaciones fecha de caducidad",
"LicensedOptions": "Opciones de licencia",
"Log": "Registro",
"Widget": "Widget",
"WidgetList": "Widgets",
"WidgetName": "Nombre",
"WidgetSerial": "Número de serie",
"WidgetDollarAmount": "Importe",
"WidgetCount": "Recuento",
"WidgetStartDate": "Fecha de comienzo",
"WidgetEndDate": "Fecha de fin",
"WidgetNotes": "Notas",
"WidgetCustom1": "Campo personalizado 1",
"WidgetCustom2": "Campo personalizado 2",
"WidgetCustom3": "Campo personalizado 3",
"WidgetCustom4": "Campo personalizado 4",
"WidgetCustom5": "Campo personalizado 5",
"WidgetCustom6": "Campo personalizado 6",
"WidgetCustom7": "Campo personalizado 7",
"WidgetCustom8": "Campo personalizado 8",
"WidgetCustom9": "Campo personalizado 9",
"WidgetCustom10": "Campo personalizado 10",
"WidgetCustom11": "Campo personalizado 11",
"WidgetCustom12": "Campo personalizado 12",
"WidgetCustom13": "Campo personalizado 13",
"WidgetCustom14": "Campo personalizado 14",
"WidgetCustom15": "Campo personalizado 15",
"WidgetCustom16": "Campo personalizado 16",
"RowsPerPage": "Filas por página",
"PageOfPageText": "{0}-{1} de {2}",
"Loading": "Cargando...",

View File

@@ -1663,31 +1663,6 @@
"SupportedUntil": "Support et mises à jour date d'expiration",
"LicensedOptions": "Options de licence",
"Log": "Enregistrement",
"Widget": "Widget",
"WidgetList": "Widgets",
"WidgetName": "Nom",
"WidgetSerial": "Numéro de série",
"WidgetDollarAmount": "Montant",
"WidgetCount": "Nombre",
"WidgetStartDate": "Date de début",
"WidgetEndDate": "Date de fin",
"WidgetNotes": "Remarques",
"WidgetCustom1": "Champ personnalisé 1",
"WidgetCustom2": "Champ personnalisé 2",
"WidgetCustom3": "Champ personnalisé 3",
"WidgetCustom4": "Champ personnalisé 4",
"WidgetCustom5": "Champ personnalisé 5",
"WidgetCustom6": "Champ personnalisé 6",
"WidgetCustom7": "Champ personnalisé 7",
"WidgetCustom8": "Champ personnalisé 8",
"WidgetCustom9": "Champ personnalisé 9",
"WidgetCustom10": "Champ personnalisé 10",
"WidgetCustom11": "Champ personnalisé 11",
"WidgetCustom12": "Champ personnalisé 12",
"WidgetCustom13": "Champ personnalisé 13",
"WidgetCustom14": "Champ personnalisé 14",
"WidgetCustom15": "Champ personnalisé 15",
"WidgetCustom16": "Champ personnalisé 16",
"RowsPerPage": "Lignes par page",
"PageOfPageText": "{0}-{1} sur {2}",
"Loading": "Chargement...",

View File

@@ -22,16 +22,16 @@ namespace AyaNova.Util
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!!
private const int DESIRED_SCHEMA_LEVEL = 1;
internal const long EXPECTED_COLUMN_COUNT = 1276;
internal const long EXPECTED_INDEX_COUNT = 146;
internal const long EXPECTED_CHECK_CONSTRAINTS = 517;
internal const long EXPECTED_COLUMN_COUNT = 1262;
internal const long EXPECTED_INDEX_COUNT = 144;
internal const long EXPECTED_CHECK_CONSTRAINTS = 513;
internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 192;
internal const long EXPECTED_VIEWS = 9;
internal const long EXPECTED_ROUTINES = 2;
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!!
///////////////////////////////////////////////////////////////// C1276:I146:CC517:FC192:V9:R2
///////////////////////////////////////////////////////////////// C1262:I144:CC513:FC192:V9:R2
@@ -369,7 +369,7 @@ BEGIN
case ayatype
when 0 then return 'LT:NoType';
when 1 then return 'LT:Global';
when 2 then aytable = 'awidget';
when 2 then return 'LT:UNUSED';
when 3 then aytable = 'auser';
when 4 then return 'LT:ServerState';
when 5 then return 'LT:License';
@@ -494,15 +494,9 @@ $BODY$ LANGUAGE PLPGSQL STABLE");
//Add user table
await ExecQueryAsync("CREATE TABLE alicense (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, dbid TEXT, key TEXT NOT NULL)");
//Add widget table
//id, TEXT, longtext, boolean, currency,
await ExecQueryAsync("CREATE TABLE awidget (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT NOT NULL UNIQUE, serial BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,"
+ "startdate TIMESTAMP, enddate TIMESTAMP, dollaramount DECIMAL(38,18), active BOOL NOT NULL, usertype int4, count INTEGER,"
+ "notes TEXT, userid BIGINT, wiki TEXT, customfields TEXT, tags VARCHAR(255) ARRAY)");
await ExecQueryAsync("CREATE TABLE afileattachment (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, "
+ "attachtoobjectid BIGINT NOT NULL, attachtoatype INTEGER NOT NULL, "
+ "storedfilename TEXT NOT NULL, displayfilename TEXT NOT NULL, contenttype TEXT, lastmodified TIMESTAMP NOT NULL, notes TEXT, exists BOOL NOT NULL, size BIGINT NOT NULL)");
+ "attachtoobjectid BIGINT NOT NULL, attachtoatype INTEGER NOT NULL, "
+ "storedfilename TEXT NOT NULL, displayfilename TEXT NOT NULL, contenttype TEXT, lastmodified TIMESTAMP NOT NULL, notes TEXT, exists BOOL NOT NULL, size BIGINT NOT NULL)");
//index required for ops that need to check if file already in db (delete, count refs etc)
//LOOKAT: isn't this useless without the ID as well or is that not fetched?

View File

@@ -402,8 +402,7 @@ namespace AyaNova.Util
await EraseTableAsync("apm", conn);
//---
await EraseTableAsync("afileattachment", conn);
await EraseTableAsync("awidget", conn);
await EraseTableAsync("afileattachment", conn);
await EraseTableAsync("aevent", conn);
await EraseTableAsync("adatalistsavedfilter", conn);
await EraseTableAsync("adatalistcolumnview", conn);

View File

@@ -114,49 +114,49 @@ namespace AyaNova.Util
apiServerState.SetOpsOnly("Seeding database with sample data");
// //WIDGET sample form customization
// //CUSTOMER sample form customization
// {
// var fc = new FormCustom()
// {
// FormKey = AyaType.Widget.ToString(),
// FormKey = AyaType.Customer.ToString(),
// Template = @"[
// {
// ""fld"": ""Notes"",
// ""required"": true
// },
// {
// ""fld"": ""WidgetCustom1"",
// ""fld"": ""CustomerCustom1"",
// ""required"": false,
// ""type"": 1
// },
// {
// ""fld"": ""WidgetCustom2"",
// ""fld"": ""CustomerCustom2"",
// ""required"": true,
// ""type"": 4
// },
// {
// ""fld"": ""WidgetCustom3"",
// ""fld"": ""CustomerCustom3"",
// ""required"": false,
// ""type"": 5
// },
// {
// ""fld"": ""WidgetCustom4"",
// ""fld"": ""CustomerCustom4"",
// ""required"": false,
// ""type"": 6
// },
// {
// ""fld"": ""WidgetCustom5"",
// ""fld"": ""CustomerCustom5"",
// ""required"": false,
// ""type"": 8
// },
// {
// ""fld"": ""WidgetCustom6"",
// ""fld"": ""CustomerCustom6"",
// ""required"": false,
// ""type"": 2
// },
// {
// ""fld"": ""WidgetCustom7"",
// ""fld"": ""CustomerCustom7"",
// ""required"": false,
// ""type"": 3
// }
@@ -175,9 +175,9 @@ namespace AyaNova.Util
// {
// Name = "Name starts with generic",
// UserId = 1,
// ListKey = "TestWidgetDataList",
// ListKey = "TestCustomerDataList",
// Public = true,
// ListView = @"[{""fld"": ""widgetname"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Generic""}]}}]"
// ListView = @"[{""fld"": ""customername"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Generic""}]}}]"
// };
// //Create and save to db
@@ -188,9 +188,9 @@ namespace AyaNova.Util
// {
// Name = "Awesome (lots of fields)",
// UserId = 1,
// ListKey = "TestWidgetDataList",
// ListKey = "TestCustomerDataList",
// Public = true,
// ListView = @"[{""fld"": ""widgetname"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Awesome""}]}},{""fld"":""widgetserial""},{""fld"":""widgetdollaramount""},{""fld"":""widgetusertype""},{""fld"":""widgetstartdate""},{""fld"":""widgetactive""},{""fld"":""username""},{""fld"":""widgettags""},{""fld"":""widgetcustom1""},{""fld"":""widgetcustom2""}]"
// ListView = @"[{""fld"": ""customername"",""filter"": {""any"":false,""items"": [{""op"": ""%-"",""value"": ""Awesome""}]}},{""fld"":""customerserial""},{""fld"":""customerdollaramount""},{""fld"":""customerusertype""},{""fld"":""customerstartdate""},{""fld"":""customeractive""},{""fld"":""username""},{""fld"":""customertags""},{""fld"":""customercustom1""},{""fld"":""customercustom2""}]"
// };
// //Create and save to db
@@ -227,8 +227,7 @@ namespace AyaNova.Util
//Generate owner and lead tech
await SeedUserAsync(log, 1, AuthorizationRoles.BizAdmin | AuthorizationRoles.Service | AuthorizationRoles.Inventory | AuthorizationRoles.OpsAdmin, UserType.Service);
//Generate one office person / secretary
await SeedUserAsync(log, 1, AuthorizationRoles.Service | AuthorizationRoles.Inventory | AuthorizationRoles.Accounting, UserType.NotService);
// await SeedWidgetAsync(log, 3);//keeping this here for now but must remove later
await SeedUserAsync(log, 1, AuthorizationRoles.Service | AuthorizationRoles.Inventory | AuthorizationRoles.Accounting, UserType.NotService);
await SeedVendorAsync(log, 10);
await SeedUnitModelAsync(log, 10);
await SeedCustomerAsync(log, 25);
@@ -287,7 +286,6 @@ namespace AyaNova.Util
//1 accountant / bookkeeper
await SeedUserAsync(log, 1, AuthorizationRoles.Accounting | AuthorizationRoles.BizAdminRestricted, UserType.NotService);
//await SeedWidgetAsync(log, 100);
await SeedVendorAsync(log, 50);
await SeedUnitModelAsync(log, 20);
await SeedCustomerAsync(log, 500);
@@ -356,7 +354,6 @@ namespace AyaNova.Util
//5 accountant / bookkeeper
await SeedUserAsync(log, 5, AuthorizationRoles.Accounting | AuthorizationRoles.BizAdminRestricted, UserType.NotService);
//await SeedWidgetAsync(log, 100);
await SeedVendorAsync(log, 100);
await SeedUnitModelAsync(log, 40);
await SeedCustomerAsync(log, 1000);
@@ -426,7 +423,6 @@ namespace AyaNova.Util
//accountant / bookkeeper
await SeedUserAsync(log, 20, AuthorizationRoles.Accounting | AuthorizationRoles.BizAdminRestricted, UserType.NotService);
//await SeedWidgetAsync(log, 100);
await SeedVendorAsync(log, 500);
await SeedUnitModelAsync(log, 200);
await SeedCustomerAsync(log, 10000);
@@ -1772,62 +1768,7 @@ namespace AyaNova.Util
}
#region WIDGET
// //////////////////////////////////////////////////////
// //Seed widget for testing
// //
// public async Task SeedWidgetAsync(ILogger log, int count)
// {
// //this is 4 times slower than doing it inside the loop below
// //seems counterintuitive but maybe it's to do with the db context not being refreshed?
// //RANDOM ROLES
// Array values = Enum.GetValues(typeof(UserType));
// Random random = new Random();
// DateTime seedStartWindow = DateTime.Now.AddYears(-1).AddMonths(-6);
// DateTime seedEndWindow = DateTime.Now.AddYears(1).AddMonths(6);
// for (int x = 0; x < count; x++)
// {
// Widget o = new Widget();
// o.Name = Uniquify(Fake.Commerce.ProductName());
// o.Active = true;
// DateTime dtSeed = Fake.Date.Between(seedStartWindow, seedEndWindow).ToUniversalTime();
// o.StartDate = dtSeed;
// o.EndDate = dtSeed.AddMinutes(60).ToUniversalTime();
// o.DollarAmount = Convert.ToDecimal(Fake.Commerce.Price());
// //Random but valid enum
// UserType randomUserType = (UserType)values.GetValue(random.Next(values.Length));
// o.UserType = randomUserType;
// o.Notes = Fake.Lorem.Sentence(null, 5);
// o.Tags = RandomTags();
// o.UserId = Fake.Random.Int(1, TotalSeededUsers);
// //RANDOM CUSTOM FIELD DATA
// var c1 = DateUtil.UniversalISO8661Format(Fake.Date.Between(DateTime.Now.AddYears(-1), DateTime.Now.AddYears(1)));
// var c2 = Fake.Lorem.Sentence(null, 5);
// var c3 = Fake.Random.Int(1, 99999999);
// var c4 = Fake.Random.Bool().ToString().ToLowerInvariant();
// var c5 = Fake.Random.Decimal();
// o.CustomFields = $@"{{c1:""{c1}"",c2:""{c2}"",c3:{c3},c4:{c4},c5:{c5}}}";
// //This seems wrong to do in a loop but is 4 times faster this way ?!?
// using (AyContext ct = ServiceProviderProvider.DBContext)
// {
// WidgetBiz biz = WidgetBiz.GetBiz(ct);
// var NewObject = await biz.CreateAsync(o);
// if (NewObject == null)
// {
// log.LogError($"Seeder::SeedWidget error creating {o.Name}\r\n" + biz.GetErrorsAsString());
// throw new System.Exception("Seeder::SeedWidget error creating widget\r\n" + biz.GetErrorsAsString());
// }
// }
// }
// }
#endregion
public HashSet<string> HashCompanyNames = new HashSet<string>();
private int TotalSeededCustomers = 0;