Пример #1
0
        private PostLoadsResponse ValidatePostedLoad(PostLoadsResponse response,
                                                     LoadEntity dbLoad,
                                                     PostingLoad load,
                                                     string[] bookedTransTypes,
                                                     List <ServiceTypeEntity> serviceTypes)
        {
            if (dbLoad == null)
            {
                response.ModelState.AddModelError($"urn:load:{load.LoadId}", "Load not found");
            }
            else
            {
                if (load.ShippersFSC < 0)
                {
                    response.ModelState.AddModelError($"urn:load:{load.LoadId}:{nameof(load.ShippersFSC)}", $"Shippers FSC value must be positive");
                }
                else if (load.ShippersFSC == 0 && !dbLoad.Customer.AllowZeroFuel && !HasFuelUpdateApi(dbLoad.CustomerId))
                {
                    response.ModelState.AddModelError($"urn:load:{load.LoadId}:{nameof(load.ShippersFSC)}", $"Shippers FSC value must be provided");
                }
                if (dbLoad.FuelRate != load.ShippersFSC && !dbLoad.Customer.AllowEditingFuel)
                {
                    response.ModelState.AddModelError($"urn:load:{load.LoadId}:{nameof(load.ShippersFSC)}", $"User is not allowed to edit Shippers FSC");
                }
                var loadAlreadyBooked = bookedTransTypes.Contains(dbLoad.LatestTransactionTypeId);
                if (loadAlreadyBooked)
                {
                    response.ModelState.AddModelError($"urn:load:{load.LoadId}", $"Load cannot be posted because it has already been booked. Current status: {dbLoad.LatestTransactionTypeId}");
                }
            }

            if (load.LineHaulRate <= 0)
            {
                response.ModelState.AddModelError($"urn:load:{load.LoadId}:{nameof(load.LineHaulRate)}", $"Line Haul rate value must be provided");
            }

            if (string.IsNullOrWhiteSpace(load.Commodity))
            {
                response.ModelState.AddModelError($"urn:load:{load.LoadId}:{nameof(load.Commodity)}", $"Commodity is required");
            }

            if (load.ServiceTypeIds != null && load.ServiceTypeIds.Count > 0 && !serviceTypes.Any(x => load.ServiceTypeIds.Contains(x.ServiceTypeId)))
            {
                response.ModelState.AddModelError($"urn:load:{load.LoadId}:{nameof(load.ServiceTypeIds)}", $"Invalid service type, not found.");
            }

            return(response);
        }
