From 6bcee0dd88fb655dc017c496e58813c279d7affd Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Mon, 26 Jul 2021 15:40:06 +0000 Subject: [PATCH] --- server/AyaNova/biz/QuoteStatusBiz.cs | 220 ++++++++++ server/AyaNova/util/Seeder.cs | 574 ++++++++++++++++++++++++++- 2 files changed, 791 insertions(+), 3 deletions(-) create mode 100644 server/AyaNova/biz/QuoteStatusBiz.cs diff --git a/server/AyaNova/biz/QuoteStatusBiz.cs b/server/AyaNova/biz/QuoteStatusBiz.cs new file mode 100644 index 00000000..7a14e568 --- /dev/null +++ b/server/AyaNova/biz/QuoteStatusBiz.cs @@ -0,0 +1,220 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using AyaNova.Util; +using AyaNova.Api.ControllerHelpers; +using AyaNova.Models; + +namespace AyaNova.Biz +{ + internal class QuoteStatusBiz : BizObject, ISearchAbleObject + { + internal QuoteStatusBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles) + { + ct = dbcontext; + UserId = currentUserId; + UserTranslationId = userTranslationId; + CurrentUserRoles = UserRoles; + BizType = AyaType.QuoteStatus; + } + + internal static QuoteStatusBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null) + { + if (httpContext != null) + return new QuoteStatusBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items)); + else + return new QuoteStatusBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //EXISTS + internal async Task ExistsAsync(long id) + { + return await ct.QuoteStatus.AnyAsync(z => z.Id == id); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //CREATE + // + internal async Task CreateAsync(QuoteStatus newObject) + { + await ValidateAsync(newObject, null); + if (HasErrors) + return null; + else + { + await ct.QuoteStatus.AddAsync(newObject); + await ct.SaveChangesAsync(); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct); + await SearchIndexAsync(newObject, true); + return newObject; + } + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //GET + // + internal async Task GetAsync(long id, bool logTheGetEvent = true) + { + var ret = await ct.QuoteStatus.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id); + if (logTheGetEvent && ret != null) + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, BizType, AyaEvent.Retrieved), ct); + return ret; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //UPDATE + // + internal async Task PutAsync(QuoteStatus 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; + } + + + await ValidateAsync(putObject, dbObject); + 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); + return putObject; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //DELETE + // + internal async Task DeleteAsync(long id) + { + using (var transaction = await ct.Database.BeginTransactionAsync()) + { + try + { + var dbObject = await GetAsync(id, false); + if (dbObject == null) + { + AddError(ApiErrorCode.NOT_FOUND); + return false; + } + await ValidateCanDeleteAsync(dbObject); + if (HasErrors) + return false; + ct.QuoteStatus.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 FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct); + await transaction.CommitAsync(); + } + catch + { + //Just re-throw for now, let exception handler deal, but in future may want to deal with this more here + throw; + } + return true; + } + } + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //SEARCH + // + private async Task SearchIndexAsync(QuoteStatus 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) + { + var obj = await GetAsync(id, false); + var SearchParams = new Search.SearchIndexProcessObjectParameters(); + DigestSearchText(obj, SearchParams); + return SearchParams; + } + + public void DigestSearchText(QuoteStatus obj, Search.SearchIndexProcessObjectParameters searchParams) + { + if (obj != null) + searchParams.AddText(obj.Notes) + .AddText(obj.Name); + } + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //VALIDATION + // + + private async Task ValidateAsync(QuoteStatus proposedObj, QuoteStatus currentObj) + { + + bool isNew = currentObj == null; + + //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.QuoteStatus.AnyAsync(m => m.Name == proposedObj.Name && m.Id != proposedObj.Id)) + { + AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name"); + } + } + + } + + private async Task ValidateCanDeleteAsync(QuoteStatus inObj) + { + //MIGRATE_OUTSTANDING - check workorder records once wo is coded here + await Task.CompletedTask; + //Referential integrity + //FOREIGN KEY CHECKS + if (await ct.QuoteState.AnyAsync(m => m.QuoteStatusId == inObj.Id)) + AddError(ApiErrorCode.VALIDATION_REFERENTIAL_INTEGRITY, "generalerror", await Translate("QuoteStatus")); + } + + + + + + + + ///////////////////////////////////////////////////////////////////// + + }//eoc + + +}//eons + diff --git a/server/AyaNova/util/Seeder.cs b/server/AyaNova/util/Seeder.cs index 432bcca9..aaef7f42 100644 --- a/server/AyaNova/util/Seeder.cs +++ b/server/AyaNova/util/Seeder.cs @@ -1331,6 +1331,165 @@ namespace AyaNova.Util } + //--------- quote status + + ///////////////////////////////////////////////////// + //WorkorderStatus + { + + { + QuoteStatus stat = new QuoteStatus(); + stat.Name = "Submitted"; + stat.Active = true; + stat.Color = "#c00000"; + stat.Completed = false; + stat.Locked = true; + stat.SelectRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.RemoveRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.Notes = "Use to lock quote after given to customer and wait for approval"; + using (AyContext ct = ServiceProviderProvider.DBContext) + { + QuoteStatusBiz biz = QuoteStatusBiz.GetBiz(ct); + var NewObject = await biz.CreateAsync(stat); + + if (NewObject == null) + { + var err = $"Seeder::SeedKnownObjects error creating quote status\r\n{biz.GetErrorsAsString()}"; + log.LogError(err); + throw new System.Exception(err); + } + } + } + + { + QuoteStatus stat = new QuoteStatus(); + stat.Name = "Awarded"; + stat.Active = true; + stat.Color = "#80ffff"; + stat.Completed = false; + stat.Locked = false; + stat.SelectRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.RemoveRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting | AuthorizationRoles.Service; + stat.Notes = "Waiting for work order to be generated"; + using (AyContext ct = ServiceProviderProvider.DBContext) + { + QuoteStatusBiz biz = QuoteStatusBiz.GetBiz(ct); + var NewObject = await biz.CreateAsync(stat); + + if (NewObject == null) + { + var err = $"Seeder::SeedKnownObjects error creating quote status\r\n{biz.GetErrorsAsString()}"; + log.LogError(err); + throw new System.Exception(err); + } + } + } + + { + QuoteStatus stat = new QuoteStatus(); + stat.Name = "In progress"; + stat.Active = true; + stat.Color = "#00ff00"; + stat.Completed = false; + stat.Locked = false; + stat.SelectRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.RemoveRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.Notes = "In process of completing this quote"; + using (AyContext ct = ServiceProviderProvider.DBContext) + { + QuoteStatusBiz biz = QuoteStatusBiz.GetBiz(ct); + var NewObject = await biz.CreateAsync(stat); + + if (NewObject == null) + { + var err = $"Seeder::SeedKnownObjects error creating quote status\r\n{biz.GetErrorsAsString()}"; + log.LogError(err); + throw new System.Exception(err); + } + } + } + + { + QuoteStatus stat = new QuoteStatus(); + stat.Name = "Not repairable"; + stat.Active = true; + stat.Color = "#ff0000"; + stat.Completed = true; + stat.Locked = true; + stat.SelectRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.RemoveRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.Notes = "Beyond economical repair"; + using (AyContext ct = ServiceProviderProvider.DBContext) + { + QuoteStatusBiz biz = QuoteStatusBiz.GetBiz(ct); + var NewObject = await biz.CreateAsync(stat); + + if (NewObject == null) + { + var err = $"Seeder::SeedKnownObjects error creating quote status\r\n{biz.GetErrorsAsString()}"; + log.LogError(err); + throw new System.Exception(err); + } + } + } + + { + QuoteStatus stat = new QuoteStatus(); + stat.Name = "New"; + stat.Active = true; + stat.Color = "#8080ff"; + stat.Completed = false; + stat.Locked = true; + stat.SelectRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.RemoveRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.Notes = "New quote required; Sales to complete this Quote for submission"; + using (AyContext ct = ServiceProviderProvider.DBContext) + { + QuoteStatusBiz biz = QuoteStatusBiz.GetBiz(ct); + var NewObject = await biz.CreateAsync(stat); + + if (NewObject == null) + { + var err = $"Seeder::SeedKnownObjects error creating quote status\r\n{biz.GetErrorsAsString()}"; + log.LogError(err); + throw new System.Exception(err); + } + } + } + + + + + { + QuoteStatus stat = new QuoteStatus(); + stat.Name = "Not awarded"; + stat.Active = true; + stat.Color = "#f2f2f2"; + stat.Completed = true; + stat.Locked = true; + stat.SelectRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + stat.RemoveRoles = AuthorizationRoles.BizAdmin | AuthorizationRoles.Sales | AuthorizationRoles.Accounting; + + using (AyContext ct = ServiceProviderProvider.DBContext) + { + QuoteStatusBiz biz = QuoteStatusBiz.GetBiz(ct); + var NewObject = await biz.CreateAsync(stat); + + if (NewObject == null) + { + var err = $"Seeder::SeedKnownObjects error creating quote status\r\n{biz.GetErrorsAsString()}"; + log.LogError(err); + throw new System.Exception(err); + } + } + } + } + //--------- /quote status + + + + + ///////////////////////////////////////////////////// //TASKGROUP // @@ -2259,7 +2418,7 @@ namespace AyaNova.Util //var rerun=await biz.GetAsync(NewObject.Id,false); //newObject is still being tracked so can just set the unitid and save it here - NewObject.UnitId=newUnit.Id; + NewObject.UnitId = newUnit.Id; await ct.SaveChangesAsync(); //await biz.PutAsync(NewObject); @@ -2945,7 +3104,7 @@ namespace AyaNova.Util woItemExpense = new WorkOrderItemExpense() { UserId = RandomServiceTechUserId(), - // TotalCost = cost * 2m, + // TotalCost = cost * 2m, ChargeAmount = cost * 2.2m, ChargeToCustomer = true, ReimburseUser = true, @@ -3074,12 +3233,421 @@ namespace AyaNova.Util } + + + //--------------------- QUOTE -------------------------- + + private int TotalSeededQuotes = 0; + ////////////////////////////////////////////////////// + //Quote + // + public async Task SeedQuoteAsync(ILogger log, int count) + { + DateTime seedStartWindow = DateTime.UtcNow.AddMonths(-9); + DateTime seedEndWindow = DateTime.UtcNow.AddMonths(3); + for (int x = 0; x < count; x++) + { + Quote o = new Quote(); + o.Notes = Fake.Lorem.Sentence(); + o.Tags = RandomTags(); + if (Fake.Random.Bool())//50% have projects + o.ProjectId = Fake.Random.Long(1, TotalSeededProjects); + var tempDate = Fake.Date.Between(seedStartWindow, seedEndWindow); + var tempHour = Fake.Random.Int(9, 17);//9am to 5 pm (except some times may be in different dst state so this will be out by an hour for example depending on time of year and time zone in question) + var woDate = DesiredTimeInUtc(new DateTime(tempDate.Year, tempDate.Month, tempDate.Day, tempHour, 0, 0)); + + o.CreatedDate = woDate > DateTime.UtcNow ? DateTime.UtcNow : woDate;//no created dates in future but want a range of past dates to show off age of wo + // o.CompleteByDate = woDate.AddDays(5); + // o.CustomerContactName = "contact name here"; + o.CustomerId = GetRandomCustomerId();//Fake.Random.Long(1, TotalSeededCustomers); + + using (AyContext ct = ServiceProviderProvider.DBContext) + { + var cust = await ct.Customer.AsNoTracking().FirstAsync(z => z.Id == o.CustomerId); + o.Latitude = cust.Latitude; + o.Longitude = cust.Longitude; + o.Address = cust.Address; + o.City = cust.City; + o.Region = cust.Region; + o.Country = cust.Country; + + + if (cust.BillHeadOffice && cust.HeadOfficeId != null) + { + var head = await ct.HeadOffice.AsNoTracking().FirstAsync(z => z.Id == cust.HeadOfficeId); + o.PostAddress = head.PostAddress; + o.PostCity = head.PostCity; + o.PostRegion = head.PostRegion; + o.PostCountry = head.PostCountry; + o.PostCode = head.PostCode; + } + else + { + + o.PostAddress = cust.PostAddress; + o.PostCity = cust.PostCity; + o.PostRegion = cust.PostRegion; + o.PostCountry = cust.PostCountry; + o.PostCode = cust.PostCode; + } + } + + + o.CustomerReferenceNumber = "crf-" + Fake.Finance.Account(4); + o.InternalReferenceNumber = "irf-" + Fake.Finance.Account(4); + //o.ServiceDate = woDate; + + int woItemCount = Fake.Random.Int(1, 4); + for (int y = 0; y < woItemCount; y++) + { + var woItem = new QuoteItem() + { + Sequence = y + 1, + Notes = $"itemnotes - {y} ", + TechNotes = $"technotes - {y}", + RequestDate = woDate.AddMinutes(y), + WorkOrderItemPriorityId = Fake.Random.Long(1, 5),//there are 5 different sample priorities + WorkOrderItemStatusId = Fake.Random.Long(1, 3)//there are 3 different sample woitem status + }; + + //UNITS + var woItemUnit = new QuoteItemUnit() + { + UnitId = GetRandomUnitForCustomer(o.CustomerId), + Notes = Fake.Lorem.Sentence() + }; + woItem.Units.Add(woItemUnit); + + woItemUnit = new QuoteItemUnit() + { + UnitId = GetRandomUnitForCustomer(o.CustomerId), + Notes = Fake.Lorem.Sentence() + }; + woItem.Units.Add(woItemUnit); + + //SCHEDULED USERS + var woItemScheduledUser = new QuoteItemScheduledUser() + { + UserId = RandomServiceTechUserId(), + EstimatedQuantity = 1, + StartDate = woDate, + StopDate = woDate.AddHours(1) + }; + woItem.ScheduledUsers.Add(woItemScheduledUser); + + + woItemScheduledUser = new QuoteItemScheduledUser() + { + UserId = RandomServiceTechUserId(), + EstimatedQuantity = 2, + StartDate = woDate, + StopDate = woDate.AddHours(1) + }; + woItem.ScheduledUsers.Add(woItemScheduledUser); + + if (y == 1) + { + //known tech and subcontractor on every item + woItemScheduledUser = new QuoteItemScheduledUser() + { + UserId = KnownUserTechId, + EstimatedQuantity = 2, + StartDate = woDate, + StopDate = woDate.AddHours(2) + }; + woItem.ScheduledUsers.Add(woItemScheduledUser); + + woItemScheduledUser = new QuoteItemScheduledUser() + { + UserId = KnownUserSubContractorId, + EstimatedQuantity = 2, + StartDate = woDate, + StopDate = woDate.AddHours(2) + }; + woItem.ScheduledUsers.Add(woItemScheduledUser); + } + + if (y == 3) + { + //known restricted tech and subcontractor on some items + woItemScheduledUser = new QuoteItemScheduledUser() + { + UserId = KnownUserTechRestrictedId, + EstimatedQuantity = 2, + StartDate = woDate, + StopDate = woDate.AddHours(2) + }; + woItem.ScheduledUsers.Add(woItemScheduledUser); + + woItemScheduledUser = new QuoteItemScheduledUser() + { + UserId = KnownUserSubContractorRestrictedId, + EstimatedQuantity = 2, + StartDate = woDate, + StopDate = woDate.AddHours(2) + }; + woItem.ScheduledUsers.Add(woItemScheduledUser); + } + + //PARTS + var woItemPart = new QuoteItemPart() + { + + Quantity = 1, + PartId = Fake.Random.Long(1, TotalSeededParts), + PartWarehouseId = 1 + }; + woItem.Parts.Add(woItemPart); + + woItemPart = new QuoteItemPart() + { + + Quantity = 1, + PartId = Fake.Random.Long(1, TotalSeededParts), + PartWarehouseId = 1 + }; + woItem.Parts.Add(woItemPart); + + + + //LOANERS + var woItemLoan = new QuoteItemLoan() + { + OutDate = woDate.AddHours(1), + DueDate = woDate.AddHours(4), + Quantity = 4, + Rate = LoanUnitRateUnit.Hours, + LoanUnitId = Fake.Random.Long(1, TotalSeededLoanUnits) + }; + woItem.Loans.Add(woItemLoan); + + woItemLoan = new QuoteItemLoan() + { + OutDate = woDate.AddHours(2), + DueDate = woDate.AddHours(3), + Quantity = 1, + Rate = LoanUnitRateUnit.Hours, + LoanUnitId = Fake.Random.Long(1, TotalSeededLoanUnits) + }; + woItem.Loans.Add(woItemLoan); + + + //LABOR + var techId = RandomServiceTechUserId(); + var woItemLabor = new QuoteItemLabor() + { + UserId = techId, + ServiceRateQuantity = 1, + ServiceStartDate = woDate, + ServiceStopDate = woDate.AddHours(1), + ServiceRateId = Fake.Random.Long(1, TotalSeededServiceRates), + ServiceDetails = Fake.Lorem.Sentence() + }; + woItem.Labors.Add(woItemLabor); + + woItemLabor = new QuoteItemLabor() + { + UserId = RandomServiceTechUserId(), + ServiceRateQuantity = 2, + ServiceStartDate = woDate, + ServiceStopDate = woDate.AddHours(1), + ServiceRateId = Fake.Random.Long(1, TotalSeededServiceRates), + ServiceDetails = Fake.Lorem.Sentence() + }; + woItem.Labors.Add(woItemLabor); + + + //TRAVEL + var woItemTravel = new QuoteItemTravel() + { + UserId = techId, + TravelRateQuantity = 1, + TravelStartDate = woDate, + TravelStopDate = woDate.AddHours(1), + TravelRateId = Fake.Random.Long(1, TotalSeededTravelRates), + TravelDetails = Fake.Lorem.Sentence(), + Distance = Fake.Random.Decimal(1.0m, 20.0m) + }; + woItem.Travels.Add(woItemTravel); + + woItemTravel = new QuoteItemTravel() + { + UserId = RandomServiceTechUserId(), + TravelRateQuantity = 2, + TravelStartDate = woDate, + TravelStopDate = woDate.AddHours(1), + TravelRateId = Fake.Random.Long(1, TotalSeededTravelRates), + TravelDetails = Fake.Lorem.Sentence(), + Distance = Fake.Random.Decimal(1.0m, 20.0m) + }; + woItem.Travels.Add(woItemTravel); + + //TASKS + var woItemTask = new QuoteItemTask() + { + CompletedByUserId = techId, + Task = "Dis-assemble", + Sequence = 1, + Status = WorkorderItemTaskCompletionType.Incomplete + }; + woItem.Tasks.Add(woItemTask); + + woItemTask = new QuoteItemTask() + { + CompletedByUserId = techId, + Task = "Lubricate", + Sequence = 2, + Status = WorkorderItemTaskCompletionType.Incomplete + }; + woItem.Tasks.Add(woItemTask); + + + woItemTask = new QuoteItemTask() + { + CompletedByUserId = techId, + Task = "Repair", + Sequence = 3, + Status = WorkorderItemTaskCompletionType.Incomplete + }; + woItem.Tasks.Add(woItemTask); + + woItemTask = new QuoteItemTask() + { + CompletedByUserId = techId, + Task = "Re-assemble", + Sequence = 4, + Status = WorkorderItemTaskCompletionType.Incomplete + }; + woItem.Tasks.Add(woItemTask); + + woItemTask = new QuoteItemTask() + { + CompletedByUserId = techId, + Task = "Test and confirm repair", + Sequence = 5, + Status = WorkorderItemTaskCompletionType.Incomplete + }; + woItem.Tasks.Add(woItemTask); + + + + + //EXPENSES + var cost = Fake.Random.Decimal(1, 10); + var woItemExpense = new QuoteItemExpense() + { + UserId = RandomServiceTechUserId(), + //TotalCost = cost, + ChargeAmount = cost * 1.2m, + ChargeToCustomer = true, + ReimburseUser = true, + ChargeTaxCodeId = TCGoods, + Name = Fake.Commerce.ProductName() + + }; + woItem.Expenses.Add(woItemExpense); + + woItemExpense = new QuoteItemExpense() + { + UserId = RandomServiceTechUserId(), + // TotalCost = cost * 2m, + ChargeAmount = cost * 2.2m, + ChargeToCustomer = true, + ReimburseUser = true, + ChargeTaxCodeId = TCGoods, + Name = Fake.Commerce.ProductName() + + }; + woItem.Expenses.Add(woItemExpense); + + + //OUTSIDE SERVICES + var ShippingCost = Fake.Random.Decimal(5, 20); + var RepairCost = Fake.Random.Decimal(50, 1000); + var woItemOutsideService = new QuoteItemOutsideService() + { + UnitId = GetRandomUnitForCustomer(o.CustomerId), + Notes = Fake.Lorem.Sentence(), + VendorSentToId = Fake.Random.Long(1, TotalSeededVendors), + VendorSentViaId = Fake.Random.Long(1, TotalSeededVendors), + RMANumber = "RMA" + Fake.Finance.Account(6), + TrackingNumber = "TR" + Fake.Finance.Account(8), + RepairCost = RepairCost, + RepairPrice = RepairCost * 1.5m, + ShippingCost = ShippingCost, + ShippingPrice = ShippingCost * 1.5m, + SentDate = woDate, + ETADate = woDate.AddDays(7), + ReturnDate = woDate.AddDays(8), + TaxCodeId = 1 + }; + woItem.OutsideServices.Add(woItemOutsideService); + + ShippingCost = Fake.Random.Decimal(5, 20); + RepairCost = Fake.Random.Decimal(50, 1000); + woItemOutsideService = new QuoteItemOutsideService() + { + UnitId = GetRandomUnitForCustomer(o.CustomerId), + Notes = Fake.Lorem.Sentence(), + VendorSentToId = Fake.Random.Long(1, TotalSeededVendors), + VendorSentViaId = Fake.Random.Long(1, TotalSeededVendors), + RMANumber = "RMA" + Fake.Finance.Account(6), + TrackingNumber = "TR" + Fake.Finance.Account(8), + RepairCost = RepairCost, + RepairPrice = RepairCost * 1.5m, + ShippingCost = ShippingCost, + ShippingPrice = ShippingCost * 1.5m, + SentDate = woDate, + ETADate = woDate.AddDays(7), + ReturnDate = woDate.AddDays(8), + TaxCodeId = 1 + }; + woItem.OutsideServices.Add(woItemOutsideService); + + o.Items.Add(woItem); + } + + //sample status changes + { + var WoState = new QuoteState() + { + QuoteStatusId = (long)SeedQuoteStatus.InProcess, + UserId = RandomServiceTechUserId(), + Created = woDate.AddMinutes(5) + }; + o.States.Add(WoState); + } + + + + + + //This seems wrong to do in a loop but is 4 times faster this way ?!? + using (AyContext ct = ServiceProviderProvider.DBContext) + { + QuoteBiz biz = QuoteBiz.GetBiz(ct); + var NewObject = await biz.QuoteCreateAsync(o, false); + TotalSeededQuotes++; + if (NewObject == null) + { + var err = $"Seeder::SeedQuote error creating {o.Serial}\r\n{biz.GetErrorsAsString()}"; + log.LogError(err); + throw new System.Exception(err); + } + } + } + } + + //---------------------- /quote ------------------------ + + + private long GetRandomUnitForCustomer(long customerId) { var l = CustomerUnits.Where(x => x.Key == customerId).FirstOrDefault();//Fake.Random.Long(1, TotalSeededUnits); var numUnits = l.Value.Count(); //because faker values are INCLUSIVE and this is going to be used as an index on an array need to -1 each end of the range - var i = Fake.Random.Int(0, numUnits-1); + var i = Fake.Random.Int(0, numUnits - 1); return l.Value[i]; }