Esempio n. 1
0
        public async Task <IActionResult> Edit(int id, [Bind("ParentId,Class,CurrencyId,Id,UserId,LogRecordId,Status,ModDateTime,ModByUserId,Comment")] PaymentGroup paymentGroup)
        {
            if (id != paymentGroup.Id)
            {
                return(NotFound());
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(paymentGroup);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!PaymentGroupExists(paymentGroup.Id))
                    {
                        return(NotFound());
                    }
                    else
                    {
                        throw;
                    }
                }
                return(RedirectToAction(nameof(Index)));
            }
            ViewData["CurrencyId"] = new SelectList(_context.Currencies, "Id", "Id", paymentGroup.CurrencyId);
            ViewData["ParentId"]   = new SelectList(_context.PersonGroups, "Id", "Id", paymentGroup.ParentId);
            return(View(_mapper.Map <PaymentGroupViewModel>(paymentGroup)));
        }
Esempio n. 2
0
            public static PaymentGroup ToPaymentGroup(PaymentsGroupViewModel vm, bool showCategorized)
            {
                var visiblePayments = vm.GetVisiblePayments(showCategorized);
                var paymentGroup    = new PaymentGroup
                {
                    IsExpanded   = vm.IsExpanded,
                    Amount       = visiblePayments.Select(v => v.Amount).Sum(),
                    Ccy          = visiblePayments[0].Ccy,
                    Id           = vm.Id.ToUUID(),
                    Kind         = visiblePayments[0].Kind,
                    PaymentCount = visiblePayments.Count,
                    What         = visiblePayments[0].What,
                    When         = visiblePayments[0].When.ToTimestamp(),
                    CategoryId   = visiblePayments[0].CategoryId.ToUUID(),
                    ColumnId     = visiblePayments[0].ColumnId.ToUUID(),
                    DebtId       = visiblePayments[0].DebtId.ToUUID()
                };

                if (vm.IsExpanded)
                {
                    foreach (var v in visiblePayments)
                    {
                        paymentGroup.Payments.Add(new PaymentView {
                            Payment = ToPaymentView(v)
                        });
                    }
                }
                return(paymentGroup);
            }
Esempio n. 3
0
        public async Task <IActionResult> Create([Bind("ParentId,Class,CurrencyId,Id,UserId,LogRecordId,Status,ModDateTime,ModByUserId,Comment")] PaymentGroup paymentGroup)
        {
            if (ModelState.IsValid)
            {
                _context.Add(paymentGroup);
                await _context.SaveChangesAsync();

                return(RedirectToAction(nameof(Index)));
            }
            ViewData["CurrencyId"] = new SelectList(_context.Currencies, "Id", "Id", paymentGroup.CurrencyId);
            ViewData["ParentId"]   = new SelectList(_context.PersonGroups, "Id", "Id", paymentGroup.ParentId);
            return(View(_mapper.Map <PaymentGroupViewModel>(paymentGroup)));
        }
Esempio n. 4
0
        // GET: PaymentGroup/Delete/5
        public ActionResult Delete(Nullable <byte> id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }

            Db           db           = new Db(DbServices.ConnectionString);
            PaymentGroup paymentGroup = PaymentGroupServices.Get(id.Value, db);

            if (paymentGroup == null)
            {
                return(HttpNotFound());
            }
            return(View(paymentGroup));
        }
    protected void GridPaymentGroups_ItemCreated(object sender, GridItemEventArgs e)
    {
        if (e.Item is GridDataItem)
        {
            PaymentGroup group = (PaymentGroup)e.Item.DataItem;

            if (group == null)
            {
                return;
            }

            HyperLink editLink = (HyperLink)e.Item.FindControl("ManageLink");
            editLink.Attributes["href"]    = "#";
            editLink.Attributes["onclick"] = String.Format("return ShowPaymentGroupForm('{0}','{1}');",
                                                           group.Identity, e.Item.ItemIndex);
        }
    }
Esempio n. 6
0
        public ActionResult Create([Bind(Include = "Id, Name")] PaymentGroup paymentGroup)
        {
            Db db = new Db(DbServices.ConnectionString);

            if (ModelState.IsValid)
            {
                try
                {
                    PaymentGroupServices.Insert(CurrentUser.Id, paymentGroup, db);
                    TempData["Success"] = ResourceServices.GetString(Cf.Data.Resources.ResourceBase.Culture, "UI", "InsertConfirmed");
                    return(RedirectToAction("Index"));
                }
                catch (CfException cfex)
                {
                    TempData["Failure"] = cfex.ErrorDefinition.LocalizedMessage;
                }
                catch (Exception ex)
                {
                    TempData["Failure"] = ex.Message;
                }
            }

            return(View(paymentGroup));
        }
