using CAPI.Custom.Data; using CAPI.Custom.Data.Repositories; using CAPI.Custom.Domain; using CAPI.Custom.Entities; using CAPI.Custom.Repositories; using CAPI.Custom.Security; using CAPI.Custom.Services.Events.Quotes; using framework_business; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CAPI.Custom.Services { public class QuoteService { private readonly QuoteRepository repository; private vUserShared User => repository.User; public QuoteService(QuoteRepository repository) { this.repository = repository ?? new QuoteRepository(); } public QuoteService() : this(null) { } public IQueryable GetAll() => repository.Entities; public Quote GetByItemId(int itemId) => repository.GetByItemId(itemId); public Sale GetSale(int quoteId) => repository.GetSale(quoteId); #region Child Queries public IQueryable GetEnquiryItems(int quoteId) => repository.GetEnquiryItems(quoteId); public IQueryable GetRequestItems(int quoteId) => repository.GetRequestItems(quoteId); public IQueryable GetItems(int quoteId) { repository.FixQuoteItemSorting(quoteId) .ApplyCATListPriceToQuoteItems(quoteId) .CheckPrevisoulyQuotedItemPrice(quoteId); return repository.GetItems(quoteId); } public IQueryable GetBuyoutSuppliers(int quoteId) => repository.GetBuyoutSuppliers(quoteId); public IQueryable GetFinalItems(int quoteId) => repository.GetFinalItems(quoteId); public IQueryable GetFinalStockItems(int quoteId) => GetFinalItems(quoteId).Where(p => "STOCK".Equals(p.Supplier, StringComparison.OrdinalIgnoreCase)); public IQueryable GetBuyoutItems(int buyoutSupplierId) => repository.GetBuyoutItems(buyoutSupplierId); public QuoteItem GetItemByItemId(int itemId) => repository.GetItemByItemId(itemId); public QuoteBuyoutSupplier GetBuyoutSupplierByItemId(int buyoutSupplierId) => repository.GetBuyoutSupplierByItemId(buyoutSupplierId); #endregion #region Other Queries public IEnumerable GetCreatedByUserDisplays() => repository.GetCreatedByUserDisplays(); public IEnumerable GetCustomerTradingNames() => repository.GetCustomerTradingNames(); public IEnumerable GetStatuses() => repository.GetStatuses(); #endregion public Quote Create(int customerId, int? contactId = null, DateTime? quoteDate = null) { var customers = new CustomerRepository(); var users = new EntityReadOnlyRepository(); var customer = customers.GetByItemId(customerId); var setup = repository.Setup; if (customer.IsNull()) throw new KeyNotFoundException($"Unable to find Customer with itemID: {customerId}"); var contacts = customers.GetContacts(customerId).ToList() .OrderBy(p => Math.Abs(customer.PrimaryEmail.CompareTo(p.Email))) .ThenBy(p => p.Name); var contact = contacts.FirstOrDefault(); if (contactId.HasValue) { contact = contacts.FirstOrDefault(p => p.itemID == contactId.Value) ?? contact; } var address = new Address(customer.PhysicalAddress); var invoiceNumber = repository.GetNextSalesInvoiceNumber(); var quoteNumber = repository.GetQuoteNumberFromInvoiceNumber(invoiceNumber); var quote = new Quote { Number = quoteNumber, CreatedBy = User.recId, LastUpdatedBy = User.recId, LastUpdatedByDisplay = User.userDisplay, Date = quoteDate ?? DateTime.Now, // Customer Info CustomerCode = customer.Code, CustomerCurrency = customer.Currency.NullOrEmptyCoalesce("ZAR"), CustomerIsParent = customer.IsParent, CustomerLegalEntity = customer.LegalEntity, CustomerParentCompany = customer.ParentCompany, CustomerPhysicalAddress = customer.PhysicalAddress, CustomerPostalAddress = customer.PostalAddress, CustomerTradingName = customer.TradingName, CustomerType = customer.Type, CustomerVATNumber = customer.VATRegistered, CustomerVATRate = customer.VATRate.NullOrEmptyCoalesce($"{setup.vatRate}"), CustomerDefaultDiscount = customer.DefaultDiscount, CustomerDefaultMargin = customer.DefaultMargin, // Customer Contact Info CustomerContactEmail = contact.Email ?? customer.PrimaryEmail, CustomerContactMobileNumber = contact?.Mobile ?? string.Empty, CustomerContactName = contact?.Name ?? customer.Contacts, CustomerContactTelephone = contact?.Telephone ?? customer.Telephone, // Other repeated ItemsDefaultMargin = customer.DefaultMargin, DeliveryCity = address.City, DeliveryCountry = address.Country, DeliveryState = address.State, DeliveryPostalCode = address.PostalCode, DeliveryStreet = address.Street, DeliverySuburb = address.Suburb }; quote = repository.Context.Create(quote); DomainEvents.Dispatch(new QuoteCreatedEvent { InvoiceNumber = invoiceNumber, QuoteId = quote.itemID, UserId = User.recId }); return GetByItemId(quote.itemID); } public Quote Update(Quote quote) { var stored = repository.GetByItemId(quote.itemID); var statusChanged = stored.QuoteStatus != quote.QuoteStatus; var oldStatus = stored.StatusId.GetValueOrDefault(); var newStatus = quote.StatusId.GetValueOrDefault(); quote.LastUpdatedBy = User.recId; quote.LastUpdatedByDisplay = User.userDisplay; quote.LastUpdatedDate = DateTime.Now; quote = repository.Update(quote); DomainEvents.Dispatch(new QuoteUpdatedEvent { QuoteId = quote.itemID, UserId = User.recId }); if (statusChanged && newStatus == QuoteStatus.Accepted) { DomainEvents.Dispatch(new QuoteAcceptedEvent { QuoteId = quote.itemID, UserId = User.recId }); } return quote; } public QuoteBuyoutSupplier GetOrCreateBuyoutSupplier(int quoteId, int supplierId) { var supplierRepository = new SupplierRepository(); var supplier = supplierRepository.GetByItemId(supplierId); if (supplier.IsNull()) throw new Exception($"Could not find Supplier with itemID: {supplierId}"); var buyoutSupplier = GetBuyoutSuppliers(quoteId) .FirstOrDefault(p => p.TradingName == supplier.TradingName); if (buyoutSupplier.IsNull()) { buyoutSupplier = repository.CreateBuyoutSupplier(quoteId, supplierId); DomainEvents.Dispatch(new BuyoutSupplierCreatedEvent() { BuyoutSupplierId = buyoutSupplier.itemID, UserId = User.recId }); } return buyoutSupplier; } public QuoteBuyoutSupplier UpdateBuyoutSupplier(int buyoutSupplierId, decimal? freight = null, decimal? duty = null, int? buyoutStatus = null, string notes = null) { var stored = GetBuyoutSupplierByItemId(buyoutSupplierId); if (stored.IsNull()) throw new Exception($"Could not find BuyoutSupplier: {buyoutSupplierId}"); if (freight.HasValue) stored.Freight = freight; if (duty.HasValue) stored.Duty = duty; if (buyoutStatus.HasValue) stored.Status = buyoutStatus; if (notes.IsNotNull() && notes != stored.Notes) stored.Notes = notes; stored = repository.Context.Update(stored); DomainEvents.Dispatch(new BuyoutSupplierUpdatedEvent() { BuyoutSupplierId = buyoutSupplierId, UserId = User.recId }); return stored; } public Quote AcceptQuote(int quoteId) { var quote = GetByItemId(quoteId); if (quote.StatusId == QuoteStatus.Accepted) { return quote; } quote.StatusId = QuoteStatus.Accepted; repository.Save(); DomainEvents.Dispatch(new QuoteAcceptedEvent { QuoteId = quoteId, UserId = User.recId }); return GetByItemId(quoteId); } public void ProcessQuote(int quoteId) { var processor = new QuoteProcessor(); processor.ProcessQuote(quoteId); DomainEvents.Dispatch(new QuoteProcessedEvent { QuoteId = quoteId, UserId = User.recId }); } public void RecalculateQuoteItems(IEnumerable items) { if (items.Any()) { var quoteId = items.First().ParentSurfaceItemId.GetValueOrDefault(); repository.RecalculateQuoteItems(items); DomainEvents.Dispatch(new QuoteItemsRecalculatedEvent { QuoteId = quoteId, UserId = User.recId }); } } public IQueryable RecalculateRequestItemsByMasterNumber(int quoteId, string masterNumber) { new RequestItemsCalculator(repository).Execute(quoteId, masterNumber.ToUpper()); repository.Save(); return GetRequestItems(quoteId).Where(p => masterNumber.ToUpper() == p.MasterNumber); } public QuoteBuyoutItem RecalculateBuyoutItem(QuoteBuyoutItem item) { var result = repository.RecalculateBuyoutItem(item); DomainEvents.Dispatch(new BuyoutSupplierUpdatedEvent() { BuyoutSupplierId = item.ParentSurfaceItemId.GetValueOrDefault(), UserId = User.recId }); return result; } public QuoteBuyoutItem UpdateBuyoutItem(QuoteBuyoutItem item) { var result = repository.UpdateBuyoutItem(item); DomainEvents.Dispatch(new BuyoutSupplierUpdatedEvent() { BuyoutSupplierId = item.ParentSurfaceItemId.GetValueOrDefault(), UserId = User.recId }); return result; } public QuoteBuyoutItem CloneBuyoutItem(int itemId) { var result = repository.CloneBuyoutItem(itemId); DomainEvents.Dispatch(new BuyoutSupplierUpdatedEvent() { BuyoutSupplierId = result.ParentSurfaceItemId.GetValueOrDefault(), UserId = User.recId }); return result; } public void RefreshItemsPriceAndAvailability(int quoteId) => repository.RefreshItemsPriceAndAvailability(quoteId); } }