public static void AddTransByCalcProfitFromAssets(EMMADataSet.TransactionsDataTable trans,
     List<FinanceAccessParams> accessParams, int itemID, bool calcProfitFromAssets)
 {
     lock(tableAdapter)
     {
         bool oldClearBeforeFill = tableAdapter.ClearBeforeFill;
         tableAdapter.ClearBeforeFill = false;
         EMMADataSet.TransactionsDataTable tmpTable = new EMMADataSet.TransactionsDataTable();
         tableAdapter.FillByCalcProfitFromAssets(tmpTable, FinanceAccessParams.BuildAccessList(accessParams),
             itemID, calcProfitFromAssets);
         foreach (EMMADataSet.TransactionsRow tmpTrans in tmpTable)
         {
             EMMADataSet.TransactionsRow match = trans.FindByID(tmpTrans.ID);
             if (match == null)
             {
                 trans.ImportRow(tmpTrans);
             }
         }
         tableAdapter.ClearBeforeFill = oldClearBeforeFill;
     }
 }
        /// <summary>
        /// Update the database transactions table from the specified XML.
        /// </summary>
        /// <param name="corc"></param>
        /// <param name="fileXML"></param>
        /// <returns></returns>
        private int UpdateTransactionsFromXML(CharOrCorp corc, XmlDocument fileXML, short walletID)
        {
            int retVal = 0;
            EMMADataSet.TransactionsDataTable transData = new EMMADataSet.TransactionsDataTable();
            long highestIDSoFar = _apiSettings.GetHighestID(corc, APIDataType.Transactions);
            long highestID = 0;
            DateTime ticker = DateTime.UtcNow.AddSeconds(-10);

            try
            {
                int updated = 0;

                XmlNodeList transEntries = null;
                XmlDocument xml = new XmlDocument();

                UpdateStatus(0, 1, "Getting transactions from file", "", false);
                transEntries = EveAPI.GetResults(fileXML);
                UpdateStatus(1, 1, "", transEntries.Count + " entries found in file.", false);

                if (transEntries != null && transEntries.Count > 0)
                {
                    int batchPrg = 0;
                    UpdateStatus(0, transEntries.Count, "Processing transactions", "", false);

                    XmlNode entryIDNode = transEntries[0].SelectSingleNode("@transactionID");
                    //long fileMaxID = long.Parse(entryIDNode.Value,
                    //    System.Globalization.CultureInfo.InvariantCulture.NumberFormat);

                    // Loop through the results returned from this call to the API and add the line to
                    // the data table if the transactionID is not already in the database.
                    foreach (XmlNode transEntry in transEntries)
                    {
                        XmlNode transIDNode = transEntry.SelectSingleNode("@transactionID");
                        long transID = long.Parse(transIDNode.Value,
                            System.Globalization.CultureInfo.InvariantCulture.NumberFormat);

                        if (transID > highestID) { highestID = transID; }

                        //if (transID > highestIDSoFar)
                        //{
                        if (!Transactions.TransactionExists(transData, transID) &&
                            transData.FindByID(transID) == null)
                        {
                            // Actually create the line and add it to the data table
                            SortedList<long, string> nameIDs = new SortedList<long, string>();
                            EMMADataSet.TransactionsRow newRow = BuildTransRow(transID, transData,
                                transEntry, walletID, nameIDs, false);

                            transData.AddTransactionsRow(newRow);
                            retVal++;

                            // This section searches the character, item and station ref type tables
                            // for the values used in this new transaction entry.
                            // If they are not present in the table then they are added.
                            #region Check other tables and add values if needed.
                            foreach (KeyValuePair<long, string> checkName in nameIDs)
                            {
                                Names.AddName(checkName.Key, checkName.Value);
                            }
                            Items.AddItem(newRow.ItemID, transEntry.SelectSingleNode("@typeName").Value);
                            #endregion
                        }
                        else
                        {
                            SortedList<long, string> nameIDs = new SortedList<long, string>();
                            // We've got a transaction that already exists in the database,
                            // update the row with additional data if available.
                            EMMADataSet.TransactionsRow newRow =
                                BuildTransRow(transID, transData, transEntry, walletID, nameIDs, true);
                            EMMADataSet.TransactionsRow oldRow = transData.FindByID(transID);
                            bool updateDone = false;

                            if (newRow.BuyerWalletID != oldRow.BuyerWalletID && newRow.BuyerWalletID != 0)
                            {
                                oldRow.BuyerWalletID = newRow.BuyerWalletID;
                                updateDone = true;
                            }
                            if (newRow.SellerWalletID != oldRow.SellerWalletID && newRow.SellerWalletID != 0)
                            {
                                oldRow.SellerWalletID = newRow.SellerWalletID;
                                updateDone = true;
                            }
                            // If a corp sells somthing to another corp (or itself) then we will get into
                            // the position of having the other party set as a character when in fact
                            // it is that character's corp.
                            // We check for this here and correct it if required.
                            if (oldRow.BuyerID == _charID && newRow.BuyerID == _corpID)
                            {
                                oldRow.BuyerID = newRow.BuyerID;
                                oldRow.BuyerCharacterID = newRow.BuyerCharacterID;
                                oldRow.BuyerWalletID = newRow.BuyerWalletID;
                                oldRow.BuyerForCorp = newRow.BuyerForCorp;
                                updateDone = true;
                            }
                            if (oldRow.SellerID == _charID && newRow.SellerID == _corpID)
                            {
                                oldRow.SellerID = newRow.SellerID;
                                oldRow.SellerCharacterID = newRow.SellerCharacterID;
                                oldRow.SellerWalletID = newRow.SellerWalletID;
                                oldRow.SellerForCorp = newRow.SellerForCorp;
                                updateDone = true;
                            }

                            if (updateDone)
                            {
                                updated++;
                            }
                        }
                        //}

                        batchPrg++;
                        UpdateStatus(batchPrg, transEntries.Count, "", "", false);
                    }
                }

                if (highestID > highestIDSoFar)
                {
                    SetHighestID(corc, APIDataType.Transactions, highestID);
                }

                UpdateStatus(0, 0, retVal + " transactions added to database.", "", false);
                UpdateStatus(0, 0, updated + " transactions updated.", "", false);

                if (transData.Count > 0)
                {
                    Transactions.Store(transData);

                    UpdateStatus(1, 1, "", "Complete", true);
                }

            }
            catch (Exception ex)
            {
                EMMAException emmaEx = ex as EMMAException;
                if (emmaEx == null)
                {
                    // If we've caught a standard exception rather than an EMMA one then log it be creating a
                    // new exception.
                    // Note that we don't need to actually throw it..
                    emmaEx = new EMMAException(ExceptionSeverity.Error, "Error when adding transactions", ex);
                }

                SetLastAPIUpdateError(corc, APIDataType.Transactions, ex.Message);
                UpdateStatus(-1, 0, "Error", ex.Message, true);
            }

            if (UpdateEvent != null)
            {
                UpdateEvent(this, new APIUpdateEventArgs(APIDataType.Transactions,
                    corc == CharOrCorp.Char ? _charID : _corpID,
                    APIUpdateEventType.UpdateCompleted));
            }

            return retVal;
        }
        private static void GetItemTransData(List<FinanceAccessParams> accessParams, List<int> itemIDs,
            List<long> regionIDs, List<long> stationIDs, DateTime startDate, DateTime endDate,
            long quantity, long recentBuyUnitsToIgnore,
            ref decimal avgSellPrice, ref decimal medianSellPrice, ref decimal avgBuyPrice,
            ref decimal medianBuyPrice, ref long unitsBought, ref long unitsSold,
            ref decimal brokerBuyFees, ref decimal brokerSellFees, ref decimal transactionTax,
            ref decimal transportCosts, ref decimal avgSellProfit,
            bool calcBrokerFees, bool calcTransTax, bool getBuyData, bool getSellData, bool getMedians,
            bool calcTransportCosts, bool useReprocessData, bool useMostRecentBuyPrice,
            bool restrictedCostCalc)
        {
            long totBuy = 0, totSell = 0;
            decimal totIskBuy = 0, totIskSell = 0, totIskSellProfit = 0;
            brokerBuyFees = 0;
            brokerSellFees = 0;
            transactionTax = 0;
            transportCosts = 0;
            // Used for working out transport costs for sell transactions.
            Dictionary<long, Dictionary<int, long>> quantities = new Dictionary<long, Dictionary<int, long>>();
            long quantityRemaining = quantity;
            bool ignoreQuantity = quantity == 0;
            // These map from a char ID to a value from 1-5 that is the character's skill level in that skill.
            Dictionary<long, int> brokerRelations = new Dictionary<long, int>();
            Dictionary<long, int> accounting = new Dictionary<long, int>();

            SortedList<decimal, long> priceFrequencies = new SortedList<decimal, long>();

            startDate = startDate.ToUniversalTime();
            endDate = endDate.ToUniversalTime();

            Diagnostics.ResetTimer("Transactions.GetBuyTrans");
            Diagnostics.ResetTimer("Transactions.ProcessBuyTrans");
            Diagnostics.ResetTimer("Transactions.CalcBuyBrokerFees");
            Diagnostics.ResetTimer("Transactions.GetBkrRelLvl");
            Diagnostics.ResetTimer("Transactions.CalcBuyBrokerFeesGetOrder");
            Diagnostics.ResetTimer("Transactions.GetStanding");
            Diagnostics.ResetTimer("Transactions.CalculateBuyBkr");
            Diagnostics.ResetTimer("Transactions.CalcBuyMedian");
            Diagnostics.ResetTimer("Transactions.GetSellTrans");
            Diagnostics.ResetTimer("Transactions.ProcessSellTrans");
            Diagnostics.ResetTimer("Transactions.CalcSellBrokerFees");
            Diagnostics.ResetTimer("Transactions.CalcSellTransTax");
            Diagnostics.ResetTimer("Transactions.CalcSellTransportCosts");
            Diagnostics.ResetTimer("Transactions.CalcSellMedian");

            EMMADataSet.TransactionsDataTable transactions = new EMMADataSet.TransactionsDataTable();
            if (getBuyData)
            {
                Diagnostics.StartTimer("Transactions.GetBuyTrans");
                // Retrieve buy transactions that match our criteria
                transactions = GetTransData(accessParams, itemIDs, regionIDs, stationIDs, startDate, endDate, "Buy");
                transactions.OrderByDescending(t => t.DateTime);
                Diagnostics.StopTimer("Transactions.GetBuyTrans");
                ReprocessResultList reprocessResults = new ReprocessResultList();
                if (itemIDs.Count == 1 && useReprocessData)
                {
                    reprocessResults = ReprocessJobs.GetItemResults(itemIDs[0], UserAccount.CurrentGroup.ID);
                }

                priceFrequencies = new SortedList<decimal, long>();

                Diagnostics.StartTimer("Transactions.ProcessBuyTrans");
                int transIndex = -1;
                int reprocIndex = (reprocessResults.Count == 0 ? -2 : -1);
                bool useReproc = false;
                bool useTrans = true;
                for (int i = 0; i < transactions.Count + reprocessResults.Count; i++)
                {
                    decimal currentUnitPrice = 0.0m;
                    long currentQuantity = 0;

                    if (reprocIndex != -2)
                    {
                        DateTime nextTransDate = DateTime.MinValue;
                        DateTime nextReprocDate = DateTime.MinValue;
                        if (transactions.Count > transIndex + 1)
                        {
                            nextTransDate = transactions[transIndex + 1].DateTime;
                        }
                        if (reprocessResults.Count > reprocIndex + 1)
                        {
                            nextReprocDate = reprocessResults[reprocIndex + 1].JobDate;
                        }
                        if (nextTransDate.CompareTo(nextReprocDate) < 0)
                        {
                            useReproc = true;
                            useTrans = false;
                            reprocIndex++;
                        }
                        else
                        {
                            useReproc = false;
                            useTrans = true;
                            transIndex++;
                        }
                    }
                    else
                    {
                        transIndex++;
                    }

                    bool includeTrans = true;
                    if (useTrans)
                    {
                        EMMADataSet.TransactionsRow trans = transactions[transIndex];
                        currentQuantity = trans.Quantity;
                        currentUnitPrice = trans.Price;
                    }
                    else if (useReproc)
                    {
                        ReprocessResult result = reprocessResults[reprocIndex];
                        currentQuantity = result.Quantity;
                        currentUnitPrice = result.EffectiveBuyPrice / currentQuantity;
                    }

                    if (recentBuyUnitsToIgnore > 0)
                    {
                        // If we're ignoring the first x units then first reduce the quantity we have
                        // to ignore by the quantity of the current transaction
                        int quantityToUse = (int)(recentBuyUnitsToIgnore < currentQuantity ?
                            recentBuyUnitsToIgnore : currentQuantity);
                        recentBuyUnitsToIgnore -= quantityToUse;
                        if (recentBuyUnitsToIgnore == 0)
                        {
                            // If the current transaction has a greater quantity than we are ignoring
                            // then reduce the quantity on the transaction by whatever we have left to ignore
                            currentQuantity -= quantityToUse;
                        }
                        else
                        {
                            // otherwise, just move to the next transaction.
                            includeTrans = false;
                        }
                    }

                    if (includeTrans)
                    {
                        int quantityToUse = 1;

                        if (quantityRemaining > 0 || ignoreQuantity)
                        {
                            quantityToUse = (int)(ignoreQuantity ? currentQuantity :
                                (quantityRemaining < currentQuantity ? quantityRemaining : currentQuantity));

                            // Increase total buy units and total isk on buy transactions by
                            // the appropriate amounts.
                            totBuy += quantityToUse;
                            decimal transTot = currentUnitPrice * quantityToUse;
                            totIskBuy += transTot;

                            if (getMedians)
                            {
                                if (priceFrequencies.ContainsKey(currentUnitPrice))
                                {
                                    priceFrequencies[currentUnitPrice] = priceFrequencies[currentUnitPrice] + quantityToUse;
                                }
                                else
                                {
                                    priceFrequencies.Add(currentUnitPrice, quantityToUse);
                                }
                            }

                            #region Calculate broker fees
                            if (calcBrokerFees && useTrans)
                            {
                                Diagnostics.StartTimer("Transactions.CalcBuyBrokerFees");
                                Order buyOrder = null, empty = null;

                                Diagnostics.StartTimer("Transactions.CalcBuyBrokerFeesGetOrder");
                                EMMADataSet.TransactionsRow trans = transactions[transIndex];
                                Orders.GetOrder(new Transaction(trans), out buyOrder, out empty);
                                Diagnostics.StopTimer("Transactions.CalcBuyBrokerFeesGetOrder");
                                if (buyOrder != null)
                                {
                                    Diagnostics.StartTimer("Transactions.GetBkrRelLvl");
                                    long id = trans.BuyerForCorp ? trans.BuyerCharacterID : trans.BuyerID;
                                    int bkrrellvl = 0;
                                    decimal corpStanding = 0;
                                    decimal factionStanding = 0;

                                    if (brokerRelations.ContainsKey(id))
                                    {
                                        bkrrellvl = brokerRelations[id];
                                    }
                                    else
                                    {
                                        bool corpID = false;
                                        bkrrellvl = UserAccount.CurrentGroup.GetCharacter(id, ref corpID).BrokerRelationsLvl;
                                        brokerRelations.Add(id, bkrrellvl);
                                    }
                                    Diagnostics.StopTimer("Transactions.GetBkrRelLvl");

                                    Diagnostics.StartTimer("Transactions.GetStanding");
                                    EveDataSet.staStationsRow station = Stations.GetStation(buyOrder.StationID);
                                    if (station != null && !station.IscorporationIDNull())
                                    {
                                        int stationCorp = station.corporationID;
                                        EveDataSet.crpNPCCorporationsRow npcCorp =
                                            NPCCorps.GetCorp(stationCorp);
                                        if (npcCorp != null)
                                        {
                                            factionStanding = Standings.GetStanding(trans.BuyerID,
                                                npcCorp.factionID);
                                        }
                                        corpStanding = Standings.GetStanding(trans.BuyerID, stationCorp);
                                    }
                                    Diagnostics.StopTimer("Transactions.GetStanding");

                                    Diagnostics.StartTimer("Transactions.CalculateBuyBkr");
                                    decimal fee = transTot * (decimal)(1 /
                                        Math.Exp((double)(0.1m * factionStanding + 0.04m * corpStanding)) *
                                        1 - (0.05 * bkrrellvl)) / 100.0m;
                                    Diagnostics.StopTimer("Transactions.CalculateBuyBkr");

                                    brokerBuyFees += fee;
                                }

                                Diagnostics.StopTimer("Transactions.CalcBuyBrokerFees");
                            }
                            #endregion
                        }
                        else
                        {
                            // if we're only retrieving data for x units and have already got that
                            // many then just jump out of the loop.
                            i = transactions.Count + reprocessResults.Count;
                        }

                        quantityRemaining -= quantityToUse;
                    }
                }
                Diagnostics.StopTimer("Transactions.ProcessBuyTrans");

                #region Calculate median buy price
                if (getMedians && priceFrequencies.Count > 0)
                {
                    Diagnostics.StartTimer("Transactions.CalcBuyMedian");

                    float tmp = (totBuy + 1) / 2;
                    long limit = (long)Math.Round(tmp, MidpointRounding.AwayFromZero);
                    if (limit != tmp) { limit -= 1; }
                    int index = 0;
                    long quantitySoFar = 0;

                    while (quantitySoFar < limit)
                    {
                        quantitySoFar += priceFrequencies[priceFrequencies.Keys[index]];
                        index++;
                    }

                    if (limit == 0)
                    {
                        medianSellPrice = priceFrequencies.Keys[0];
                    }
                    else if (quantitySoFar > limit || limit == tmp)
                    {
                        medianBuyPrice = priceFrequencies.Keys[index - 1];
                    }
                    else
                    {
                        medianBuyPrice = (priceFrequencies.Keys[index - 1] + priceFrequencies[index]) / 2;
                    }

                    /*decimal[] priceArray = prices.ToArray();
                    Array.Sort<decimal>(priceArray);

                    int rem = 0;
                    int result = Math.DivRem(priceArray.Length, 2, out rem);
                    decimal median = 0;
                    if (rem == 0)
                    {
                        median = (priceArray[result - 1] + priceArray[result]) / 2;
                    }
                    else
                    {
                        median = priceArray[result];
                    }
                    medianBuyPrice = median;*/
                    Diagnostics.StopTimer("Transactions.CalcBuyMedian");
                }
                #endregion
            }

            if (getSellData)
            {
                Diagnostics.StartTimer("Transactions.GetSellTrans");
                // Retrieve sell transactions that match our criteria.
                transactions = GetTransData(accessParams, itemIDs, regionIDs, stationIDs, startDate, endDate, "Sell");
                Diagnostics.StopTimer("Transactions.GetSellTrans");

                priceFrequencies = new SortedList<decimal, long>();

                quantityRemaining = quantity;
                Diagnostics.StartTimer("Transactions.ProcessSellTrans");
                for (int i = 0; i < transactions.Count; i++)
                {
                    EMMADataSet.TransactionsRow trans = transactions[i];
                    int quantityToUse = 1;

                    if (quantityRemaining > 0 || ignoreQuantity)
                    {
                        quantityToUse = (int)(ignoreQuantity ? trans.Quantity :
                            (quantityRemaining < trans.Quantity ? quantityRemaining : trans.Quantity));

                        // Increase total sell units and total isk on sell transactions by
                        // the appropriate amounts.
                        totSell += quantityToUse;
                        decimal transTot = trans.Price * quantityToUse;
                        totIskSell += transTot;
                        if (trans.SellerUnitProfit == 0)
                        {
                            // If the seller unit profit has not been recorded for some reason then
                            // we need to calculate it.
                            Transaction t = new Transaction(trans);
                            totIskSellProfit += t.GrossUnitProfit * quantityToUse;
                        }
                        else { totIskSellProfit += trans.SellerUnitProfit * quantityToUse; }

                        if (getMedians)
                        {
                            decimal price = trans.Price;
                            if (priceFrequencies.ContainsKey(price))
                            {
                                priceFrequencies[price] = priceFrequencies[price] + quantityToUse;
                            }
                            else
                            {
                                priceFrequencies.Add(price, quantityToUse);
                            }
                        }

                        #region Calculate broker fees
                        if (calcBrokerFees)
                        {
                            // Only add the broker fee if we've got a matching journal record.
                            // If we don't it was probably a quick buy (i.e. no broker fee)
                            Order sellOrder = null, blank = null;
                            Orders.GetOrder(new Transaction(trans), out blank, out sellOrder);
                            if (sellOrder != null)
                            {
                                Diagnostics.StartTimer("Transactions.CalcSellBrokerFees");
                                long id = trans.SellerForCorp ? trans.SellerCharacterID : trans.SellerID;
                                int bkrrellvl = 0;
                                decimal factionStanding = 0, corpStanding = 0;

                                if (brokerRelations.ContainsKey(id))
                                {
                                    bkrrellvl = brokerRelations[id];
                                }
                                else
                                {
                                    bool corpID = false;
                                    bkrrellvl = UserAccount.CurrentGroup.GetCharacter(id, ref corpID).BrokerRelationsLvl;
                                    brokerRelations.Add(id, bkrrellvl);
                                }

                                EveDataSet.staStationsRow station = Stations.GetStation(trans.StationID);
                                if (station != null && !station.IscorporationIDNull())
                                {
                                    int stationCorp = station.corporationID;
                                    EveDataSet.crpNPCCorporationsRow npcCorp =
                                        NPCCorps.GetCorp(stationCorp);
                                    if (npcCorp != null)
                                    {
                                        factionStanding = Standings.GetStanding(trans.BuyerID,
                                            npcCorp.factionID);
                                    }
                                    corpStanding = Standings.GetStanding(trans.SellerID, stationCorp);
                                }

                                decimal fee = transTot * (decimal)(1 /
                                    Math.Exp((double)(0.1m * factionStanding + 0.04m * corpStanding)) *
                                    1 - (0.05 * bkrrellvl)) / 100.0m;

                                brokerSellFees += fee;
                            }
                            Diagnostics.StopTimer("Transactions.CalcSellBrokerFees");
                        }
                        #endregion
                        #region Calculate transaction tax
                        if (calcTransTax)
                        {
                            Diagnostics.StartTimer("Transactions.CalcSellTransTax");
                            long id = trans.SellerForCorp ? trans.SellerCharacterID : trans.SellerID;
                            int acclvl = 0;
                            if (accounting.ContainsKey(id))
                            {
                                acclvl = accounting[id];
                            }
                            else
                            {
                                bool corpID = false;
                                acclvl = UserAccount.CurrentGroup.GetCharacter(id, ref corpID).AccountingLvl;
                                accounting.Add(id, acclvl);
                            }
                            transactionTax += transTot * (decimal)(0.01 - 0.001 * acclvl);
                            Diagnostics.StopTimer("Transactions.CalcSellTransTax");
                        }
                        #endregion
                        #region Record quantities for working out transport costs.
                        Dictionary<int, long> itemQuantities;
                        if (quantities.ContainsKey(trans.StationID))
                        {
                            itemQuantities = quantities[trans.StationID];
                        }
                        else
                        {
                            itemQuantities = new Dictionary<int, long>();
                            quantities.Add(trans.StationID, itemQuantities);
                        }

                        if (itemQuantities.ContainsKey(trans.ItemID))
                        {
                            long qSoFar = itemQuantities[trans.ItemID];
                            qSoFar += trans.Quantity;
                            itemQuantities[trans.ItemID] = qSoFar;
                        }
                        else
                        {
                            itemQuantities.Add(trans.ItemID, trans.Quantity);
                        }
                        #endregion
                    }
                    else
                    {
                        // if we're only retrieving data for x units and have already got that
                        // many then just jump out of the loop.
                        i = transactions.Count;
                    }

                    quantityRemaining -= quantityToUse;
                }

                if ((useMostRecentBuyPrice || restrictedCostCalc) && totSell > 0)
                {
                    decimal totIsk = 0;
                    long q = 0;
                    transactions = GetTransData(accessParams, itemIDs, regionIDs, stationIDs, startDate, endDate, "Buy");
                    OrderedEnumerableRowCollection<EMMADataSet.TransactionsRow> orderedTransactions = null;
                    if (useMostRecentBuyPrice)
                    {
                        orderedTransactions = transactions.OrderByDescending(t => t.DateTime);
                    }
                    else if (restrictedCostCalc)
                    {
                        orderedTransactions = transactions.OrderBy(t => t.DateTime);
                    }

                    foreach(EMMADataSet.TransactionsRow trans in orderedTransactions)
                    {
                        //EMMADataSet.TransactionsRow trans = transactions[i];
                        long qToUse = (q + trans.Quantity > totSell) ? (totSell - q) : trans.Quantity;
                        q += qToUse;
                        totIsk += trans.Price * qToUse;
                        if (q >= totSell)
                        {
                            break;
                        }
                    }
                    if (q > 0)
                    {
                        totIskSellProfit = ((totIskSell / totSell) - (totIsk / q)) * totSell;
                    }
                }
                Diagnostics.StopTimer("Transactions.ProcessSellTrans");

            }

            #region Calculate transport costs
            if (calcTransportCosts)
            {
                Diagnostics.StartTimer("Transactions.CalcSellTransportCosts");
                Dictionary<long, Dictionary<int, long>>.Enumerator enumerator = quantities.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    Dictionary<int, long>.Enumerator enumerator2 = enumerator.Current.Value.GetEnumerator();
                    while (enumerator2.MoveNext())
                    {
                        // Get cost of contracts using this item occuring up to 14 days before
                        // the beginning of the report.
                        transportCosts += Contracts.GetTransportCosts(enumerator2.Current.Key,
                            enumerator.Current.Key, enumerator2.Current.Value, startDate.AddDays(-14));
                    }
                }
                Diagnostics.StopTimer("Transactions.CalcSellTransportCosts");
            }
            #endregion
            #region Calculate median sell price
            if (getMedians && priceFrequencies.Count > 0)
            {
                Diagnostics.StartTimer("Transactions.CalcSellMedian");

                float tmp = (totSell + 1) / 2;
                long limit = (long)Math.Round(tmp, MidpointRounding.AwayFromZero);
                if (limit != tmp) { limit -= 1; }
                int index = 0;
                long quantitySoFar = 0;

                while (quantitySoFar < limit)
                {
                    quantitySoFar += priceFrequencies[priceFrequencies.Keys[index]];
                    index++;
                }

                if (limit == 0)
                {
                    medianSellPrice = priceFrequencies.Keys[0];
                }
                else if (quantitySoFar > limit || limit == tmp)
                {
                    medianSellPrice = priceFrequencies.Keys[index - 1];
                }
                else
                {
                    medianSellPrice = (priceFrequencies.Keys[index - 1] + priceFrequencies[index]) / 2;
                }

                /*decimal[] priceArray = prices.ToArray();
                Array.Sort<decimal>(priceArray);

                int rem = 0;
                int result = Math.DivRem(priceArray.Length, 2, out rem);
                decimal median = 0;
                if (rem == 0)
                {
                    median = (priceArray[result - 1] + priceArray[result]) / 2;
                }
                else
                {
                    median = priceArray[result];
                }
                medianSellPrice = median;*/
                Diagnostics.StopTimer("Transactions.CalcSellMedian");
            }
            #endregion

            // Set values for return parameters. (note, broker fees and transaction tax are already set for us.)
            avgSellPrice = (totSell == 0 ? 0 : totIskSell / totSell);
            avgBuyPrice = (totBuy == 0 ? 0 : totIskBuy / totBuy);
            avgSellProfit = (totSell == 0 ? 0 : totIskSellProfit / totSell);
            unitsBought = totBuy;
            unitsSold = totSell;
        }
        public void LoadOldEmmaXML(string filename, long charID, long corpID)
        {
            EMMADataSet.TransactionsDataTable table = new EMMADataSet.TransactionsDataTable();
            XmlDocument xml = new XmlDocument();
            //UpdateStatus(0, 0, "", "Loading file", false);
            xml.Load(filename);

            XmlNodeList nodes = xml.SelectNodes("/DocumentElement/Transactions");

            int counter = 0;
            UpdateStatus(0, 0, "", "Extracting data from XML", false);
            foreach (XmlNode node in nodes)
            {
                long transID = long.Parse(node.SelectSingleNode("ID").FirstChild.Value);

                if (!Transactions.TransactionExists(table, transID) &&
                    table.FindByID(transID) == null)
                {
                    // Actually create the line and add it to the data table
                    EMMADataSet.TransactionsRow newRow = BuildTransRow(transID, table, node, corpID, charID);

                    table.AddTransactionsRow(newRow);
                }
                else
                {
                    // We've got a transaction that already exists in the database,
                    // update the row with additional data if available.
                    EMMADataSet.TransactionsRow newRow = BuildTransRow(transID, table, node, corpID, charID);
                    EMMADataSet.TransactionsRow oldRow = table.FindByID(transID);

                    //if (newRow.BuyerWalletID != oldRow.BuyerWalletID && newRow.BuyerWalletID != 0)
                    //{
                    //    oldRow.BuyerWalletID = newRow.BuyerWalletID;
                    //}
                    //if (newRow.SellerWalletID != oldRow.SellerWalletID && newRow.SellerWalletID != 0)
                    //{
                    //    oldRow.SellerWalletID = newRow.SellerWalletID;
                    //}
                    // If a corp sells somthing to another corp (or itself) then we will get into
                    // the position of having the other party set as a character when in fact
                    // it is that character's corp.
                    // We check for this here and correct it if required.

                    // Change to just always update the database with the data from the import.
                    //if (oldRow.BuyerID == charID && newRow.BuyerID == corpID)
                    //{
                        oldRow.BuyerID = newRow.BuyerID;
                        oldRow.BuyerCharacterID = newRow.BuyerCharacterID;
                        oldRow.BuyerWalletID = newRow.BuyerWalletID;
                        oldRow.BuyerForCorp = newRow.BuyerForCorp;
                    //}
                    //if (oldRow.SellerID == charID && newRow.SellerID == corpID)
                    //{
                        oldRow.SellerID = newRow.SellerID;
                        oldRow.SellerCharacterID = newRow.SellerCharacterID;
                        oldRow.SellerWalletID = newRow.SellerWalletID;
                        oldRow.SellerForCorp = newRow.SellerForCorp;
                    //}

                        oldRow.DateTime = newRow.DateTime;
                        oldRow.Price = newRow.Price;
                        oldRow.Quantity = newRow.Quantity;
                        oldRow.ItemID = newRow.ItemID;
                        oldRow.StationID = newRow.StationID;
                }
                counter++;
                UpdateStatus(counter, nodes.Count, "", "", false);

                // If we've got 1000 rows then update the database and move on to the next batch.
                if (table.Count >= 1000)
                {
                    UpdateStatus(0, 0, "", "Updating database", false);
                    lock (tableAdapter)
                    {
                        tableAdapter.Update(table);
                        table.Clear();
                    }
                }
            }

            UpdateStatus(0, 0, "", "Updating database", false);
            lock (tableAdapter)
            {
                tableAdapter.Update(table);
            }
        }
        /// <summary>
        /// Get a list containing all transactions that meet the specified criteria.
        /// </summary>
        /// <param name="accessParams"></param>
        /// <param name="itemIDs"></param>
        /// <param name="stationIDs"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public static TransactionList LoadTransactions(List<FinanceAccessParams> accessParams, List<int> itemIDs,
            List<long> stationIDs, DateTime startDate, DateTime endDate, string type)
        {
            TransactionList retVal = new TransactionList();

            //---------------------------------------------------------------------------------------------------

            EMMADataSet.TransactionsDataTable table = new EMMADataSet.TransactionsDataTable();
            table = GetTransData(accessParams, itemIDs, new List<long>(), stationIDs, startDate, endDate, type);

            foreach (EMMADataSet.TransactionsRow row in table)
            {
                Transaction trans = new Transaction(row);
                retVal.Add(trans);
            }

            //---------------------------------------------------------------------------------------------------
            // This was an attempt to speed up loading of data by only loading IDs initally and
            // then retrieving other information as required.
            // It was not much faster on the inital load and gave worse stuttering during operation
            // so it's no longer used. Instead we simply limit the inital data retrieval on
            // the view form to the last week's worth of data.
            //---------------------------------------------------------------------------------------------------

            /*string itemString = "";
            string stationString = "";
            foreach (int item in itemIDs) { itemString = itemString + (itemString.Length == 0 ? "" : ",") + item; }
            foreach (int station in stationIDs) { stationString = stationString + (stationString.Length == 0 ? "" : ",") + station; }

            SqlConnection connection = new SqlConnection(Properties.Settings.Default.EMMA_DatabaseConnectionString);
            SqlDataAdapter adapter = null;
            SqlCommand command = null;
            connection.Open();

            command = new SqlCommand("TransCountByItemAndLoc", connection);
            command.CommandType = System.Data.CommandType.StoredProcedure;
            command.Parameters.Add(new SqlParameter("@accessParams", FinanceAccessParams.BuildAccessList(accessParams)));
            command.Parameters.Add(new SqlParameter("@itemIDs", itemString));
            command.Parameters.Add(new SqlParameter("@stationIDs", stationString));
            command.Parameters.Add(new SqlParameter("@regionIDs", ""));
            command.Parameters.Add(new SqlParameter("@startDate", SqlDateTime.MinValue.Value));
            command.Parameters.Add(new SqlParameter("@endDate", SqlDateTime.MaxValue.Value));
            command.Parameters.Add(new SqlParameter("@transType", ""));
            adapter = new SqlDataAdapter(command);

            // lock on the transactions table adapter, even though we're not actually using it, we're still
            // accessing the same database table.
            lock (tableAdapter)
            {
                SqlDataReader reader = adapter.SelectCommand.ExecuteReader();
                try
                {
                    while (reader.Read())
                    {
                        Transaction trans = new Transaction(reader.GetInt64(0));
                        retVal.Add(trans);
                    }
                }
                finally
                {
                    reader.Close();
                }
            }*/

            //---------------------------------------------------------------------------------------------------

            return retVal;
        }
        /// <summary>
        /// Retrieves transaction data from the database.
        /// The parameters supplied are used to determine the correct stored procedure to use
        /// to give us the best performance possible.
        /// </summary>
        /// <param name="accessList"></param>
        /// <param name="itemIDs"></param>
        /// <param name="regionIDs"></param>
        /// <param name="stationIDs"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <param name="type">If value is 'buy' or 'sell' then only the relevant transactions are returned. Any other values returns both buy and sell transactions</param>
        /// <returns></returns>
        public static EMMADataSet.TransactionsDataTable GetTransData(List<FinanceAccessParams> accessList,
            List<int> itemIDs, List<long> regionIDs, List<long> stationIDs,
            DateTime startDate, DateTime endDate, string type)
        {
            EMMADataSet.TransactionsDataTable retVal = new EMMADataSet.TransactionsDataTable();
            long charID = 0;
            List<short> walletIDs = new List<short>();

            // Make sure start/end dates are within the allowed ranges
            startDate = startDate.ToUniversalTime();
            endDate = endDate.ToUniversalTime();
            if (startDate.CompareTo(SqlDateTime.MinValue.Value) < 0) startDate = SqlDateTime.MinValue.Value;
            if (endDate.CompareTo(SqlDateTime.MinValue.Value) < 0) endDate = SqlDateTime.MinValue.Value;
            if (startDate.CompareTo(SqlDateTime.MaxValue.Value) > 0) startDate = SqlDateTime.MaxValue.Value;
            if (endDate.CompareTo(SqlDateTime.MaxValue.Value) > 0) endDate = SqlDateTime.MaxValue.Value;

            if (itemIDs == null) { itemIDs = new List<int>(); }
            if (regionIDs == null) { regionIDs = new List<long>(); }
            if (stationIDs == null) { stationIDs = new List<long>(); }

            if (itemIDs.Count == 0) { itemIDs.Add(0); }
            if (regionIDs.Count == 0) { regionIDs.Add(0); }
            if (stationIDs.Count == 0) { stationIDs.Add(0); }

            if (accessList.Count == 1)
            {
                charID = accessList[0].OwnerID;
                bool includeCorporate = accessList[0].IncludeCorporate;
                foreach (short wallet in accessList[0].WalletIDs)
                {
                    if (walletIDs.Count == 0 && wallet != 0) { walletIDs = accessList[0].WalletIDs; }
                }

                if (walletIDs.Count == 0 && itemIDs.Count == 1 && regionIDs.Count == 1 && stationIDs.Count == 1)
                {
                    lock (tableAdapter)
                    {
                        // Retrieve transactions for a single character (or corp) and only one
                        // of each item, station and region paramter. (note these could be the '0' paramter
                        // that will include anything.)
                        // e.g. getting all transactions for a single character involving tritanium
                        // at any location.
                        tableAdapter.FillByAnySingle(retVal, charID, includeCorporate, itemIDs[0], stationIDs[0],
                            regionIDs[0], startDate, endDate, type);
                    }
                }
                else if (itemIDs.Count == 1 && regionIDs.Count == 1 && stationIDs.Count == 1)
                {
                    short w1 = accessList[0].WalletIDs[0], w2 = accessList[0].WalletIDs[1],
                        w3 = accessList[0].WalletIDs[2], w4 = accessList[0].WalletIDs[3],
                        w5 = accessList[0].WalletIDs[4], w6 = accessList[0].WalletIDs[5];
                    lock (tableAdapter)
                    {
                        // Retrieve transactions for a single corporation, specific wallets and only one
                        // of each item, station and region paramter
                        // e.g. getting all transactions in wallet 3 and 4 of specified corp involving
                        // tritanium at any location.
                        tableAdapter.FillBySingleAndWallets(retVal, charID, w1, w2, w3, w4, w5, w6, itemIDs[0],
                            stationIDs[0], regionIDs[0], startDate, endDate, type);
                    }
                }
            }
            else
            {
                if (itemIDs.Count == 1 && regionIDs.Count == 1 && stationIDs.Count == 1)
                {
                    lock (tableAdapter)
                    {
                        // Retrieve transactions for multiple characters and/or corporations where only one
                        // of each item, station and region paramter.
                        tableAdapter.FillByOwnersAndSingle(retVal, FinanceAccessParams.BuildAccessList(accessList),
                            itemIDs[0], stationIDs[0], regionIDs[0], startDate, endDate, type);
                    }
                }
                else
                {
                    string itemString = "";
                    string stationString = "";
                    string regionString = "";
                    foreach (int item in itemIDs) { itemString = itemString + (itemString.Length == 0 ? "" : ",") + item; }
                    foreach (int station in stationIDs) { stationString = stationString + (stationString.Length == 0 ? "" : ",") + station; }
                    foreach (int region in regionIDs) { regionString = regionString + (regionString.Length == 0 ? "" : ",") + region; }

                    lock (tableAdapter)
                    {
                        // Retrieve transactions for multiple characters and/or corporations where multiple
                        // items, stations or regions are specified.
                        tableAdapter.FillByAny(retVal, FinanceAccessParams.BuildAccessList(accessList), itemString,
                             stationString, regionString, startDate, endDate, type);
                    }
                }
            }

            return retVal;
        }
        public static EMMADataSet.TransactionsDataTable GetTransData(long ownerID, bool includeCorporate,
            int itemID, long regionID, long stationID, long minTransID, string type)
        {
            EMMADataSet.TransactionsDataTable retVal = new EMMADataSet.TransactionsDataTable();

            lock (tableAdapter)
            {
                tableAdapter.FillByAnySingleAndID(retVal, ownerID, includeCorporate, itemID, stationID,
                    regionID, minTransID, type);
            }
            return retVal;
        }
 /// <summary>
 /// Get the specified transaction
 /// </summary>
 /// <param name="id"></param>
 /// <returns></returns>
 public static EMMADataSet.TransactionsRow GetTransaction(long id)
 {
     EMMADataSet.TransactionsRow retVal = null;
     EMMADataSet.TransactionsDataTable table = new EMMADataSet.TransactionsDataTable();
     lock (tableAdapter)
     {
         tableAdapter.FillByID(table, id);
     }
     if (table.Count > 0) retVal = table[0];
     return retVal;
 }
        public static bool GetResultsPage(int startPos, int pageSize, ref TransactionList transactions)
        {
            if (startPos <= 0) startPos = 1;
            EMMADataSet.TransactionsDataTable table = new EMMADataSet.TransactionsDataTable();
            lock (tableAdapter)
            {
                tableAdapter.FillByResultsPage(table, startPos, pageSize);
            }
            foreach (EMMADataSet.TransactionsRow transaction in table)
            {
                transactions.Add(new Transaction(transaction));
            }

            return table.Count == pageSize;
        }
 public static void DeleteTransaction(long id)
 {
     EMMADataSet.TransactionsDataTable table = new EMMADataSet.TransactionsDataTable();
     lock (tableAdapter)
     {
         tableAdapter.FillByID(table, id);
         if (table.Count > 0) { table[0].Delete(); }
         tableAdapter.Update(table);
     }
 }