Esempio n. 7
0
        static public object FromBasic(IHasIdentity basic)
        {
            string argumentType = basic.GetType().ToString();

            switch (argumentType)
            {
            // ----- Is there any way to make self-writing code here through replication or similar, so
            // ----- that every case doesn't need to be listed?


            // ------------ COMMUNICATION CLASSES ------------

            case "Swarmops.Basic.Types.BasicCommunicationTurnaround":
                return(CommunicationTurnaround.FromBasic((BasicCommunicationTurnaround)basic));

            case "Swarmops.Basic.Types.Communications.BasicOutboundComm":
                return(OutboundComm.FromBasic((BasicOutboundComm)basic));

            case "Swarmops.Basic.Types.Communications.BasicOutboundCommRecipient":
                return(OutboundCommRecipient.FromBasic((BasicOutboundCommRecipient)basic));


            // ----------- FINANCIAL CLASSES ----------

            case "Swarmops.Basic.Types.BasicExpenseClaim":
                return(ExpenseClaim.FromBasic((BasicExpenseClaim)basic));

            case "Swarmops.Basic.Types.Financial.BasicCashAdvance":
                return(CashAdvance.FromBasic((BasicCashAdvance)basic));

            case "Swarmops.Basic.Types.BasicInboundInvoice":
                return(InboundInvoice.FromBasic((BasicInboundInvoice)basic));

            case "Swarmops.Basic.Types.Financial.BasicFinancialAccount":
                return(FinancialAccount.FromBasic((BasicFinancialAccount)basic));

            case "Swarmops.Basic.Types.Financial.BasicFinancialTransaction":
                return(FinancialTransaction.FromBasic((BasicFinancialTransaction)basic));

            case "Swarmops.Basic.Types.BasicFinancialValidation":
                return(FinancialValidation.FromBasic((BasicFinancialValidation)basic));

            case "Swarmops.Basic.Types.BasicOutboundInvoice":
                return(OutboundInvoice.FromBasic((BasicOutboundInvoice)basic));

            case "Swarmops.Basic.Types.BasicOutboundInvoiceItem":
                return(OutboundInvoiceItem.FromBasic((BasicOutboundInvoiceItem)basic));

            case "Swarmops.Basic.Types.BasicPayment":
                return(Payment.FromBasic((BasicPayment)basic));

            case "Swarmops.Basic.Types.BasicPaymentGroup":
                return(PaymentGroup.FromBasic((BasicPaymentGroup)basic));

            case "Swarmops.Basic.Types.BasicPayout":
                return(Payout.FromBasic((BasicPayout)basic));

            case "Swarmops.Basic.Types.BasicPayrollAdjustment":
                return(PayrollAdjustment.FromBasic((BasicPayrollAdjustment)basic));

            case "Swarmops.Basic.Types.BasicPayrollItem":
                return(PayrollItem.FromBasic((BasicPayrollItem)basic));

            case "Swarmops.Basic.Types.BasicSalary":
                return(Salary.FromBasic((BasicSalary)basic));

            case "Swarmops.Basic.Types.Financial.BasicFinancialTransactionTagSet":
                return(FinancialTransactionTagSet.FromBasic((BasicFinancialTransactionTagSet)basic));

            case "Swarmops.Basic.Types.Financial.BasicFinancialTransactionTagType":
                return(FinancialTransactionTagType.FromBasic((BasicFinancialTransactionTagType)basic));

            // ------------ GOVERNANCE CLASSES ------------

            case "Swarmops.Basic.Types.BasicBallot":
                return(Ballot.FromBasic((BasicBallot)basic));

            case "Swarmops.Basic.Types.BasicMeetingElectionCandidate":
                return(MeetingElectionCandidate.FromBasic((BasicInternalPollCandidate)basic));

            case "Swarmops.Basic.Types.BasicMeetingElection":
                return(MeetingElection.FromBasic((BasicInternalPoll)basic));

            case "Swarmops.Basic.Types.BasicMeetingElectionVote":
                return(MeetingElectionVote.FromBasic((BasicInternalPollVote)basic));

            case "Swarmops.Basic.Types.Governance.BasicMotion":
                return(Motion.FromBasic((BasicMotion)basic));

            case "Swarmops.Basic.Types.Governance.BasicMotionAmendment":
                return(MotionAmendment.FromBasic((BasicMotionAmendment)basic));


            // ------------ PARLEY/ACTIVISM CLASSES ------------

            case "Swarmops.Basic.Types.BasicExternalActivity":
                return(ExternalActivity.FromBasic((BasicExternalActivity)basic));

            case "Swarmops.Basic.Types.BasicParley":
                return(Parley.FromBasic((BasicParley)basic));

            case "Swarmops.Basic.Types.BasicParleyAttendee":
                return(ParleyAttendee.FromBasic((BasicParleyAttendee)basic));

            case "Swarmops.Basic.Types.BasicParleyOption":
                return(ParleyOption.FromBasic((BasicParleyOption)basic));

            case "Swarmops.Basic.Types.BasicPerson":
                return(Person.FromBasic((BasicPerson)basic));

            // ------------------ FAIL ----------------

            default:
                throw new NotImplementedException("Unimplemented argument type: " + argumentType);
            }
        }
