Exemple #1
0
        protected static ImportStats ProcessImportedData(ImportResult import, Organization organization,
                                                         Person importingPerson)
        {
            FinancialAccount payPalAccount = organization.FinancialAccounts.AssetsPaypal;
            FinancialAccount bankFees      = organization.FinancialAccounts.CostsBankFees;
            FinancialAccount donations     = organization.FinancialAccounts.IncomeDonations;

            int autoDepositLimit    = 1000; // TODO: Get from organization parameters
            int autoWithdrawalLimit = 0;

            ImportStats result = new ImportStats();

            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;
                }

                Dictionary <int, long> nominalTransaction = new Dictionary <int, long>();

                FinancialTransaction transaction = null;

                try
                {
                    transaction = FinancialTransaction.FromImportKey(organization, importKey);
                }
                catch (Exception)
                {
                    // if we get here, that means the transaction did not yet exist

                    transaction = FinancialTransaction.ImportWithStub(organization.Identity, row.DateTime,
                                                                      payPalAccount.Identity, amountCents,
                                                                      row.Comment, importKey, "" /* new SHA256 field */,
                                                                      importingPerson.Identity);
                    result.ImportedTransactionCount++;

                    if (transaction == null)
                    {
                        // No transaction was created. This is an error condition as it should have been created if it didn't
                        // exist, and the "exist" case is handled in the FromImportKey attempt above. Abort with error.
                        // Throw new exception?

                        continue;
                    }
                }

                result.ProcessedTransactionCount++;

                nominalTransaction[payPalAccount.Identity] = amountCents;

                // The transaction was created. Examine if the autobook criteria are true.

                if (amountCents < 0)
                {
                    if ((-amountCents) < autoWithdrawalLimit * 100)
                    {
                        // Book against autoWithdrawal account.

                        nominalTransaction[bankFees.Identity] = -amountCents;
                    }
                }
                else if (amountCents > 0)
                {
                    if (row.FeeCents < 0)
                    {
                        // This is always an autodeposit, if there is a fee (which is never > 0.0)

                        nominalTransaction[bankFees.Identity]  = -row.FeeCents;
                        nominalTransaction[donations.Identity] = -row.AmountCentsGross;
                    }
                    else if (amountCents < autoDepositLimit * 100)
                    {
                        // Book against autoDeposit account.

                        nominalTransaction[donations.Identity] = -amountCents;
                    }
                }

                if (transaction.Rows.AmountCentsTotal != 0) // If transaction is unbalanced, balance it
                {
                    if (transaction.RecalculateTransaction(nominalTransaction, importingPerson))
                    {
                        result.ModifiedTransactionCount++;
                    }
                }
            }

            return(result);
        }
    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;
    }