Пример #2
0
        public async Task <PostLoadsResponse> PostLoadsAsync(PostLoadsRequest request)
        {
            _securityService.GuardAction(SecurityActions.Loadshop_Ui_Shopit_Load_Post);
            var response = new PostLoadsResponse();
            var userPrimaryCustomerId = _context.Users.SingleOrDefault(u => u.IdentUserId == _userContext.UserId)?.PrimaryCustomerId;

            // Throw exceptions for non-user errors that the user cannot solve
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (!userPrimaryCustomerId.HasValue || userPrimaryCustomerId == Guid.Empty)
            {
                throw new ArgumentNullException(nameof(userPrimaryCustomerId));
            }

            if (!AuthorizedForCustomer(userPrimaryCustomerId.Value))
            {
                throw new UnauthorizedAccessException($"User is not authorized for customer: {userPrimaryCustomerId}");
            }

            if (string.IsNullOrWhiteSpace(request.CurrentUsername))
            {
                throw new ArgumentNullException(nameof(request.CurrentUsername));
            }

            if (request.Loads == null || request.Loads.Count <= 0)
            {
                response.ModelState.AddModelError($"urn:root", "PostLoads operation requires loads to be posted");
                return(response);
            }

            var loadIds = request.Loads.Select(x => x.LoadId).ToList();
            var dbLoads = await _context.Loads
                          .Include(x => x.LoadTransactions)
                          .Include(x => x.LoadStops)
                          .Include(x => x.CarrierScacs)
                          .Include(x => x.Customer)
                          .Include(x => x.Equipment)
                          .Include(x => x.PostedLoadCarrierGroups)
                          .Include(x => x.LoadServiceTypes)
                          .Where(x => loadIds.Contains(x.LoadId))
                          .ToListAsync();

            var serviceTypes = await _context.ServiceTypes.AsNoTracking().ToListAsync();

            //Verify submitting loads for only one customer at a time
            var groupedLoads = dbLoads.GroupBy(l => l.CustomerId);

            if (groupedLoads.Count() > 1)
            {
                throw new Exception("Cannot post loads from multiple shippers");
            }

            var contractedScacs  = _securityService.GetCustomerContractedScacsByPrimaryCustomer();
            var bookedTransTypes = new[] { TransactionTypes.Accepted, TransactionTypes.Pending, TransactionTypes.SentToShipperTender, TransactionTypes.PreTender };

            foreach (var load in request.Loads)
            {
                var dbLoad = dbLoads.FirstOrDefault(x => x.LoadId == load.LoadId);

                response = ValidatePostedLoad(response, dbLoad, load, bookedTransTypes, serviceTypes);

                var originalLoadHistory = _mapper.Map <LoadHistoryEntity>(dbLoad);

                var eligibleScacs = new List <string>();
                if (load.CarrierIds.Count > 0)
                {
                    var carrierScacGroups = contractedScacs
                                            .Where(x => load.CarrierIds.Contains(x.CarrierId))
                                            .Where(Utilities.QueryFilters.GetActiveCarrierScacDataFilter(request.RequestTime.Date))
                                            .GroupBy(x => x.CarrierId)
                                            .ToList();

                    var dbCarriers      = carrierScacGroups.Select(x => x.Key).ToList();
                    var missingCarriers = load.CarrierIds.Except(dbCarriers);

                    if (missingCarriers.Any())
                    {
                        response.ModelState.AddModelError($"urn:load:{load.LoadId}", $"The following carriers have no eligible SCACs defined: {string.Join(", ", missingCarriers)}");
                    }

                    eligibleScacs = carrierScacGroups.SelectMany(x => x.Select(y => y.Scac)).ToList();
                }
                else
                {
                    // When no Carriers are provided, we assume that we're posting to ALL eligible carriers for the customer
                    // Right now, TOPS is the only customer, so all Carriers and Scacs are eligible for TOPS, but in the
                    // future we'll require some lookup here to determine which Carriers are associated with the customer
                    eligibleScacs = contractedScacs
                                    .Where(x =>
                                           x.EffectiveDate <= request.RequestTime &&
                                           (x.ExpirationDate == null || x.ExpirationDate > request.RequestTime))
                                    .Select(x => x.Scac)
                                    .ToList();

                    if (!eligibleScacs.Any())
                    {
                        response.ModelState.AddModelError($"urn:load:{load.LoadId}", $"No eligible SCACs found");
                    }
                }

                /**
                 * The model state applies to the entire request, so we have to check that errors related to
                 * this load are helping to cause the invalid model state before we skip processing this load.
                 * If this load has no errors, then continue processing it successfully.
                 */
                if (!response.ModelState.IsValid && response.ModelState.Keys.Any(x => x.Contains(load.LoadId.ToString())))
                {
                    continue;
                }

                var postedCarrierGroupEntities = load.CarrierGroupIds.Select(groupId =>
                                                                             new PostedLoadCarrierGroupEntity()
                {
                    LoadId                   = dbLoad.LoadId,
                    LoadCarrierGroupId       = groupId,
                    PostedLoadCarrierGroupId = Guid.NewGuid()
                });

                dbLoad.PostedLoadCarrierGroups.Clear();
                dbLoad.PostedLoadCarrierGroups.AddRange(postedCarrierGroupEntities);

                dbLoad.Comments            = load.Comments;
                dbLoad.Commodity           = load.Commodity;
                dbLoad.LineHaulRate        = load.LineHaulRate;
                dbLoad.FuelRate            = load.ShippersFSC;
                dbLoad.SmartSpotRate       = load.SmartSpotRate ?? dbLoad.SmartSpotRate;
                dbLoad.DATGuardRate        = load.DATGuardRate ?? dbLoad.DATGuardRate;
                dbLoad.MachineLearningRate = load.MachineLearningRate ?? dbLoad.MachineLearningRate;
                dbLoad.AllCarriersPosted   = load.AllCarriersPosted;

                // remove any existing
                dbLoad.LoadServiceTypes?.Clear();

                if (load.ServiceTypeIds != null && load.ServiceTypeIds.Any())
                {
                    if (dbLoad.LoadServiceTypes == null)
                    {
                        dbLoad.LoadServiceTypes = new List <LoadServiceTypeEntity>();
                    }

                    var loadServiceTypes = load.ServiceTypeIds.Select(x => new LoadServiceTypeEntity()
                    {
                        LoadId        = load.LoadId,
                        ServiceTypeId = x
                    }).ToList();
                    dbLoad.LoadServiceTypes.AddRange(loadServiceTypes);
                }

                // Delete all existing LoadCarrierScacs for this load, before inserting new ones
                var existingCarrierScacs = _context.LoadCarrierScacs.Where(x => x.LoadId == dbLoad.LoadId).ToList();
                if (existingCarrierScacs.Count > 0)
                {
                    _context.LoadCarrierScacs.RemoveRange(existingCarrierScacs);
                }

                var carrierScacs = new List <LoadCarrierScacEntity>();
                foreach (var scac in eligibleScacs)
                {
                    var carrierScac = new LoadCarrierScacEntity
                    {
                        LoadId      = dbLoad.LoadId,
                        Scac        = scac,
                        CreateBy    = request.CurrentUsername,
                        CreateDtTm  = request.RequestTime,
                        LastChgBy   = request.CurrentUsername,
                        LastChgDtTm = request.RequestTime
                    };
                    carrierScacs.Add(carrierScac);
                }
                _context.LoadCarrierScacs.AddRange(carrierScacs);
                response.LoadCarrierScacs.AddRange(carrierScacs);

                var postedTx = new LoadTransactionEntity
                {
                    LoadId            = dbLoad.LoadId,
                    TransactionTypeId = TransactionTypes.Posted,
                    CreateBy          = request.CurrentUsername,
                    CreateDtTm        = request.RequestTime,
                    LastChgBy         = request.CurrentUsername,
                    LastChgDtTm       = request.RequestTime
                };
                _context.LoadTransactions.Add(postedTx);

                var newLoadHistory = _mapper.Map <LoadHistoryEntity>(dbLoad);
                if (_serviceUtilities.HasLoadChanged(originalLoadHistory, newLoadHistory))
                {
                    _context.LoadHistories.Add(newLoadHistory);
                }

                await _context.SaveChangesAsync(request.CurrentUsername);

                response.PostedLoads.Add(_mapper.Map <ShippingLoadData>(dbLoad));
            }

            return(response);
        }