Esempio n. 11
0
        /// <summary>
        /// Get a list of changes that would be made to assets by the transactions meeting the 
        /// specified criteria. (i.e. What would change if 'UpdateFromTransactions' was called 
        /// with the same parameters).
        /// If the return structure, the outer dictionary is indexed by stationID, the inner by 
        /// itemID. The contained value is the change to be made to the quantity of the asset.
        /// </summary>
        /// <param name="charID"></param>
        /// <param name="corpID"></param>
        /// <param name="useCorp"></param>
        /// <param name="minimumTransID"></param>
        /// <param name="includeTransAfter"></param>
        /// <returns></returns>
        public static Dictionary<long, Dictionary<int, long>> GetQuantityChangesFromTransactions(
            long charID, long corpID, bool useCorp, long minimumTransID, DateTime includeTransAfter)
        {
            Dictionary<long, Dictionary<int, long>> retVal = new Dictionary<long, Dictionary<int, long>>();
            long ownerID = useCorp ? corpID : charID;

            EMMADataSet.TransactionsDataTable transactions = new EMMADataSet.TransactionsDataTable();
            transactions = GetTransactions(charID, corpID, useCorp, minimumTransID, includeTransAfter);

            foreach (EMMADataSet.TransactionsRow trans in transactions)
            {
                long stationID = trans.StationID;
                int itemID = trans.ItemID;
                int deltaQuantity = trans.Quantity;
                if (trans.SellerID == ownerID) { deltaQuantity *= -1; }
                if (!retVal.ContainsKey(stationID))
                {
                    retVal.Add(stationID, new Dictionary<int, long>());
                }
                if (!retVal[stationID].ContainsKey(itemID))
                {
                    retVal[stationID].Add(itemID, 0);
                }
                retVal[stationID][itemID] += deltaQuantity;
            }

            return retVal;
        }
