using System; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using System.Linq; using Sockeye.Util; using Sockeye.Api.ControllerHelpers; using Sockeye.Models; using Newtonsoft.Json.Linq; using System.Collections.Generic; using Microsoft.Extensions.Logging; namespace Sockeye.Biz { internal class CustomerNoteBiz : BizObject, ISearchAbleObject, IReportAbleObject, IExportAbleObject, INotifiableObject { internal CustomerNoteBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles) { ct = dbcontext; UserId = currentUserId; UserTranslationId = userTranslationId; CurrentUserRoles = UserRoles; BizType = SockType.CustomerNote; } internal static CustomerNoteBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null) { if (httpContext != null) return new CustomerNoteBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items)); else return new CustomerNoteBiz(ct, 1, ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin); } //////////////////////////////////////////////////////////////////////////////////////////////// //EXISTS internal async Task ExistsAsync(long id) { return await ct.CustomerNote.AnyAsync(z => z.Id == id); } //////////////////////////////////////////////////////////////////////////////////////////////// //CREATE // internal async Task CreateAsync(CustomerNote newObject) { //await ValidateAsync(newObject, null); if (HasErrors) return null; else { newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); await ct.CustomerNote.AddAsync(newObject); await ct.SaveChangesAsync(); await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, SockEvent.Created), ct); await SearchIndexAsync(newObject, true); await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); return newObject; } } //////////////////////////////////////////////////////////////////////////////////////////////// //GET // internal async Task GetAsync(long id, bool logTheGetEvent = true) { var ret = await ct.CustomerNote.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id); if (logTheGetEvent && ret != null) await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, BizType, SockEvent.Retrieved), ct); return ret; } //////////////////////////////////////////////////////////////////////////////////////////////// //UPDATE // internal async Task PutAsync(CustomerNote putObject) { CustomerNote 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); //no validate required 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, SockEvent.Modified), ct); await SearchIndexAsync(putObject, false); await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, putObject.Tags, dbObject.Tags); return putObject; } //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE // internal async Task DeleteAsync(long id, Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction parentTransaction = null) { //this may be part of a larger delete operation involving other objects (e.g. Customer delete and remove contacts) //if so then there will be a parent transaction otherwise we make our own Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction transaction = null; if (parentTransaction == null) transaction = await ct.Database.BeginTransactionAsync(); CustomerNote dbObject = await GetAsync(id, false); if (dbObject == null) { AddError(ApiErrorCode.NOT_FOUND); return false; } if (HasErrors) return false; if (HasErrors) return false; ct.CustomerNote.Remove(dbObject); await ct.SaveChangesAsync(); await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "CustomerNote", 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 if it's ours if (parentTransaction == null) await transaction.CommitAsync(); return true; } //////////////////////////////////////////////////////////////////////////////////////////////// //SEARCH // private async Task SearchIndexAsync(CustomerNote 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 GetSearchResultSummary(long id, SockType specificType) { var obj = await GetAsync(id); var SearchParams = new Search.SearchIndexProcessObjectParameters(); DigestSearchText(obj, SearchParams); return SearchParams; } public void DigestSearchText(CustomerNote obj, Search.SearchIndexProcessObjectParameters searchParams) { if (obj != null) searchParams.AddText(obj.Notes) .AddText(obj.Tags); } //////////////////////////////////////////////////////////////////////////////////////////////// //REPORTING // public async Task GetReportData(DataListSelectedRequest dataListSelectedRequest, Guid jobId) { 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.CustomerNote.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; batchResults = null; foreach (CustomerNote w in orderedList) { if (!ReportRenderManager.KeepGoing(jobId)) return null; await PopulateVizFields(w); var jo = JObject.FromObject(w); ReportData.Add(jo); } orderedList = null; } vc.Clear(); return ReportData; } //populate viz fields from provided object private async Task PopulateVizFields(CustomerNote o) { if (!vc.Has("customer", o.CustomerId)) vc.Add(await ct.Customer.AsNoTracking().Where(x => x.Id == o.CustomerId).Select(x => x.Name).FirstOrDefaultAsync(), "customer", o.CustomerId); o.CustomerViz = vc.Get("customer", o.CustomerId); if (!vc.Has("user", o.UserId)) vc.Add(await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync(), "user", o.UserId); o.UserViz = vc.Get("user", o.UserId); } private VizCache vc = new VizCache(); //////////////////////////////////////////////////////////////////////////////////////////////// // IMPORT EXPORT // public async Task GetExportData(DataListSelectedRequest dataListSelectedRequest, Guid jobId) { //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, jobId); } //////////////////////////////////////////////////////////////////////////////////////////////// //VALIDATION // // private async Task ValidateAsync(CustomerNote proposedObj, CustomerNote currentObj) // { // // bool isNew = currentObj == null; // // //Any form customizations to validate? // // var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == SockType.CustomerNote.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(CustomerNote inObj) // { // //whatever needs to be check to delete this object // } //////////////////////////////////////////////////////////////////////////////////////////////// // NOTIFICATION PROCESSING // public async Task HandlePotentialNotificationEvent(SockEvent ayaEvent, ICoreBizObjectModel proposedObj, ICoreBizObjectModel currentObj = null) { if (ServerBootConfig.MIGRATING) return; ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger(); log.LogDebug($"HandlePotentialNotificationEvent processing: [SockType:{this.BizType}, AyaEvent:{ayaEvent}]"); bool isNew = currentObj == null; var oProposed = (CustomerNote)proposedObj; oProposed.CustomerViz = await ct.Customer.AsNoTracking().Where(x => x.Id == oProposed.CustomerId).Select(x => x.Name).FirstOrDefaultAsync(); proposedObj.Name = oProposed.CustomerViz; //STANDARD EVENTS FOR ALL OBJECTS await NotifyEventHelper.ProcessStandardObjectEvents(ayaEvent, proposedObj, ct); //SPECIFIC EVENTS FOR THIS OBJECT }//end of process notifications //////////////////////////////////////////////////////////////////////////////////////////////// //JOB / OPERATIONS // //Other job handlers here... ///////////////////////////////////////////////////////////////////// }//eoc }//eons