Stub Customer object

This commit is contained in:
2020-05-01 20:45:06 +00:00
parent 7c2e37fcea
commit ce5f8f21c2
12 changed files with 689 additions and 28 deletions

View File

@@ -0,0 +1,270 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
[ApiController]
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class CustomerController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger<CustomerController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public CustomerController(AyContext dbcontext, ILogger<CustomerController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Get full Customer object
/// </summary>
/// <param name="id"></param>
/// <returns>A single Customer</returns>
[HttpGet("{id}")]
public async Task<IActionResult> GetCustomer([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
//Instantiate the business object handler
CustomerBiz biz = CustomerBiz.GetBiz(ct, HttpContext);
//NOTE: This is the first check and often the only check but in some cases with some objects this will also need to check biz object rules
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));
// NOTE: HERE would be the second check of biz rules before returning the object
// in cases where there is also a business rule to affect retrieval on top of basic rights
return Ok(ApiOkResponse.Response(o, !Authorized.HasModifyRole(HttpContext.Items, biz.BizType)));
}
/// <summary>
/// Put (update) Customer
/// </summary>
/// <param name="id"></param>
/// <param name="inObj"></param>
/// <returns></returns>
[HttpPut("{id}")]
public async Task<IActionResult> PutCustomer([FromRoute] long id, [FromBody] Customer inObj)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
CustomerBiz biz = CustomerBiz.GetBiz(ct, HttpContext);
var o = await biz.GetAsync(id, false);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
try
{
if (!await biz.PutAsync(o, inObj))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(ApiOkResponse.Response(new { ConcurrencyToken = o.ConcurrencyToken }, true));
}
/// <summary>
/// Patch (update) Customer
/// </summary>
/// <param name="id"></param>
/// <param name="concurrencyToken"></param>
/// <param name="objectPatch"></param>
/// <returns></returns>
[HttpPatch("{id}/{concurrencyToken}")]
public async Task<IActionResult> PatchCustomer([FromRoute] long id, [FromRoute] uint concurrencyToken, [FromBody]JsonPatchDocument<Customer> objectPatch)
{
//https://dotnetcoretutorials.com/2017/11/29/json-patch-asp-net-core/
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
CustomerBiz biz = CustomerBiz.GetBiz(ct, HttpContext);
var o = await biz.GetAsync(id, false);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
try
{
//patch and validate
if (!await biz.PatchAsync(o, objectPatch, concurrencyToken))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(ApiOkResponse.Response(new { ConcurrencyToken = o.ConcurrencyToken }, true));
}
/// <summary>
/// Post Customer
/// </summary>
/// <param name="inObj"></param>
/// <param name="apiVersion">Automatically filled from route path, no need to specify in body</param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> PostCustomer([FromBody] Customer inObj, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
//Instantiate the business object handler
CustomerBiz biz = CustomerBiz.GetBiz(ct, HttpContext);
//If a user has change roles
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Create and validate
Customer o = await biz.CreateAsync(inObj);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return CreatedAtAction(nameof(CustomerController.GetCustomer), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
}
/// <summary>
/// Duplicate Customer
/// </summary>
/// <param name="id">Create a duplicate of this items id</param>
/// <param name="apiVersion">Automatically filled from route path, no need to specify in body</param>
/// <returns></returns>
[HttpPost("duplicate/{id}")]
public async Task<IActionResult> DuplicateCustomer([FromRoute] long id, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
//Instantiate the business object handler
CustomerBiz biz = CustomerBiz.GetBiz(ct, HttpContext);
//If a user has change roles
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
var oSrc = await biz.GetAsync(id, false);
if (oSrc == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
//Create and validate
Customer o = await biz.DuplicateAsync(oSrc);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return CreatedAtAction(nameof(CustomerController.GetCustomer), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
}
/// <summary>
/// Delete Customer
/// </summary>
/// <param name="id"></param>
/// <returns>Ok</returns>
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteCustomer([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
CustomerBiz biz = CustomerBiz.GetBiz(ct, HttpContext);
var o = await biz.GetAsync(id, false);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!await biz.DeleteAsync(o))
return BadRequest(new ApiErrorResponse(biz.Errors));
return NoContent();
}
//------------
}//eoc
}//eons

View File

@@ -16,8 +16,10 @@ namespace AyaNova.PickList
//CoreBizObject add here
case AyaType.Widget:
return new WidgetPickList() as IAyaPickList;
case AyaType.User:
case AyaType.User:
return new UserPickList() as IAyaPickList;
case AyaType.Customer:
throw new System.NotImplementedException("PICKLIST NOT IMPLEMENTED");
}
return null;
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
namespace AyaNova.Biz
{
@@ -9,21 +10,23 @@ namespace AyaNova.Biz
public static class AyaFormFieldDefinitions
{
//DEFINE VALID KEYS HERE
// public const string WIDGET_KEY = "widget";
// public const string USER_KEY = "user";
public static List<string> AyaFormFieldDefinitionKeys
{
get
{
//CoreBizObject add here
List<string> l = new List<string>{
AyaType.Widget.ToString(),AyaType.User.ToString()
};
//return the names of all AyaTypes that have the corebizobject attribute
List<string> l = new List<string>();
var values = Enum.GetValues(typeof(AyaType));
foreach (AyaType t in values)
{
if (t.HasAttribute(typeof(CoreBizObjectAttribute)))
{
l.Add(t.ToString());
}
}
return l;
}
}

View File

@@ -18,8 +18,8 @@ namespace AyaNova.Biz
//CoreBizObject add here
//Search for that IN SERVER AND CLIENT CODE and you will see all areas that need coding for the new object
//***IMPORTANT: Also need to add translations for any new biz objects added that don't match exactly the name here in the key
//because enumlist gets it that way, i.e. "Global" would be the expected key
//***IMPORTANT: Also need to add translations for any new biz objects added that don't match exactly the name here in the key
//because enumlist gets it that way, i.e. "Global" would be the expected key
NoType = 0,
Global = 1,
@@ -34,7 +34,8 @@ namespace AyaNova.Biz
License = 5,
LogFile = 6,
PickListTemplate = 7,
DEPRECATED_REUSELATER_08 = 8,
[CoreBizObject]
Customer = 8,
ServerJob = 9,
DEPRECATED_10 = 10,
TrialSeeder = 11,
@@ -55,7 +56,7 @@ namespace AyaNova.Biz
//AyaNova.Biz.BizObjectNameFetcherDIRECT
//and in the CLIENT in ayatype.js
//and need TRANSLATION KEYS because any type could show in the event log at teh client end
//and need TRANSLATION KEYS because any type could show in the event log at teh client end
}

View File

@@ -31,6 +31,8 @@ namespace AyaNova.Biz
switch (aytype)
{
//CoreBizObject add here
case AyaType.Customer:
return await ct.Customer.AnyAsync(m => m.Id == id);
case AyaType.User:
return await ct.User.AnyAsync(m => m.Id == id);
case AyaType.Widget:

View File

@@ -19,11 +19,14 @@ namespace AyaNova.Biz
//Returns the biz object class that corresponds to the type presented
//Used by SEARCH and objects with JOBS
internal static BizObject GetBizObject(AyaType aytype, AyContext dbcontext, long userId = 1, AuthorizationRoles roles = AuthorizationRoles.All)
{
switch (aytype)
{
//CoreBizObject add here
case AyaType.Customer:
return new CustomerBiz(dbcontext, userId, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, roles);
case AyaType.User:
return new UserBiz(dbcontext, userId, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, roles);
case AyaType.Widget:

View File

@@ -3,6 +3,7 @@ namespace AyaNova.Biz
//Turn a type and ID into a displayable name
//this version uses a direct DataReader for performance in tight loops (search)
//Used by search
internal static class BizObjectNameFetcherDirect
{
@@ -21,15 +22,11 @@ namespace AyaNova.Biz
}
string TABLE = string.Empty;
string COLUMN = "name";
//CoreBizObject add here
//CoreBizObject add here BUT ONLY ADD IF AYATYPE NAME DIFFERS FROM TABLE NAME OR NO NAME FIELD AS PRIMARY NAME-LIKE COLUMN
switch (aytype)
{
case AyaType.User:
TABLE = "auser";
break;
case AyaType.Widget:
TABLE = "awidget";
break;
//Oddballs only, otherwise let default handle it
case AyaType.FileAttachment:
TABLE = "afileattachment";
@@ -42,9 +39,10 @@ namespace AyaNova.Biz
TABLE = "aformcustom";
COLUMN = "formkey";
break;
default:
throw new System.NotSupportedException($"AyaNova.BLL.BizObjectNameFetcher::Name type {aytype.ToString()} is not supported");
TABLE = "a" + aytype.ToString().ToLowerInvariant();
break;
}
cmd.CommandText = $"SELECT m.{COLUMN} FROM {TABLE} AS m WHERE m.id = {id} LIMIT 1";
using (var dr = cmd.ExecuteReader())

View File

@@ -31,6 +31,26 @@ namespace AyaNova.Biz
#region All roles initialization
//CoreBizObject add here
//TODO: BIZ objects, fine tune this stuff, best guess first pass here
////////////////////////////////////////////////////////////
//CUSTOMER
//
roles.Add(AyaType.Customer, new BizRoleSet()
{
Change = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.SalesFull | AuthorizationRoles.TechFull | AuthorizationRoles.AccountingFull,
ReadFullRecord = AuthorizationRoles.BizAdminLimited | AuthorizationRoles.DispatchLimited | AuthorizationRoles.SalesLimited | AuthorizationRoles.TechLimited,
Select = AuthorizationRoles.All
});
////////////////////////////////////////////////////////////
//GLOBAL BIZ SETTINGS
//

View File

@@ -0,0 +1,305 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.JsonPatch;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Models;
namespace AyaNova.Biz
{
internal class CustomerBiz : BizObject, ISearchAbleObject
{
internal CustomerBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
ct = dbcontext;
UserId = currentUserId;
UserTranslationId = userTranslationId;
CurrentUserRoles = UserRoles;
BizType = AyaType.Customer;
}
internal static CustomerBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
{
if (httpContext != null)
return new CustomerBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
else//when called internally for internal ops there will be no context so need to set default values for that
return new CustomerBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
internal async Task<bool> ExistsAsync(long id)
{
return await ct.Customer.AnyAsync(e => e.Id == id);
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
///
///
internal async Task<Customer> GetAsync(long fetchId, bool logTheGetEvent = true)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
var ret = await ct.Customer.SingleOrDefaultAsync(m => m.Id == fetchId);
if (logTheGetEvent && ret != null)
{
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct);
}
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//Called from route and also seeder
internal async Task<Customer> CreateAsync(Customer inObj)
{
await ValidateAsync(inObj, null);
if (HasErrors)
return null;
else
{
//do stuff with Customer
Customer outObj = inObj;
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
//Save to db
await ct.Customer.AddAsync(outObj);
await ct.SaveChangesAsync();
//Handle child and associated items:
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(outObj, true);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, outObj.Tags, null);
return outObj;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DUPLICATE
//
internal async Task<Customer> DuplicateAsync(Customer dbObj)
{
Customer outObj = new Customer();
CopyObject.Copy(dbObj, outObj, "Wiki");
// outObj.Name = Util.StringUtil.NameUniquify(outObj.Name, 255);
//generate unique name
string newUniqueName = string.Empty;
bool NotUnique = true;
long l = 1;
do
{
newUniqueName = Util.StringUtil.UniqueNameBuilder(dbObj.Name, l++, 255);
NotUnique = await ct.Customer.AnyAsync(m => m.Name == newUniqueName);
} while (NotUnique);
outObj.Name = newUniqueName;
outObj.Id = 0;
outObj.ConcurrencyToken = 0;
await ct.Customer.AddAsync(outObj);
await ct.SaveChangesAsync();
//Handle child and associated items:
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(outObj, true);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, outObj.Tags, null);
return outObj;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
//put
internal async Task<bool> PutAsync(Customer dbObj, Customer inObj)
{
//make a snapshot of the original for validation but update the original to preserve workflow
Customer SnapshotOfOriginalDBObj = new Customer();
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
//Replace the db object with the PUT object
CopyObject.Copy(inObj, dbObj, "Id");
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
//Set "original" value of concurrency token to input token
//this will allow EF to check it out
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken;
await ValidateAsync(dbObj, SnapshotOfOriginalDBObj);
if (HasErrors)
return false;
//Log event and save context
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
await SearchIndexAsync(dbObj, false);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObj.Tags, SnapshotOfOriginalDBObj.Tags);
return true;
}
//patch
internal async Task<bool> PatchAsync(Customer dbObj, JsonPatchDocument<Customer> objectPatch, uint concurrencyToken)
{
//Validate Patch is allowed
if (!ValidateJsonPatch<Customer>.Validate(this, objectPatch)) return false;
//make a snapshot of the original for validation but update the original to preserve workflow
Customer SnapshotOfOriginalDBObj = new Customer();
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
//Do the patching
objectPatch.ApplyTo(dbObj);
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
await ValidateAsync(dbObj, SnapshotOfOriginalDBObj);
if (HasErrors)
return false;
//Log event and save context
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
await SearchIndexAsync(dbObj, false);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObj.Tags, SnapshotOfOriginalDBObj.Tags);
return true;
}
private async Task SearchIndexAsync(Customer obj, bool isNew)
{
//SEARCH INDEXING
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
if (isNew)
await Search.ProcessNewObjectKeywordsAsync(SearchParams);
else
await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams);
}
public async Task<Search.SearchIndexProcessObjectParameters> GetSearchResultSummary(long id)
{
var obj = await ct.Customer.SingleOrDefaultAsync(m => m.Id == id);
var SearchParams = new Search.SearchIndexProcessObjectParameters();
if (obj != null)
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
return SearchParams;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal async Task<bool> DeleteAsync(Customer dbObj)
{
//Determine if the object can be deleted, do the deletion tentatively
//Probably also in here deal with tags and associated search text etc
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
if (HasErrors)
return false;
ct.Customer.Remove(dbObj);
await ct.SaveChangesAsync();
//Log event
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
//
//Can save or update?
private async Task ValidateAsync(Customer proposedObj, Customer currentObj)
{
//run validation and biz rules
bool isNew = currentObj == null;
//Name required
if (string.IsNullOrWhiteSpace(proposedObj.Name))
AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name");
//Name must be less than 255 characters
if (proposedObj.Name.Length > 255)
AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "Name", "255 max");
//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.Customer.AnyAsync(m => m.Name == proposedObj.Name && m.Id != proposedObj.Id))
{
AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name");
}
}
//Any form customizations to validate?
var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.Customer.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);
}
}
//Can delete?
// private void ValidateCanDelete(Customer inObj)
// {
// //whatever needs to be check to delete this object
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//JOB / OPERATIONS
//
//Other job handlers here...
/////////////////////////////////////////////////////////////////////
}//eoc
}//eons