Esempio n. 12
0
        /// <summary>
        /// This is called after an import of XML asset data but before the changes are commited
        /// to the database.
        /// The aim is to try to work out which assets have simply moved location and which have 
        /// been added or lost. Additionally, if items have moved, update the cost of the item 
        /// stack appropriately.
        /// </summary>
        /// <param name="assetData">The datatable containing the changes that will be applied to the assets database</param>
        /// <param name="charID"></param>
        /// <param name="corp"></param>
        /// <param name="changes">Contains changes in quantities of item stacks that were in the XML.</param>
        /// <param name="gained">A list of items that have been gained from 'nowhere'</param>
        /// <param name="lost">A list of items that have been totally lost from the owner's asset data.</param>
        public static void AnalyseChanges(EMMADataSet.AssetsDataTable assetData, long ownerID,
            AssetList changes, out AssetList gained, out AssetList lost)
        {
            // Note that 'changes' will only include items that exist in the XML from the API. i.e. Any
            // stacks that have been completely removed from a location will not show up.
            // To get these removed assets, we need to retrieve any 'unprocessed' assets from the
            // assetData table.
            gained = new AssetList();
            lost = new AssetList();

            // Add any unprocessed asset stacks to the 'changes' collection.
            // Although the main data changes have not yet been supplied to the database,
            // the processed flags have been set for the relevant asset rows.
            // This means that we can get the list of unprocessed assets direct from
            // the database.
            List<AssetAccessParams> accessParams = new List<AssetAccessParams>();
            accessParams.Add(new AssetAccessParams(ownerID));
            EMMADataSet.AssetsDataTable unprocMatches = new EMMADataSet.AssetsDataTable();
            Assets.GetAssets(unprocMatches, accessParams, 0, 0, 0, 0, false);

            foreach (EMMADataSet.AssetsRow unprocMatch in unprocMatches)
            {
                Asset change = new Asset(unprocMatch);
                // Ignore any assets 'for sale via contract' or 'in transit'
                if (change.Quantity != 0 && change.StatusID != (int)AssetStatus.States.ForSaleViaContract &&
                    change.StatusID != (int)AssetStatus.States.InTransit)
                {
                    change.Quantity *= -1; // These assets are effectively missing so invert the quantity.
                    changes.Add(change);
                }
            }

            // Note 'changes2' is a list of the same items as 'changes'.
            // It is needed because the filter functionallity changes the list which we cannot
            // do within the main foreach loop.
            AssetList changes2 = new AssetList();
            foreach (Asset change in changes)
            {
                changes2.Add(change);
            }

            // See if any of our 'missing' assets match transactions that are marked to have thier profit
            // calculated during an assets update.
            #region Update transactions with CalcProfitFromAssets flag
            EMMADataSet.TransactionsDataTable transData = new EMMADataSet.TransactionsDataTable();
            Transactions.AddTransByCalcProfitFromAssets(transData,
                UserAccount.CurrentGroup.GetFinanceAccessParams(APIDataType.Transactions), 0, true);

            foreach (EMMADataSet.TransactionsRow trans in transData)
            {
                long quantityRemaining = trans.Quantity;
                decimal totalBuyPrice = 0;

                changes.ItemFilter = "ItemID = " + trans.ItemID + " AND Quantity < 0";
                foreach (Asset change in changes.FiltredItems)
                {
                    // If we get a match then use the missing items's cost to calculate the
                    // transaction's profit.
                    // Note that we don't need to adjust any item quantities since either the
                    // changes have already been made or the item stack is 'unprocessed' and will
                    // be cleared out anyway.
                    long deltaQ = Math.Min(Math.Abs(change.Quantity), quantityRemaining);
                    // Adjust the quantity of the 'missing' items.
                    change.Quantity += deltaQ;
                    quantityRemaining -= deltaQ;
                    // Because this transaction's asset quantity change will have been made
                    // back when the transaction was processed, we will have some unexpected
                    // added items as well.
                    //
                    // E.g. Consider station A and B. At A, there are 2 units of item X, at B
                    // there is nothing.
                    // At a later date, the player moves 2 of X from A to B and sells it.
                    // When the sell transaction is processed, station B will be set to -2
                    // units of X and the transaction will be set as 'CalcProfitFromAssets'.
                    // When the asset update occurs, it will show zero for both locations so
                    // they will be unprocessed.
                    // This will result in 2 unexplained units gained at station B and 2 lost
                    // at station A. (We've gone from A=2,B=-2 to A=0 B=0)
                    changes2.ItemFilter = "ItemID = " + trans.ItemID + " AND LocationID = " + trans.StationID +
                        " AND Status = 1";
                    if (changes2.FiltredItems.Count > 0)
                    {
                        // We've already accounted for the cost of items, etc so just reduce the
                        // quantity.
                        changes2[0].Quantity -= deltaQ;
                    }

                    totalBuyPrice += (change.UnitBuyPrice * deltaQ);

                    if (quantityRemaining != trans.Quantity)
                    {
                        // We've found enough missing items to match this transaction either completely
                        // or partially so calculate it's profit and set it as completed.
                        trans.CalcProfitFromAssets = false;
                        trans.SellerUnitProfit = trans.Price - (totalBuyPrice / trans.Quantity);
                    }
                }
            }
            /*
            foreach (Asset change in changes)
            {
                // Note: because we are changing quantity within the foreach loop
                // we don't use the filter on the AssetList.
                // Instead, just use this if condition each time around.
                if (change.Quantity < 0 && !noTransItemIDs.Contains(change.ItemID))
                {
                    // If we get a match then use the missing items's cost to calculate the
                    // transaction's profit.
                    // Note that we don't need to adjust any item quantities since either the
                    // changes have already been made or the item stack is 'unprocessed' and will
                    // be cleared out anyway.
                    Transactions.AddTransByCalcProfitFromAssets(transData,
                        UserAccount.CurrentGroup.GetFinanceAccessParams(APIDataType.Transactions),
                        change.ItemID, true);
                    if (transData.Count == 0) { noTransItemIDs.Add(change.ItemID); }
                    foreach (EMMADataSet.TransactionsRow trans in transData)
                    {
                        if (trans.ItemID == change.ItemID && !completedTrans.Contains(trans.ID))
                        {
                            long quantityRemaining = trans.Quantity;
                            TransProcessData data = new TransProcessData();
                            if (processData.ContainsKey(trans.ID))
                            {
                                data = processData[trans.ID];
                                quantityRemaining = data.QuantityRemaining;
                            }
                            else
                            {
                                data.QuantityMatched = 0;
                                data.QuantityRemaining = trans.Quantity;
                                data.TotalBuyPrice = 0;
                                processData.Add(trans.ID, data);
                            }

                            long deltaQ = Math.Min(Math.Abs(change.Quantity), quantityRemaining);
                            // Adjust the quantity of the 'missing' items.
                            change.Quantity += deltaQ;
                            // Because this transaction's asset quantity change will have been made
                            // back when the transaction was processed, we will have some unexpected
                            // added items as well.
                            //
                            // E.g. Consider station A and B. At A, there are 2 units of item X, at B
                            // there is nothing.
                            // At a later date, the player moves 2 of X from A to B and sells it.
                            // When the sell transaction is processed, station B will be set to -2
                            // units of X and the transaction will be set as 'CalcProfitFromAssets'.
                            // When the asset update occurs, it will show zero for both locations so
                            // they will be unprocessed.
                            // This will result in 2 unexplained units gained at station B and 2 lost
                            // at station A. (We've gone from A=2,B=-2 to A=0 B=0)
                            changes2.ItemFilter = "ItemID = " + trans.ItemID + " AND LocationID = " + trans.StationID;
                            foreach (Asset change2 in changes2)
                            {
                                if (change2.Quantity > 0)
                                {
                                    // We've already accounted for the cost of items, etc so just reduce the
                                    // quantity.
                                    change2.Quantity -= deltaQ;
                                }
                            }

                            data.QuantityRemaining = data.QuantityRemaining - deltaQ;
                            data.QuantityMatched = data.QuantityMatched + deltaQ;
                            data.TotalBuyPrice = data.TotalBuyPrice + (change.UnitBuyPrice * deltaQ);

                            if (data.QuantityRemaining == 0)
                            {
                                // We've found enough missing items to match this transaction completely
                                // so calculate it's profit and set it as completed.
                                trans.CalcProfitFromAssets = false;
                                trans.SellerUnitProfit = trans.Price - (data.TotalBuyPrice / data.QuantityMatched);
                                completedTrans.Add(trans.ID);
                                if (uncompletedTrans.Contains(trans.ID)) { uncompletedTrans.Remove(trans.ID); }
                            }
                            else
                            {
                                // We havn't found enough missing items to completely match this transaction
                                // yet so add to to the list of uncompleted transactions.
                                uncompletedTrans.Add(trans.ID);
                            }
                        }
                    }
                }
            }

            // Calculate profits as best we can for any 'uncompleted' transactions.
            // i.e. those that we did not have enough missing items to match completely.
            foreach (long transID in uncompletedTrans)
            {
                EMMADataSet.TransactionsRow trans = transData.FindByID(transID);
                if (trans != null && processData.ContainsKey(transID))
                {
                    TransProcessData data = processData[transID];
                    trans.CalcProfitFromAssets = false;
                    trans.SellerUnitProfit = trans.Price - (data.TotalBuyPrice / data.QuantityMatched);
                }
            }
             * */
            // Update transactions database
            Transactions.Store(transData);
            #endregion

            // Work through the list of changes.
            //
            // If items have been added then see if there is a matching quantity that has been removed
            // somwhere. If so, use the old asset stack cost to set the cost for the new asset stack.
            // If we can't find a match for any added items then add them to the 'gained' list.
            //
            // By definititon, any items that have been removed and match stacks added elsewhere will
            // be matched by the above process. Any removed items that are left over are added to the
            // 'lost' list of assets.
            changes.ItemFilter = "";

            // If we're in manufacturing mode then we don't want to try and match
            // lost assets with gained assets until after the user has selected
            // what has been built, etc.
            if (!UserAccount.Settings.ManufacturingMode)
            {
                #region Try and match gains in one place against losses in another.
                List<int> zeroLossItemIDs = new List<int>();
                foreach (Asset change in changes)
                {
                    if (change.Quantity > 0 && !zeroLossItemIDs.Contains(change.ItemID))
                    {
                        int itemID = change.ItemID;
                        long locationID = change.LocationID;

                        changes2.ItemFilter = "ItemID = " + itemID + " AND Quantity < 0";
                        if (changes2.FiltredItems.Count == 0)
                        {
                            if (!zeroLossItemIDs.Contains(change.ItemID)) { zeroLossItemIDs.Add(change.ItemID); }
                        }

                        foreach (Asset change2 in changes2.FiltredItems)
                        {
                            if (change.Quantity > 0 && change2.Quantity < 0)
                            {
                                // Get the asset data lines associated with the two changes in asset quantities
                                // that we have found.
                                //bool got1 = Assets.AssetExists(assetData, charID, corp, locationID, itemID,
                                //    change.StatusID, change.ContainerID != 0, change.ContainerID, change.IsContainer,
                                //    true, true, change.AutoConExclude, ref assetID1);
                                //bool got2 = Assets.AssetExists(assetData, charID, corp, change2.LocationID, itemID,
                                //    change2.StatusID, change2.ContainerID != 0, change2.ContainerID, change2.IsContainer,
                                //    true, true, change2.AutoConExclude, ref assetID2);
                                Assets.AddAssetToTable(assetData, change.ID);
                                Assets.AddAssetToTable(assetData, change2.ID);

                                EMMADataSet.AssetsRow row1 = assetData.FindByID(change.ID);
                                EMMADataSet.AssetsRow row2 = assetData.FindByID(change2.ID);
                                Asset a1 = new Asset(row1, null);
                                Asset a2 = new Asset(row2, null);
                                long thisAbsDeltaQ = Math.Min(Math.Abs(change.Quantity), Math.Abs(change2.Quantity));

                                // If the rows are processed then the actual movement of items has already happened
                                // so we don't need to adjust quantities.
                                // If they are not processed then we need to adjust the quantities now.
                                if (!row1.Processed) { row1.Quantity += thisAbsDeltaQ; }
                                if (!row2.Processed) { row2.Quantity -= thisAbsDeltaQ; }

                                // If the stack we're adding to now has zero items in it then we can just
                                // remove it. Otherwise we need to work out the value of the new stack.
                                if (row1.Quantity > 0)
                                {
                                    // Calculate the cost of items in the new stack based on the
                                    // cost of items from the old stack.
                                    decimal newCost = 0;
                                    newCost = ((a1.TotalBuyPrice) + (a2.UnitBuyPrice * thisAbsDeltaQ)) /
                                        (a1.Quantity + thisAbsDeltaQ);
                                    row1.Cost = newCost;
                                    row1.CostCalc = true;
                                }
                                else if (row1.Quantity == 0) { row1.Delete(); }
                                if (row2.Quantity == 0) { row2.Delete(); }

                                change.Quantity -= thisAbsDeltaQ;
                                change2.Quantity += thisAbsDeltaQ;
                            }
                        }

                    }
                }
                #endregion
            }

            // We have some items left over that could not be accounted for from losses/gains
            // elsewhere. Add these to the appropriate 'lost' or 'gained' list.
            #region Add remaining unexplained changes to the appropriate lost/gained list
            foreach (Asset change in changes)
            {
                if (change.Quantity != 0)
                {
                    Asset unexplained = new Asset();
                    unexplained.ID = change.ID;
                    unexplained.ItemID = change.ItemID;
                    unexplained.LocationID = change.LocationID;
                    unexplained.Quantity = change.Quantity;
                    unexplained.StatusID = change.StatusID;
                    unexplained.IsContainer = change.IsContainer;
                    unexplained.Container = change.Container;
                    unexplained.AutoConExclude = change.AutoConExclude;
                    unexplained.OwnerID = change.OwnerID;
                    if (change.UnitBuyPricePrecalculated)
                    {
                        unexplained.UnitBuyPrice = change.UnitBuyPrice;
                        unexplained.UnitBuyPricePrecalculated = true;
                    }
                    else
                    {
                        unexplained.UnitBuyPrice = 0;
                        unexplained.UnitBuyPricePrecalculated = false;
                    }
                    if (change.Quantity > 0)
                    {
                        // If the unexplained assets are for sale via contract or in transit then we
                        // would expect them not to show up if they are in the same state as before.
                        // This being the case, we do not need to add them to the list of unexplained items.
                        if (change.StatusID != (int)AssetStatus.States.ForSaleViaContract &&
                            change.StatusID != (int)AssetStatus.States.InTransit) { gained.Add(unexplained); }
                    }
                    else
                    {
                        // If the unexplained assets are for sale via contract or in transit then we
                        // would expect them not to show up if they are in the same state as before.
                        // This being the case, we do not need to add them to the list of unexplained items.
                        if (change.StatusID != (int)AssetStatus.States.ForSaleViaContract &&
                            change.StatusID != (int)AssetStatus.States.InTransit) { lost.Add(unexplained); }
                    }
                }
            }
            #endregion
        }
