protected void ButtonAutoRemap_Click(object sender, EventArgs e)
 {
     Payouts.AutomatchAgainstUnbalancedTransactions(Organization.FromIdentity(Int32.Parse(this.DropOrganizations.SelectedValue)));
     PopulateGrid();
     PopulatePayouts(Int32.Parse(this.DropOrganizations.SelectedValue));
     this.GridPayouts.Rebind();
     this.GridTransactions.Rebind();
 }
Example #2
0
    protected void Page_Load(object sender, EventArgs e)
    {
        this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.Bookkeeping, AccessType.Write);

        this.PageTitle = "Debug Ledgers";
        this.PageIcon  = "iconshock-tester";

        // Initialize by mapping all

        // TODO: If this O(n^2) matching becomes teh suckage, optimize using hashtables over amounts

        Payouts.AutomatchAgainstUnbalancedTransactions(this.CurrentOrganization);

        // Iterate over all open payment groups and try to map them to unbalanced transactions

        FinancialTransactions unbalancedTransactions = FinancialTransactions.GetUnbalanced(this.CurrentOrganization); // TODO: this fn should move to Organization
        PaymentGroups         openGroups             = PaymentGroups.ForOrganization(this.CurrentOrganization, false);

        foreach (PaymentGroup openGroup in openGroups)
        {
            openGroup.MapTransaction(unbalancedTransactions);
        }
    }
Example #3
0
    protected void Page_Load(object sender, EventArgs e)
    {
        FinancialAccount bankAccount = FinancialAccount.FromIdentity(29);
        Organization     euroPirates = Organization.FromIdentity(7);

        FinancialTransactions unbalancedTransactions = FinancialTransactions.GetUnbalanced(euroPirates);

        // assume all in bank account

        foreach (FinancialTransaction transaction in unbalancedTransactions)
        {
            if (transaction.Description[0] == 'H' && Char.IsDigit(transaction.Description[1]) &&
                transaction.Rows.BalanceCentsDelta > 0)
            {
                // outbound intl invoice. Add an untracked intl invoice 10 days prior, map it to this

                OutboundInvoice newInvoice = OutboundInvoice.Create(euroPirates,
                                                                    transaction.DateTime, euroPirates.FinancialAccounts.IncomeSales, "Untracked",
                                                                    "*****@*****.**", string.Empty, euroPirates.Currency, false, string.Empty, Person.FromIdentity(1));
                newInvoice.AddItem("Untracked item", transaction.Rows.BalanceCentsDelta);

                // Doesn't close

                // Add transaction

                // Create the financial transaction with rows

                FinancialTransaction invoiceTransaction =
                    FinancialTransaction.Create(euroPirates.Identity, transaction.DateTime.AddDays(-10),
                                                "Outbound Invoice #" + newInvoice.Identity);

                invoiceTransaction.AddRow(euroPirates.FinancialAccounts.AssetsOutboundInvoices, newInvoice.AmountCents,
                                          null);
                invoiceTransaction.AddRow(euroPirates.FinancialAccounts.IncomeSales, -newInvoice.AmountCents, null);
                invoiceTransaction.Dependency = newInvoice;

                transaction.AddRow(euroPirates.FinancialAccounts.AssetsOutboundInvoices, -newInvoice.AmountCents, null);

                Payment payment = Payment.CreateSingle(euroPirates, transaction.DateTime, euroPirates.Currency,
                                                       newInvoice.AmountCents, newInvoice, null);

                transaction.Dependency = payment.Group;
                payment.Group.Open     = false;
            }

            if ((transaction.Description == "Kostnad" || transaction.Description == "Bank charges") && transaction.Rows.BalanceCentsDelta < 0 &&
                transaction.Rows.BalanceCentsDelta > -125000)
            {
                // Bank fee.

                transaction.AddRow(euroPirates.FinancialAccounts.CostsBankFees, -transaction.Rows.BalanceCentsDelta,
                                   null);
            }

            if (transaction.Description.StartsWith("Paypal "))
            {
                // Bank fee.

                transaction.AddRow(euroPirates.FinancialAccounts.CostsBankFees, -transaction.Rows.BalanceCentsDelta,
                                   null);
            }
        }

        Payouts.AutomatchAgainstUnbalancedTransactions(euroPirates);
    }