Exemple #3
0
    protected void Page_Load(object sender, EventArgs e)
    {
        // THIS CODE IS TAKEN FROM PAYPAL'S TEMPLATE.

        Person notifyPerson = Person.FromIdentity(1);


        //Post back to either sandbox or live
        string         strLive = "https://www.paypal.com/cgi-bin/webscr";
        HttpWebRequest req     = (HttpWebRequest)WebRequest.Create(strLive);

        //Set values for the request back
        req.Method      = "POST";
        req.ContentType = "application/x-www-form-urlencoded";
        byte[] param           = Request.BinaryRead(HttpContext.Current.Request.ContentLength);
        string strRequest      = Encoding.ASCII.GetString(param);
        string originalRequest = strRequest;

        strRequest       += "&cmd=_notify-validate";
        req.ContentLength = strRequest.Length;

        //Send the request to PayPal and get the response
        StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII);

        streamOut.Write(strRequest);
        streamOut.Close();
        StreamReader streamIn    = new StreamReader(req.GetResponse().GetResponseStream());
        string       strResponse = streamIn.ReadToEnd();

        streamIn.Close();

        // Decode parameters
        List <string> temp = new List <string>();
        Dictionary <string, string> parameters = new Dictionary <string, string>();

        string[] parameterStrings = originalRequest.Split('&');
        foreach (string parameter in parameterStrings)
        {
            string[] data = parameter.Split('=');

            string dataKey   = Server.UrlDecode(data[0]);
            string dataValue = Server.UrlDecode(data[1]);

            parameters[dataKey] = dataValue;
            temp.Add(string.Format("{0,-20} {1}", dataKey, dataValue));
        }


        notifyPerson.SendNotice("Paypal listener point A", String.Join("\r\n", temp.ToArray()), 1);

        bool proceed = true;

        if (strResponse == "INVALID")
        {
            notifyPerson.SendNotice("Paypal listener FAIL - Response is INVALID", string.Empty, 1);
            proceed = false;
        }
        if (!parameters.ContainsKey("payment_status"))
        {
            notifyPerson.SendNotice("Paypal listener FAIL - payment_status was not supplied", string.Empty, 1);
            proceed = false;
        }
        else if (parameters["payment_status"] != "Completed" && parameters["payment_status"] != "Refunded")
        {
            notifyPerson.SendNotice("Paypal listener FAIL - payment_status is not Completed / Refunded", string.Empty, 1);
            proceed = false;
        }
        if (parameters["receiver_email"] != "*****@*****.**")
        {
            notifyPerson.SendNotice("Paypal listener FAIL - receiver_email is not [email protected]", string.Empty, 1);
            proceed = false;  // HACK -- adjust for multiple orgs
        }
        if (parameters["mc_currency"] != "SEK")
        {
            notifyPerson.SendNotice("Paypal listener FAIL - mc_currency is not SEK", string.Empty, 1);
            proceed = false; // HACK -- adjust for multiple orgs
        }
        if (!parameters.ContainsKey("mc_gross") || String.IsNullOrEmpty(parameters["mc_gross"]))
        {
            notifyPerson.SendNotice("Paypal listener FAIL - mc_net is null or empty", string.Empty, 1);
            proceed = false;
        }

        if (strResponse == "VERIFIED" && proceed)
        {
            string transactionId = parameters["txn_id"];
            string identifier    = null;

            if (parameters.ContainsKey("invoice"))
            {
                identifier = parameters["invoice"];
            }

            OutboundInvoice invoice = null;

            if (!String.IsNullOrEmpty(identifier))
            {
                invoice = OutboundInvoice.FromReference(identifier);

                notifyPerson.SendNotice("Paypal listener - invoice identified as #" + invoice.Identity.ToString(), string.Empty, 1);
            }

            string grossString = parameters["mc_gross"];

            string feeString = "0.0";

            if (parameters.ContainsKey("mc_fee") && !string.IsNullOrEmpty(parameters["mc_fee"]))
            {
                feeString = parameters["mc_fee"];
            }

            string comment = "Paypal ";

            if (parameters["payment_status"] == "Completed")
            {
                comment += parameters["txn_type"];
            }
            else
            {
                // Refund

                comment += "Refund";
            }

            if (parameters.ContainsKey("item_name"))
            {
                comment = parameters["item_name"];
            }

            if (invoice != null)
            {
                comment = "Outbound Invoice #" + invoice.Identity.ToString() + " payment Paypal";
            }

            DateTime dateTime = DateTime.ParseExact(parameters["payment_date"].Replace("PDT", "-07").Replace("PST", "-08"), "HH:mm:ss MMM dd, yyyy zz", CultureInfo.InvariantCulture).ToLocalTime();

            Organization org = Organization.PPSE;

            Int64 feeCents   = Int64.Parse(feeString.Replace(".", ""));
            Int64 grossCents = Int64.Parse(grossString.Replace(".", ""));

            Int64 amountCents = grossCents - feeCents;

            FinancialTransaction transaction = FinancialTransaction.ImportWithStub(org.Identity, dateTime,
                                                                                   org.FinancialAccounts.AssetsPaypal.Identity, amountCents,
                                                                                   comment, transactionId,
                                                                                   0);  // TODO: Convert to cents

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

                if (amountCents < 0 && parameters["payment_status"] == "Completed")
                {
                    // Do not balance the transaction -- will have to be balanced manually
                }
                else if (amountCents > 0)
                {
                    if (invoice != null)
                    {
                        Int64 expectedNetCents = invoice.AmountCents;

                        transaction.AddRow(org.FinancialAccounts.CostsBankFees, expectedNetCents - amountCents, null);  // can be negative
                        transaction.AddRow(org.FinancialAccounts.AssetsOutboundInvoices, -expectedNetCents, null);

                        // Hack: Add a line to the outbound invoice HERE with the PayPal surcharge

                        invoice.AddItem("PayPal/Credit Card surcharge, 5%", (Int64)(invoice.AmountCents * 0.05));

                        Payment payment = Payment.CreateSingle(invoice.Organization, DateTime.Now, invoice.Currency,
                                                               expectedNetCents, invoice, null);  // HACK HACK HACK: says we always received the expected amount

                        payment.AddInformation(PaymentInformationType.RefundInformation, "PayPal " + transactionId);
                        payment.AddInformation(PaymentInformationType.Name, parameters["first_name"] + " " + parameters["last_name"]);
                        payment.AddInformation(PaymentInformationType.Email, parameters["payer_email"]);

                        transaction.Dependency = payment.Group;
                    }
                    else if (feeCents > 0)
                    {
                        // This is always an autodeposit

                        transaction.AddRow(org.FinancialAccounts.CostsBankFees, feeCents, null);
                        transaction.AddRow(org.FinancialAccounts.IncomeDonations, -grossCents, null);
                    }
                    else
                    {
                        transaction.AddRow(org.FinancialAccounts.IncomeDonations, -amountCents, null);
                    }
                }
            }
        }
    }
Exemple #4
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);
        }