Esempio n. 13
0
        /// <summary>
        /// Update the assets table based on the transactions meeting the specified criteria.
        /// </summary>
        /// <param name="charID"></param>
        /// <param name="corpID"></param>
        /// <param name="useCorp"></param>
        /// <param name="minimumTransID"></param>
        /// <param name="includeTransAfter"></param>
        /// <returns></returns>
        private static long UpdateFromTransactions(EMMADataSet.AssetsDataTable assetsData,
            AssetList changes, long charID, long corpID, bool useCorp, long minimumTransID,
            DateTime includeTransAfter)
        {
            long maxID = 0;
            long ownerID = useCorp ? corpID : charID;

            EMMADataSet.TransactionsDataTable transactions = new EMMADataSet.TransactionsDataTable();
            transactions = GetTransactions(charID, corpID, useCorp, minimumTransID, includeTransAfter);

            foreach (EMMADataSet.TransactionsRow trans in transactions)
            {
                // If the ID is greater than 9000000000000000000 then it must have been created by EMMA as part of
                // an item exchange contract. These should be ignored here.
                if (trans.ID < 9000000000000000000)
                {
                    int deltaQuantity = trans.Quantity;
                    if (trans.SellerID == ownerID) { deltaQuantity *= -1; }

                    // Change this to not actually make the change in the database. Instead, use the
                    // asset data table passed in and record the changes made in the 'changes' list.

                    /*ChangeAssets(charID, useCorp, trans.StationID, trans.ItemID, 0, (int)AssetStatus.States.Normal,
                        false, deltaQuantity, (deltaQuantity > 0 ? trans.Price : 0), deltaQuantity > 0);*/
                    long assetID = 0;
                    EMMADataSet.AssetsRow asset = null;

                    AssetExists(assetsData, ownerID, trans.StationID, trans.ItemID,
                        (int)AssetStatus.States.Normal, false, 0, false, false, true, false, true, 0, ref assetID);

                    if (assetID != 0)
                    {
                        // Asset stack already exists in database and/or datatable, modify it
                        // based upon the transaction data.
                        asset = assetsData.FindByID(assetID);
                        asset.Quantity += deltaQuantity;
                        if (deltaQuantity > 0)
                        {
                            Asset logicalAsset = new Asset(asset);
                            asset.CostCalc = true;
                            asset.Cost = (logicalAsset.TotalBuyPrice + (trans.Price * trans.Quantity)) /
                                (logicalAsset.Quantity + trans.Quantity);
                        }
                        Asset chg = new Asset(asset);
                        chg.Quantity = deltaQuantity;
                        changes.Add(chg);
                    }
                    else
                    {
                        // Asset does not exist in database so add it to the datatable.
                        asset = assetsData.NewAssetsRow();
                        asset.Quantity = deltaQuantity;
                        asset.AutoConExclude = false;
                        asset.ContainerID = 0;
                        asset.CorpAsset = useCorp;
                        asset.Cost = trans.Price;
                        asset.CostCalc = true;
                        asset.IsContainer = false;
                        asset.ItemID = trans.ItemID;
                        asset.EveItemID = 0;
                        asset.LocationID = trans.StationID;
                        asset.OwnerID = charID;
                        asset.Processed = true;
                        asset.RegionID = trans.RegionID;
                        asset.ReprocExclude = false;
                        asset.Status = (int)AssetStatus.States.Normal;
                        asset.SystemID = Stations.GetStation(trans.StationID).solarSystemID;
                        asset.BoughtViaContract = false;
                        assetsData.AddAssetsRow(asset);
                        changes.Add(new Asset(asset));
                    }

                    if (trans.ID > maxID) { maxID = trans.ID; }
                }
            }

            return maxID;
        }
