diff --git a/server/AyaNova/Controllers/DataListSavedColumnViewController.cs b/server/AyaNova/Controllers/DataListSavedColumnViewController.cs
new file mode 100644
index 00000000..870a0583
--- /dev/null
+++ b/server/AyaNova/Controllers/DataListSavedColumnViewController.cs
@@ -0,0 +1,120 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Authorization;
+
+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}/data-list-column-view")]
+ [Produces("application/json")]
+ [Authorize]
+ public class DataListSavedColumnViewController : ControllerBase
+ {
+ private readonly AyContext ct;
+ private readonly ILogger log;
+ private readonly ApiServerState serverState;
+
+
+ ///
+ /// ctor
+ ///
+ ///
+ ///
+ ///
+ public DataListSavedColumnViewController(AyContext dbcontext, ILogger logger, ApiServerState apiServerState)
+ {
+ ct = dbcontext;
+ log = logger;
+ serverState = apiServerState;
+ }
+
+
+ // ///
+ // /// Get full DataListSavedColumnView object
+ // ///
+ // ///
+ // /// A single DataListSavedColumnView
+ // [HttpGet("{id}")]
+ // public async Task GetDataListSavedColumnView([FromRoute] long id)
+ // {
+ // if (!serverState.IsOpen)
+ // return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
+
+ // //Instantiate the business object handler
+ // DataListSavedColumnViewBiz biz = DataListSavedColumnViewBiz.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));
+ // }
+
+
+
+
+
+
+ ///
+ /// Put (update) the single DataListSavedColumnView for this userid / listkey combo
+ ///
+ /// Replacement default saved column view
+ ///
+ [HttpPut]
+ public async Task PutDataListSavedColumnView([FromBody] DataListSavedColumnView 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
+ DataListSavedColumnViewBiz biz = DataListSavedColumnViewBiz.GetBiz(ct, HttpContext);
+
+
+ try
+ {
+ if (!await biz.PutAsync(inObj))
+ return BadRequest(new ApiErrorResponse(biz.Errors));
+ }
+ catch (DbUpdateConcurrencyException)
+ {
+ if (!await biz.ExistsAsync(inObj.Id))
+ return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
+ else
+ return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
+ }
+ return Ok();
+ //return Ok(ApiOkResponse.Response(new { Concurrency = o.Concurrency }));
+ }
+
+
+
+ //------------
+
+
+ }//eoc
+}//eons
\ No newline at end of file
diff --git a/server/AyaNova/biz/AyaType.cs b/server/AyaNova/biz/AyaType.cs
index 6a426b30..b41708f4 100644
--- a/server/AyaNova/biz/AyaType.cs
+++ b/server/AyaNova/biz/AyaType.cs
@@ -133,7 +133,8 @@ namespace AyaNova.Biz
PartAssembly = 65,
[CoreBizObject]
PartWarehouse = 66,
- PartInventory = 67
+ PartInventory = 67,
+ DataListSavedColumnView = 68
diff --git a/server/AyaNova/biz/DataListSavedColumnViewBiz.cs b/server/AyaNova/biz/DataListSavedColumnViewBiz.cs
new file mode 100644
index 00000000..7dd65f37
--- /dev/null
+++ b/server/AyaNova/biz/DataListSavedColumnViewBiz.cs
@@ -0,0 +1,234 @@
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using Microsoft.EntityFrameworkCore;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+using AyaNova.Util;
+using AyaNova.Api.ControllerHelpers;
+using AyaNova.Models;
+using AyaNova.DataList;
+
+
+namespace AyaNova.Biz
+{
+
+
+ internal class DataListSavedColumnViewBiz : BizObject
+ {
+
+ internal DataListSavedColumnViewBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
+ {
+ ct = dbcontext;
+ UserId = currentUserId;
+ UserTranslationId = userTranslationId;
+ CurrentUserRoles = UserRoles;
+ BizType = AyaType.DataListSavedColumnView;
+ }
+
+ internal static DataListSavedColumnViewBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
+ {
+ if (httpContext != null)
+ return new DataListSavedColumnViewBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
+ else
+ return new DataListSavedColumnViewBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //EXISTS
+ internal async Task ExistsAsync(long id)
+ {
+ return await ct.DataListSavedColumnView.AnyAsync(z => z.Id == id);
+ }
+
+
+
+ // ////////////////////////////////////////////////////////////////////////////////////////////////
+ // //CREATE
+ // internal async Task CreateAsync(DataListSavedColumnView inObj)
+ // {
+ // await ValidateAsync(inObj, true);
+ // if (HasErrors)
+ // return null;
+ // else
+ // {
+ // //do stuff with datafilter
+ // DataListSavedColumnView outObj = inObj;
+ // outObj.UserId = UserId;
+
+
+ // await ct.DataListSavedColumnView.AddAsync(outObj);
+ // await ct.SaveChangesAsync();
+
+ // //Handle child and associated items:
+
+ // //EVENT LOG
+ // await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
+
+
+
+ // return outObj;
+
+ // }
+ // }
+
+
+
+
+
+
+ // ////////////////////////////////////////////////////////////////////////////////////////////////
+ // /// GET
+
+ // //Get one
+ // internal async Task 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.DataListSavedColumnView.SingleOrDefaultAsync(z => z.Id == fetchId && (z.Public == true || z.UserId == UserId));
+ // if (logTheGetEvent && ret != null)
+ // {
+ // //Log
+ // await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct);
+ // }
+ // return ret;
+ // }
+
+
+
+
+ // //get ViewList (NOT PAGED)
+ // internal async Task> GetViewListAsync(string listKey)
+ // {
+ // List items = new List();
+ // if (!string.IsNullOrWhiteSpace(listKey))
+ // {
+ // items = await ct.DataListSavedColumnView
+ // .AsNoTracking()
+ // .Where(z => z.ListKey == listKey && (z.Public == true || z.UserId == UserId))
+ // .OrderBy(z => z.Name)
+ // .Select(z => new NameIdItem()
+ // {
+ // Id = z.Id,
+ // Name = z.Name
+ // }).ToListAsync();
+
+ // }
+ // return items;
+ // }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //UPDATE
+ //
+
+ //put
+ internal async Task PutAsync(DataListSavedColumnView inObj)
+ {
+ //preserve the owner ID if none was specified
+ if (inObj.UserId == 0)
+ inObj.UserId = dbObject.UserId;
+
+ //Replace the db object with the PUT object
+ CopyObject.Copy(inObj, dbObject, "Id");
+ //Set "original" value of concurrency token to input token
+ //this will allow EF to check it out
+ ct.Entry(dbObject).OriginalValues["Concurrency"] = inObj.Concurrency;
+
+ await ValidateAsync(dbObject);
+ if (HasErrors)
+ return false;
+ await ct.SaveChangesAsync();
+
+ //Log modification and save context
+ await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
+
+
+
+ return true;
+ }
+
+
+ // ////////////////////////////////////////////////////////////////////////////////////////////////
+ // //DELETE
+ // //
+ // internal async Task DeleteAsync(DataListSavedColumnView dbObject)
+ // {
+ // //Determine if the object can be deleted, do the deletion tentatively
+ // //Probably also in here deal with tags and associated search text etc
+
+ // //FUTURE POSSIBLE NEED
+ // //ValidateCanDelete(dbObject);
+
+ // if (HasErrors)
+ // return false;
+ // ct.DataListSavedColumnView.Remove(dbObject);
+ // await ct.SaveChangesAsync();
+
+ // //Delete sibling objects
+
+ // //Event log process delete
+ // await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Name, ct);
+
+
+ // //Delete search index
+ // //Search.ProcessDeletedObjectKeywords(dbObject.Id, BizType);
+
+
+ // return true;
+ // }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //VALIDATION
+ //
+
+ //Can save or update?
+ private async Task ValidateAsync(DataListSavedColumnView inObj)
+ {
+
+
+ if (inObj.UserId != UserId)
+ AddError(ApiErrorCode.NOT_AUTHORIZED, "UserId", "Only own view can be modified");
+
+
+ if (string.IsNullOrWhiteSpace(inObj.ListKey))
+ AddError(ApiErrorCode.VALIDATION_REQUIRED, "ListKey");
+
+
+//TODO TODO TODO
+//THIS SHOULD USE NAME LIST INSTEAD OR BE DONE AT OBJECT ABOVE FOR DEFAULTS, SAME FOR FILTER BIZ THIS IS BASED OFF OF NO NEED TO INSTANTIATE
+ var DataList = DataListFactory.GetAyaDataList(inObj.ListKey);
+
+ if (DataList == null)
+ {
+ AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListKey", $"ListKey \"{inObj.ListKey}\" DataListKey is not valid");
+ }
+
+ if (inObj.ListKey.Length > 255)
+ AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "ListKey", "255 max");
+
+ //check if filter can be reconstructed into a C# filter object
+ try
+ {
+ List dataListFilterOptions = JsonConvert.DeserializeObject>(inObj.Filter);
+ }
+ catch (System.Exception ex)
+ {
+ AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Filter", "Filter is not valid JSON string, can't convert to List, error: " + ex.Message);
+ }
+
+ return;
+ }
+
+
+
+ /////////////////////////////////////////////////////////////////////
+
+ }//eoc
+
+
+}//eons
+