/// <summary>
        /// Add a new contract to the database from an EveDataContract.
        /// </summary>
        /// <param name="eveDataContract">An EveDataContract object.</param>
        /// <param name="salesAgent">A SalesAgent object representing the contract owner.</param>
        /// <param name="batch">Optional parameter with a default of false. Is this addition part of a batch? If so, do not commit.</param>
        internal Contract AddContract(IEveDataContract eveDataContract, SalesAgent salesAgent, bool batch = false)
        {
            Contract newContract = new Contract();

            newContract.ContractId = eveDataContract.ContractId;
            newContract.AssigneeId = eveDataContract.AssigneeId;
            newContract.StartStationId = eveDataContract.StartStationId;
            newContract.Status = Conversion.StringToEnum<ContractStatus>(eveDataContract.Status.ToString(), ContractStatus.Deleted);
            newContract.Type = Conversion.StringToEnum<ContractType>(eveDataContract.Type.ToString(), ContractType.ItemExchange);
            newContract.Availability = Conversion.StringToEnum<ContractAvailability>(eveDataContract.Availability.ToString(), ContractAvailability.Private);
            newContract.Price = eveDataContract.Price;
            newContract.Volume = eveDataContract.Volume;
            newContract.DateIssued = eveDataContract.DateIssued;
            newContract.DateExpired = eveDataContract.DateExpired;
            newContract.IssuerCorpId = eveDataContract.IssuerCorpId;
            newContract.IssuerId = eveDataContract.IssuerId;
            newContract.ForCorp = eveDataContract.ForCorp;
            newContract.IsValid = false;

            // Populate the contract title/description field.
            if (eveDataContract.Title == string.Empty)
            {
                newContract.Title = "None";
            }
            else
            {
                newContract.Title = eveDataContract.Title;
            }

            // Populate the remaining contract details. These calls are cached.
            newContract.SolarSystemId = this.eveDataSource.GetStationSolarSystemId(newContract.StartStationId);
            newContract.SolarSystemName = this.eveDataSource.GetSolarSystemName(newContract.SolarSystemId);
            newContract.StartStationName = this.eveDataSource.GetStationName(newContract.StartStationId);

            // Attempt to match the contract contents to a ship fit id.
            newContract.ShipFitId = this.ContractShipFitMatch(newContract.ContractId, salesAgent);

            // If the ship fit was matched, set the contract as valid.
            if (newContract.ShipFitId != 0)
            {
                newContract.IsValid = true;
            }

            // Validate the new contract.
            if (this.doctrineShipsValidation.Contract(newContract).IsValid == true)
            {
                // Add the populated contract object to the database.
                this.doctrineShipsRepository.CreateContract(newContract);

                // If this addition is not part of a batch, commit changes to the database.
                if (batch == false)
                {
                    this.doctrineShipsRepository.Save();
                }
            }

            return newContract;
        }
        internal IValidationResult SalesAgent(SalesAgent salesAgent)
        {
            IValidationResult validationResult = new ValidationResult();

            // Is the salesAgent id valid?
            if (salesAgent.SalesAgentId <= 0 || salesAgent.SalesAgentId > int.MaxValue)
            {
                validationResult.AddError("SalesAgentId.Range", "SalesAgentId can not be less than or equal to 0. Also, the upper limit cannot exceed the max value of the int data type.");
            }

            // Does the salesAgent already exist in the database?
            if (this.doctrineShipsRepository.GetSalesAgent(salesAgent.SalesAgentId) != null)
            {
                validationResult.AddError("SalesAgentId.Exists", "SalesAgentId already exists in the database.");
            }

            return validationResult;
        }
        /// <summary>
        /// <para>Adds a sales agent from an api key and account id.</para>
        /// </summary>
        /// <param name="apiId">A valid eve api id (keyID).</param>
        /// <param name="apiKey">A valid eve api key (vCode).</param>
        /// <param name="accountId">The id of the account for which a sales agent should be added.</param>
        /// <returns>Returns a validation result object.</returns>
        internal IValidationResult AddSalesAgent(int apiId, string apiKey, int accountId)
        {
            IValidationResult validationResult = new ValidationResult();

            // Check the remaining sales agent licences for the account.
            if (this.GetRemainingSalesAgentLicences(accountId) <= 0)
            {
                validationResult.AddError("SalesAgent.Licences", "You have exceeded the number of sales agent licences available with your account subscription plan.");
            }
            else
            {
                // Fetch details about the Api Key from Eve Data.
                IEveDataApiKey apiKeyInfo = this.eveDataSource.GetApiKeyInfo(apiId, apiKey);

                if (apiKeyInfo != null)
                {
                    // Validate the api key.
                    validationResult = this.doctrineShipsValidation.ApiKey(apiKeyInfo);
                    if (validationResult.IsValid == true)
                    {
                        // Use the api key info to populate a new sales agent object.
                        SalesAgent newSalesAgent = new SalesAgent();

                        // If this is a character or account key use the character details, if a corp key use the corp details.
                        if (apiKeyInfo.Type == EveDataApiKeyType.Character || apiKeyInfo.Type == EveDataApiKeyType.Account)
                        {
                            // If this is an account key, the first character in the list will be used.
                            newSalesAgent.SalesAgentId = apiKeyInfo.Characters.FirstOrDefault().CharacterId;
                            newSalesAgent.Name = apiKeyInfo.Characters.FirstOrDefault().CharacterName;
                            newSalesAgent.ImageUrl = eveDataSource.GetCharacterPortraitUrl(newSalesAgent.SalesAgentId);
                            newSalesAgent.IsCorp = false;
                        }
                        else if (apiKeyInfo.Type == EveDataApiKeyType.Corporation)
                        {
                            newSalesAgent.SalesAgentId = apiKeyInfo.Characters.FirstOrDefault().CorporationId;
                            newSalesAgent.Name = apiKeyInfo.Characters.FirstOrDefault().CorporationName;
                            newSalesAgent.ImageUrl = eveDataSource.GetCorporationLogoUrl(newSalesAgent.SalesAgentId);
                            newSalesAgent.IsCorp = true;
                        }

                        // Populate the remaining properties.
                        newSalesAgent.AccountId = accountId;
                        newSalesAgent.ApiId = apiId;
                        newSalesAgent.ApiKey = apiKey;
                        newSalesAgent.IsActive = true;
                        newSalesAgent.LastForce = DateTime.UtcNow;
                        newSalesAgent.LastContractRefresh = DateTime.UtcNow;

                        // Validate the new sales agent.
                        validationResult = this.doctrineShipsValidation.SalesAgent(newSalesAgent);
                        if (validationResult.IsValid == true)
                        {
                            // Add the new sales agent and log the event.
                            this.doctrineShipsRepository.CreateSalesAgent(newSalesAgent);
                            this.doctrineShipsRepository.Save();
                            logger.LogMessage("Sales Agent '" + newSalesAgent.Name + "' Successfully Added For Account Id: " + newSalesAgent.AccountId, 2, "Message", MethodBase.GetCurrentMethod().Name);
                        }
                    }
                }
                else
                {
                    validationResult.AddError("ApiKey.Valid", "An invalid api key was entered or the eve api is currently unavailable.");
                }
            }

            return validationResult;
        }
 public void UpdateSalesAgent(SalesAgent salesAgent)
 {
     SalesAgentOperations.UpdateSalesAgent(salesAgent);
 }
 public SalesAgent CreateSalesAgent(SalesAgent salesAgent)
 {
     return SalesAgentOperations.CreateSalesAgent(salesAgent);
 }
 public SalesAgent AddSalesAgent(SalesAgent salesAgent)
 {
     return SalesAgentOperations.AddSalesAgent(salesAgent);
 }
 internal SalesAgent CreateSalesAgent(SalesAgent salesAgent)
 {
     salesAgent.ObjectState = ObjectState.Added;
     this.unitOfWork.Repository<SalesAgent>().Insert(salesAgent);
     return salesAgent;
 }
 internal SalesAgent AddSalesAgent(SalesAgent salesAgent)
 {
     this.unitOfWork.Repository<SalesAgent>().Insert(salesAgent);
     return salesAgent;
 }
 internal void UpdateSalesAgent(SalesAgent salesAgent)
 {
     salesAgent.ObjectState = ObjectState.Modified;
     this.unitOfWork.Repository<SalesAgent>().Update(salesAgent);
 }
        /// <summary>
        /// Refresh the contracts of a single sales agent from EveData.
        /// </summary>
        /// <param name="salesAgents">A sales agent object.</param>
        /// <param name="batch">Optional parameter with a default of false. Is this refresh part of a batch? If so, do not commit.</param>
        internal void RefreshContracts(SalesAgent salesAgent, bool batch = false)
        {
            // Fetch the sales agent's contracts from EveData.
            IEnumerable<IEveDataContract> eveDataContracts = this.eveDataSource.GetContracts(salesAgent.ApiId, salesAgent.ApiKey, salesAgent.SalesAgentId, salesAgent.IsCorp);

            if (eveDataContracts != null && eveDataContracts.Any() != false)
            {
                // Fetch a hashset of the current contract ids in the database.
                HashSet<long> existingContractIds = this.doctrineShipsRepository.GetSalesAgentContractIds(salesAgent.SalesAgentId, salesAgent.IsCorp);

                foreach (var eveDataContract in eveDataContracts)
                {
                    // If the sales agent is a character but the current contract is on behalf of a corporation, skip it.
                    // (The contract should be handled and listed under a corporation agent instead).
                    if (salesAgent.IsCorp == false && eveDataContract.ForCorp == true)
                    {
                        continue;
                    }

                    // If the contract is not of type 'ItemExchange' then skip it.
                    if (eveDataContract.Type != EveDataContractType.ItemExchange)
                    {
                        continue;
                    }

                    // Does the contract already exist in the database?
                    if (existingContractIds.Contains(eveDataContract.ContractId))
                    {
                        // If the status of the Eve contract is no longer 'Outstanding', delete it from the database.
                        if (eveDataContract.Status != EveDataContractStatus.Outstanding)
                        {
                            this.doctrineShipsRepository.DeleteContract(eveDataContract.ContractId);
                        }
                    }
                    else
                    {
                        // Only attempt to add a contract if it is currently of status 'Outstanding'.
                        if (eveDataContract.Status == EveDataContractStatus.Outstanding)
                        {
                            this.AddContract(eveDataContract, salesAgent, batch: true);
                        }
                    }
                }

                // Set the agent's last refresh time.
                salesAgent.LastContractRefresh = DateTime.UtcNow;

                // Update the agent's timestamp in the database.
                this.doctrineShipsRepository.UpdateSalesAgent(salesAgent);

                // If this addition is not part of a batch, commit changes to the database.
                if (batch == false)
                {
                    this.doctrineShipsRepository.Save();
                }
            }
        }
        /// <summary>
        /// Attempts to match a contract's contents to a ship fit.
        /// </summary>
        /// <param name="contractId">The id of the contract to be matched.</param>
        /// <param name="salesAgent">A sales agent object representing the contract owner.</param>
        /// <returns>An integer ship fit id or 0 if no match was made.</returns>
        internal int ContractShipFitMatch(long contractId, SalesAgent salesAgent)
        {
            int shipFitId = 0;

            // Fetch the list of items included in the contract.
            IEnumerable<IEveDataContractItem> contractItems = this.eveDataSource.GetContractItems(salesAgent.ApiId, salesAgent.ApiKey, contractId, salesAgent.SalesAgentId, salesAgent.IsCorp);

            if (contractItems != null && contractItems.Any() == true)
            {
                string concatComponents = string.Empty;
                IEnumerable<ShipFitComponent> compressedContractItems = new List<ShipFitComponent>();

                // Compress the contract items components list, removing duplicates but adding the quantities.
                compressedContractItems = contractItems
                    .OrderBy(o => o.TypeId)
                    .GroupBy(u => u.TypeId)
                    .Select(x => new ShipFitComponent()
                    {
                        ComponentId = x.Key,
                        Quantity = x.Sum(p => p.Quantity)
                    });

                // Concatenate all components and their quantities into a single string.
                foreach (var item in compressedContractItems)
                {
                    concatComponents += item.ComponentId + item.Quantity;
                }

                // Generate a hash for the fitting and salt it with the sales agent's account id. This permits identical fits across accounts.
                string fittingHash = Security.GenerateHash(concatComponents, salesAgent.AccountId.ToString());

                // Attempt to find a ship fit with the same components list as the contract.
                if (!ShipFitList.TryGetValue(fittingHash, out shipFitId))
                {
                    shipFitId = 0;
                }
            }

            return shipFitId;
        }
 public IValidationResult SalesAgent(SalesAgent salesAgent)
 {
     return SalesAgentCheck.SalesAgent(salesAgent);
 }