Esempio n. 14
0
 /// <summary>
 /// Get transactions meeting the specified parameters.
 /// </summary>
 /// <param name="charID"></param>
 /// <param name="corpID"></param>
 /// <param name="useCorp"></param>
 /// <param name="minimumTransID"></param>
 /// <param name="includeTransAfter"></param>
 /// <returns></returns>
 private static EMMADataSet.TransactionsDataTable GetTransactions(long charID, long corpID, bool useCorp,
     long minimumTransID, DateTime includeTransAfter)
 {
     long ownerID = useCorp ? corpID : charID;
     EMMADataSet.TransactionsDataTable transactions = new EMMADataSet.TransactionsDataTable();
     if (minimumTransID == -1)
     {
         List<FinanceAccessParams> accessParams = new List<FinanceAccessParams>();
         accessParams.Add(new FinanceAccessParams(ownerID, false));
         List<long> ids = new List<long>();
         ids.Add(0);
         List<int> itemIds = new List<int>();
         itemIds.Add(0);
         transactions = Transactions.GetTransData(accessParams, itemIds, ids, ids, includeTransAfter, DateTime.MaxValue, "");
     }
     else
     {
         transactions = Transactions.GetTransData(ownerID, false, 0, 0, 0, minimumTransID, "");
     }
     return transactions;
 }