View File

@@ -9,10 +9,8 @@ namespace AyaNova.Models
public virtual DbSet<Event> Event { get; set; }
public virtual DbSet<SearchDictionary> SearchDictionary { get; set; }
public virtual DbSet<SearchKey> SearchKey { get; set; }
public virtual DbSet<User> User { get; set; }
public virtual DbSet<UserOptions> UserOptions { get; set; }
public virtual DbSet<License> License { get; set; }
public virtual DbSet<Widget> Widget { get; set; }
public virtual DbSet<FileAttachment> FileAttachment { get; set; }
public virtual DbSet<OpsJob> OpsJob { get; set; }
public virtual DbSet<OpsJobLog> OpsJobLog { get; set; }
@@ -21,7 +19,11 @@ namespace AyaNova.Models
public virtual DbSet<DataListView> DataListView { get; set; }
public virtual DbSet<Tag> Tag { get; set; }
public virtual DbSet<FormCustom> FormCustom { get; set; }
public virtual DbSet<PickListTemplate> PickListTemplate { get; set; }
public virtual DbSet<PickListTemplate> PickListTemplate { get; set; }
public virtual DbSet<Widget> Widget { get; set; }
public virtual DbSet<User> User { get; set; }
public virtual DbSet<Customer> Customer { get; set; }
//Note: had to add this constructor to work with the code in startup.cs that gets the connection string from the appsettings.json file

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
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
public partial class Customer
{
public long Id { get; set; }
public uint ConcurrencyToken { get; set; }
[Required]
public string Name { get; set; }
public bool Active { get; set; }
public string Notes { get; set; }
public string Wiki {get;set;}
public string CustomFields { get; set; }
public List<string> Tags { get; set; }
public Customer()
{
Tags = new List<string>();
}
}//eoc
}//eons

View File

@@ -315,6 +315,28 @@ namespace AyaNova.Util
}
//////////////////////////////////////////////////
//MULTIPLE BIZ OBJECT tables
if (currentSchema < 11)
{
LogUpdateMessage(log);
//CUSTOMER
await ExecQueryAsync("CREATE TABLE acustomer (id BIGSERIAL PRIMARY KEY, name varchar(255) not null unique, active bool, " +
"notes text NULL, wiki text null, customfields text NULL, tags varchar(255) ARRAY NULL)");
await ExecQueryAsync("CREATE UNIQUE INDEX acustomer_name_id_idx ON acustomer (id, name);");
await ExecQueryAsync("CREATE INDEX acustomer_tags ON acustomer using GIN(tags)");
await SetSchemaLevelAsync(++currentSchema);
}
// //////////////////////////////////////////////////
// // WikiPage table
// if (currentSchema < 11)