Esempio n. 8
0
    protected void ImportBgmaxText(string text)
    {
        string[] lines = text.Split("\r\n".ToCharArray());

        DateTime timestamp    = DateTime.MinValue;
        int      bgMaxVersion = 0;

        PaymentGroup curPaymentGroup            = null;
        Payment      curPayment                 = null;
        Currency     currency                   = null;
        Int64        curPaymentGroupAmountCents = 0;

        foreach (string line in lines)
        {
            if (line.Length < 2)
            {
                continue; // CR/LF split causes every other line to be empty
            }

            switch (line.Substring(0, 2))
            {
            case "01":     // BGMAX intro
                string bgmaxmarker = line.Substring(2, 20).Trim();
                if (bgmaxmarker != "BGMAX")
                {
                    throw new Exception("bad format -- not bgmax");
                }
                bgMaxVersion = Int32.Parse(line.Substring(22, 2));
                timestamp    = DateTime.ParseExact(line.Substring(24, 20), "yyyyMMddHHmmssffffff",
                                                   CultureInfo.InvariantCulture);
                break;

            case "05":     // Begin payment group
                if (bgMaxVersion < 1)
                {
                    throw new InvalidOperationException("BGMax record must precede first payment group");
                }
                currency                   = Currency.FromCode(line.Substring(22, 3));
                curPaymentGroup            = PaymentGroup.Create(Organization.PPSE, timestamp, currency, _currentUser);
                curPaymentGroupAmountCents = 0;
                break;

            case "20":     // Begin payment
                if (curPaymentGroup == null)
                {
                    throw new InvalidOperationException("Payment group start must precede first payment");
                }
                string fromAccount = line.Substring(2, 10);
                string reference   = line.Substring(12, 25).Trim();   // left space padded in BgMax format
                Int64  amountCents = Int64.Parse(line.Substring(37, 18), CultureInfo.InvariantCulture);
                string key         = "SEBGM" + DateTime.Today.Year.ToString() + line.Substring(57, 12);
                bool   hasImage    = (line[69] == '1' ? true : false);

                // TODO: Check if existed already -- must do -- IMPORTANT

                curPayment = curPaymentGroup.CreatePayment(amountCents, reference, fromAccount, key, hasImage);
                curPaymentGroupAmountCents += amountCents;
                break;

            case "25":     // Payment info: Freeform
                if (curPayment == null)
                {
                    throw new InvalidOperationException("Payment start must precede payment information");
                }
                curPayment.AddInformation(PaymentInformationType.Freeform, line.Substring(2, 50).Trim());
                break;

            case "26":     // Payment info: Name
                if (curPayment == null)
                {
                    throw new InvalidOperationException("Payment start must precede payment information");
                }
                curPayment.AddInformation(PaymentInformationType.Name, line.Substring(2, 35).Trim());
                break;

            case "27":     // Payment info: Street, postal code
                if (curPayment == null)
                {
                    throw new InvalidOperationException("Payment start must precede payment information");
                }
                curPayment.AddInformation(PaymentInformationType.Street, line.Substring(2, 35).Trim());
                curPayment.AddInformation(PaymentInformationType.PostalCode, line.Substring(37, 9).Replace(" ", ""));     // also removes inspace
                break;

            case "28":     // Payment info: City, Country
                if (curPayment == null)
                {
                    throw new InvalidOperationException("Payment start must precede payment information");
                }
                curPayment.AddInformation(PaymentInformationType.City, line.Substring(2, 35).Trim());
                curPayment.AddInformation(PaymentInformationType.Country, line.Substring(37, 35).Trim());
                curPayment.AddInformation(PaymentInformationType.CountryCode, line.Substring(72, 2).Trim());
                break;

            case "29":     // Payment info: City, Country
                if (curPayment == null)
                {
                    throw new InvalidOperationException("Payment start must precede payment information");
                }
                curPayment.AddInformation(PaymentInformationType.OrgNumber, line.Substring(2, 12).Trim());
                break;

            case "15":     // End payment group
                if (curPaymentGroup == null)
                {
                    throw new InvalidOperationException("Payment group start must precede payment group end");
                }
                string tag = DateTime.Today.Year.ToString() + line.Substring(45, 5);
                Int64  reportedAmountCents = Int64.Parse(line.Substring(50, 18), CultureInfo.InvariantCulture);
                curPaymentGroup.AmountCents = curPaymentGroupAmountCents;
                curPaymentGroup.Tag         = tag;
                curPaymentGroup.Open        = true; // flags payment group as ready

                curPaymentGroup.MapTransaction();

                curPaymentGroup = null;
                break;

            case "70":     // BGMAX termination
                break;     // don't care

            default:
                break;     // don't care about other fields
            }
        }

        ScriptManager.RegisterStartupScript(this, Page.GetType(), "alldone",
                                            "alert ('Timestamp: " + timestamp.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "');",
                                            true);
    }
    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;
    }
Esempio n. 10
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);
        }