Example #4
0
        private static void OnEveryHour()
        {
            try
            {
                BotLog.Write(0, "MainCycle", "One-hour entry");

                try
                {
                    // This will upgrade the database schema if and only if we failed to do so on entry.

                    if (SwarmDb.DbVersion < SwarmDb.DbVersionExpected)
                    {
                        BotLog.Write(1, "OneHour", "Entering DbUpgrade");
                        DatabaseMaintenance.UpgradeSchemata();
                        BotLog.Write(1, "OneHour", "Exited DbUpgrade");
                    }
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }


                try
                {
                    BotLog.Write(1, "OneHour", "Entering Automatch");
                    Organizations organizations = Organizations.GetAll();
                    foreach (Organization organization in organizations)
                    {
                        if (organization.IsEconomyEnabled)
                        {
                            BotLog.Write(1, "OneHour", "Automatching org #" + organization.Identity.ToString(CultureInfo.InvariantCulture));
                            Payouts.AutomatchAgainstUnbalancedTransactions(organization);
                        }
                    }
                    BotLog.Write(1, "OneHour", "Exited Automatch");

                    /*TestTrace("Running PaymentGroupMapper.Run()...");
                     * PaymentGroupMapper.Run();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                try
                {
                    /*TestTrace("Running BlogTop50Scraper.Run()...");
                     * BlogTop50Scraper.Run();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                try
                {
                    /*TestTrace("Running Mappery.CreatePiratpartietOrganizationStrengthCircuitMap()...");
                     * Mappery.CreatePiratpartietOrganizationStrengthCircuitMap();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                try
                {
                    /*TestTrace("Running Mappery.CreatePiratpartietOrganizationStrengthCityMap()...");
                     * Mappery.CreatePiratpartietOrganizationStrengthCityMap();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                BotLog.Write(0, "MainCycle", "One-hour exit");
            }
            catch (Exception e)
            {
                TraceAndReport(e);
            }
        }
    protected void ProcessImportedData(ImportResult import)
    {
        FinancialAccount assetAccount       = FinancialAccount.FromIdentity(Int32.Parse(this.DropAssetAccount.SelectedValue));
        FinancialAccount autoDepositAccount =
            FinancialAccount.FromIdentity(Int32.Parse(this.DropAutoDeposits.SelectedValue));
        FinancialAccount autoWithdrawalAccount =
            FinancialAccount.FromIdentity(Int32.Parse(this.DropAutoWithdrawals.SelectedValue));
        int          autoDepositLimit    = Int32.Parse(this.TextDepositLimit.Text);
        int          autoWithdrawalLimit = Int32.Parse(this.TextWithdrawalLimit.Text);
        int          organizationId      = Int32.Parse(this.DropOrganizations.SelectedValue);
        Organization organization        = Organization.FromIdentity(organizationId);

        int importedTransactionCount = 0;

        foreach (ImportedRow row in import.Rows)
        {
            // Each row is at least a stub, probably more.

            // If too old, ignore.

            if (row.DateTime < new DateTime(2008, 12, 4))
            {
                continue;
            }

            string importKey = row.SuppliedTransactionId;

            // If importKey is empty, construct a hash from the data fields.

            if (string.IsNullOrEmpty(importKey))
            {
                string hashKey = row.HashBase + row.Comment + (row.AmountCentsNet / 100.0).ToString(CultureInfo.InvariantCulture) + row.CurrentBalance.ToString(CultureInfo.InvariantCulture) +
                                 row.DateTime.ToString("yyyy-MM-dd-hh-mm-ss");

                importKey = SHA1.Hash(hashKey).Replace(" ", "");
            }

            if (importKey.Length > 30)
            {
                importKey = importKey.Substring(0, 30);
            }

            Int64 amountCents = row.AmountCentsNet;

            if (amountCents == 0)
            {
                amountCents = row.AmountCentsGross;
            }

            FinancialTransaction transaction = FinancialTransaction.ImportWithStub(organizationId, row.DateTime,
                                                                                   assetAccount.Identity, amountCents,
                                                                                   row.Comment, importKey,
                                                                                   _currentUser.Identity);

            if (transaction != null)
            {
                // The transaction was created. Examine if the autobook criteria are true.

                importedTransactionCount++;

                FinancialAccounts accounts = FinancialAccounts.FromBankTransactionTag(row.Comment);

                if (accounts.Count == 1)
                {
                    // This is a labelled local donation.

                    Geography        geography    = accounts[0].AssignedGeography;
                    FinancialAccount localAccount = accounts[0];

                    transaction.AddRow(organization.FinancialAccounts.IncomeDonations, -amountCents, _currentUser);
                    transaction.AddRow(organization.FinancialAccounts.CostsLocalDonationTransfers,
                                       amountCents, _currentUser);
                    transaction.AddRow(localAccount, -amountCents, _currentUser);

                    Activizr.Logic.Support.PWEvents.CreateEvent(EventSource.PirateWeb, EventType.LocalDonationReceived,
                                                                _currentUser.Identity, organizationId,
                                                                geography.Identity, 0,
                                                                transaction.Identity, localAccount.Identity.ToString());
                }
                else if (row.Comment.ToLowerInvariant().StartsWith("bg 451-0061 "))   // TODO: Organization.Parameters.FinancialTrackedTransactionPrefix
                {
                    // Check for previously imported payment group

                    PaymentGroup group = PaymentGroup.FromTag(organization,
                                                              "SEBGM" + DateTime.Today.Year.ToString() +   // TODO: Get tagging from org
                                                              row.Comment.Substring(11));

                    if (group != null)
                    {
                        // There was a previously imported and not yet closed payment group matching this transaction
                        // Close the payment group and match the transaction against accounts receivable

                        transaction.Dependency = group;
                        group.Open             = false;
                        transaction.AddRow(organization.FinancialAccounts.AssetsOutboundInvoices, -amountCents, _currentUser);
                    }
                }
                else if (amountCents < 0)
                {
                    if ((-amountCents) < autoWithdrawalLimit * 100)
                    {
                        // Book against autoWithdrawal account.
                        transaction.AddRow(autoWithdrawalAccount, -amountCents, _currentUser);
                    }
                }
                else if (amountCents > 0)
                {
                    if (row.Fee < 0)
                    {
                        // This is always an autodeposit, if there is a fee (which is never > 0.0)

                        transaction.AddRow(organization.FinancialAccounts.CostsBankFees, -row.Fee, _currentUser);
                        transaction.AddRow(autoDepositAccount, -row.AmountCentsGross, _currentUser);
                    }
                    else if (amountCents < autoDepositLimit * 100)
                    {
                        // Book against autoDeposit account.

                        transaction.AddRow(autoDepositAccount, -amountCents, _currentUser);
                    }
                }
            }
        }

        // Import complete. Examine if we expect more transactions -- if the imported balance differs from
        // the database balance:

        double databaseAccountBalance = assetAccount.BalanceTotal;

        bool mismatch = false;

        if (databaseAccountBalance != import.CurrentBalance)
        {
            mismatch = true;
        }

        string message = importedTransactionCount.ToString() + " transactions were imported.";

        if (importedTransactionCount == 0)
        {
            message = "No transactions were imported. ";
        }
        else if (importedTransactionCount == 1)
        {
            message = "One transaction was imported. ";
        }

        if (import.CurrentBalance > 0)
        {
            if (mismatch)
            {
                message += " Transactions are missing from the database. Import more transactions.";
            }
            else
            {
                message += " The account balance is up to date. No further import is necessary.";

                ScriptManager.RegisterStartupScript(this, Page.GetType(), "alldone",
                                                    "alert ('The account balance is up to date. No further import is necessary.');",
                                                    true);

                // Auto-match open payouts against new transactions

                Payouts.AutomatchAgainstUnbalancedTransactions(organization);
            }
        }

        this.LabelImportResultText.Text = message;
    }
Example #6
0
        public static ImportExternalTransactionDataResults ImportExternalTransactionData(ExternalBankData import, ImportExternalTransactionDataArgs args)
        {
            FinancialAccount assetAccount       = args.Account;
            FinancialAccount autoDepositAccount = args.Organization.FinancialAccounts.IncomeDonations;
            int autoDepositLimit = 0; // Disabled; TODO: this.CurrentOrganization.Parameters.AutoDonationLimit;

            bool autosetInitialBalance = false;
            ImportExternalTransactionDataResults result = new ImportExternalTransactionDataResults();
            int   count = 0;
            int   progressUpdateInterval = import.Records.Length / 40;
            Int64 importedCentsTotal     = 0;

            if (progressUpdateInterval > 100)
            {
                progressUpdateInterval = 100;
            }

            ProgressBarBackend progressDisplay = new ProgressBarBackend(args.Guid);

            Currency organizationCurrency = assetAccount.Organization.Currency;
            Currency accountCurrency      = assetAccount.ForeignCurrency;

            if (accountCurrency == null)
            {
                accountCurrency = organizationCurrency;
            }

            FinancialAccountRows existingRows = assetAccount.GetRows(Constants.DateTimeLow, Constants.DateTimeHigh);

            // gets all
            if (existingRows.Count == 0)
            {
                autosetInitialBalance = true;
            }


            foreach (ExternalBankDataRecord row in import.Records)
            {
                // Update progress.

                count++;
                if (progressUpdateInterval < 2 || count % progressUpdateInterval == 0)
                {
                    int percent = (count * 99) / import.Records.Length;

                    progressDisplay.Set(percent);
                }

                // Update high- and low-water marks.

                if (row.DateTime < result.EarliestTransaction)
                {
                    result.EarliestTransaction = row.DateTime;
                }

                if (row.DateTime > result.LatestTransaction)
                {
                    result.LatestTransaction = row.DateTime;
                }


                string importKey = row.ImportHash;

                Int64 amountCents = row.TransactionNetCents;

                if (amountCents == 0)
                // defensive programming - these _should_ be duplicated in the interpreter if no "fee" field
                {
                    amountCents = row.TransactionGrossCents;
                }

                Int64 foreignCents = amountCents;
                importedCentsTotal += amountCents;

                if (accountCurrency.Identity != organizationCurrency.Identity)
                {
                    amountCents =
                        new Money(amountCents, accountCurrency, row.DateTime).ToCurrency(organizationCurrency).Cents;
                }

                FinancialTransaction transaction = FinancialTransaction.ImportWithStub(args.Organization.Identity,
                                                                                       row.DateTime,
                                                                                       assetAccount.Identity, amountCents,
                                                                                       row.Description, importKey, Sha256.Compute(row.RawData),
                                                                                       args.CurrentUser.Identity);

                if (transaction != null)
                {
                    // The transaction was created.

                    result.TransactionsImported++;

                    // If non-presentation currency, log the account currency amount as well.

                    if (accountCurrency.Identity != organizationCurrency.Identity)
                    {
                        transaction.Rows[0].AmountForeignCents = new Money(foreignCents, accountCurrency);
                    }

                    if (row.Description.ToLowerInvariant().StartsWith(args.Organization.IncomingPaymentTag))
                    {
                        // Check for previously imported payment group

                        // TODO: MAKE FLEXIBLE - CALL PAYMENTREADERINTERFACE!
                        // HACK HACK HACK HACK

                        PaymentGroup group = PaymentGroup.FromTag(args.Organization,
                                                                  "SEBGM" + DateTime.Today.Year + // TODO: Get tags from org
                                                                  row.Description.Substring(args.Organization.IncomingPaymentTag.Length).Trim());

                        if (group != null && group.Open)
                        {
                            // There was a previously imported and not yet closed payment group matching this transaction
                            // Close the payment group and match the transaction against accounts receivable

                            transaction.Dependency = group;
                            group.Open             = false;
                            transaction.AddRow(args.Organization.FinancialAccounts.AssetsOutboundInvoices, -amountCents,
                                               args.CurrentUser);
                        }
                    }
                    else if (amountCents < 0)
                    {
                        // Autowithdrawal mechanisms removed, condition kept because of downstream else-if conditions
                    }
                    else if (amountCents > 0)
                    {
                        if (row.FeeCents < 0)
                        {
                            // This is always an autodeposit, if there is a fee (which is never > 0.0)

                            transaction.AddRow(args.Organization.FinancialAccounts.CostsBankFees, -row.FeeCents,
                                               args.CurrentUser);
                            transaction.AddRow(autoDepositAccount, -row.TransactionGrossCents, args.CurrentUser);
                        }
                        else if (amountCents < autoDepositLimit * 100)
                        {
                            // Book against autoDeposit account.

                            transaction.AddRow(autoDepositAccount, -amountCents, args.CurrentUser);
                        }
                    }
                }
                else
                {
                    // Transaction was not imported; assume duplicate

                    result.DuplicateTransactions++;
                }
            }

            // Import complete. Return true if the bookkeeping account matches the bank data.

            Int64 databaseAccountBalanceCents;

            if (accountCurrency.Identity == organizationCurrency.Identity)
            {
                databaseAccountBalanceCents = assetAccount.BalanceTotalCents;
            }
            else
            {
                // foreign-currency account
                databaseAccountBalanceCents = assetAccount.ForeignCurrencyBalance.Cents;
            }


            // Subtract any transactions made after the most recent imported transaction.
            // This is necessary in case of Paypal and others which continuously feed the
            // bookkeeping account with new transactions; it will already have fed transactions
            // beyond the end-of-file.

            Int64 beyondEofCents = assetAccount.GetDeltaCents(result.LatestTransaction.AddSeconds(1),
                                                              DateTime.Now.AddDays(2));

            // Caution: the "AddSeconds(1)" is not foolproof, there may be other new txs on the same second.

            if (databaseAccountBalanceCents - beyondEofCents == import.LatestAccountBalanceCents)
            {
                Payouts.AutomatchAgainstUnbalancedTransactions(args.Organization);
                OutboundInvoices.AutomatchAgainstUnbalancedTransactions(args.Organization, args.CurrentUser);
                result.AccountBalanceMatchesBank = true;
                result.BalanceMismatchCents      = 0;
            }
            else
            {
                result.AccountBalanceMatchesBank = false;
                result.BalanceMismatchCents      = (databaseAccountBalanceCents - beyondEofCents) -
                                                   import.LatestAccountBalanceCents;

                if (autosetInitialBalance)
                {
                    Int64 newInitialBalanceCents = -result.BalanceMismatchCents;
                    Money initialBalance         = new Money(newInitialBalanceCents, accountCurrency);

                    assetAccount.InitialBalance       = initialBalance;
                    result.InitialBalanceCents        = newInitialBalanceCents;
                    result.InitialBalanceCurrencyCode = accountCurrency.Code;

                    // make an approximation of conversion rate set for initial balance in presentation to tell user
                    initialBalance.ValuationDateTime = new DateTime(assetAccount.Organization.FirstFiscalYear, 1, 1);
                    result.BalanceMismatchCents      = initialBalance.ToCurrency(assetAccount.Organization.Currency).Cents;
                }
            }

            result.CurrencyCode = args.Organization.Currency.Code;
            GuidCache.Set(args.Guid + "-Results", result);
            return(result);
        }