protected void ButtonInvoice_Click(object sender, EventArgs e)
    {
        if (_authority.HasPermission(Permission.CanPayOutMoney, Organization.PPSEid, -1, Authorization.Flag.ExactOrganization))
        {
            foreach (string indexString in this.GridDebts.SelectedIndexes)
            {
                // Creating the invoice closes the expense claims.

                int      index           = Int32.Parse(indexString);
                string   protoIdentity   = (string)this.GridDebts.MasterTableView.DataKeyValues[index]["ProtoIdentity"];
                string[] identityStrings = protoIdentity.Split(',');

                ExpenseClaim template = ExpenseClaim.FromIdentity(Int32.Parse(identityStrings[0]));

                CultureInfo oldCulture = CultureInfo.CurrentUICulture;
                Thread.CurrentThread.CurrentUICulture = new CultureInfo(Organization.PPSE.DefaultCountry.Culture);

                OutboundInvoice invoice = OutboundInvoice.Create(Organization.PPSE, _currentUser,
                                                                 DateTime.Today.AddDays(30),
                                                                 Organization.PPSE.FinancialAccounts.DebtsExpenseClaims,
                                                                 template.Claimer.Name,
                                                                 template.Claimer.Email,
                                                                 template.Claimer.Street + "\r\n" +
                                                                 template.Claimer.PostalCodeAndCity,
                                                                 Organization.PPSE.DefaultCountry.Currency,
                                                                 true, GetLocalResourceObject("InvoiceLiterals.ReclaimedCashAdvance").ToString());

                Thread.CurrentThread.CurrentUICulture = oldCulture;

                foreach (string idString in identityStrings)
                {
                    int          claimId = Int32.Parse(idString);
                    ExpenseClaim claim   = ExpenseClaim.FromIdentity(claimId);

                    invoice.AddItem("Exp #" + claim.Identity.ToString() + ": " + claim.Description, -claim.AmountCents);
                    claim.Repaid = true;
                    claim.Open   = false;
                }

                // Create transaction

                FinancialTransaction transaction = FinancialTransaction.Create(Organization.PPSEid, DateTime.Now,
                                                                               "Outbound Invoice #" + invoice.Identity +
                                                                               ": " + template.ClaimerCanonical);
                transaction.AddRow(Organization.PPSE.FinancialAccounts.DebtsExpenseClaims, -invoice.AmountCents, _currentUser);
                transaction.AddRow(Organization.PPSE.FinancialAccounts.AssetsOutboundInvoices, invoice.AmountCents, _currentUser);

                transaction.Dependency = invoice;

                // Create event

                Activizr.Logic.Support.PWEvents.CreateEvent(EventSource.PirateWeb, EventType.OutboundInvoiceCreated,
                                                            _currentUser.Identity, 1, 1, 0, invoice.Identity,
                                                            protoIdentity);
            }

            PopulateGrid();
            this.GridDebts.Rebind();
        }
    }
Exemple #2
0
    protected void ChargeBudget(FinancialAccount budget, double amount, string comment)
    {
        FinancialTransaction newTransaction = FinancialTransaction.Create(1, DateTime.Now, comment);

        newTransaction.AddRow(budget, amount, _currentUser);
        newTransaction.AddRow(Organization.FromIdentity(Organization.PPSEid).FinancialAccounts.CostsInfrastructure, -amount, _currentUser);
    }
Exemple #3
0
        public override void Run()
        {
            HotBitcoinAddressUnspent utxoToReturn = HotBitcoinAddressUnspent.FromIdentity(UtxoIdentity);
            HotBitcoinAddress        utxoAddress  = utxoToReturn.Address;
            BitcoinSecret            secretKey    = utxoAddress.PrivateKey;

            // TODO: Verify that the utxoAddress is an EchoTest address, i.e. has second path component == BitcoinUtility.BitcoinEchoTestIndex

            string returnAddress = BitcoinUtility.GetInputAddressesForTransaction(BitcoinChain.Cash, utxoToReturn.TransactionHash)[0]; // assumes at least one input address -- not coinbase

            // Return the money

            BitcoinTransactionInputs inputs = utxoToReturn.AsInputs;
            Int64 satoshisToReturn          = utxoToReturn.AmountSatoshis;

            Coin[]    coins       = inputs.Coins;
            ICoin[]   iCoins      = coins;
            ISecret[] privateKeys = utxoToReturn.AsInputs.PrivateKeys;

            TransactionBuilder txBuilder = new TransactionBuilder();

            txBuilder = txBuilder.SendFees(new Satoshis(BitcoinUtility.EchoFeeSatoshis));
            txBuilder = txBuilder.AddCoins(iCoins);
            txBuilder = txBuilder.AddKeys(privateKeys);

            if (returnAddress.StartsWith("1"))
            {
                txBuilder = txBuilder.Send(new BitcoinPubKeyAddress(returnAddress),
                                           new Satoshis(utxoToReturn.AmountSatoshis - BitcoinUtility.EchoFeeSatoshis));
            }
            else if (returnAddress.StartsWith("3"))
            {
                txBuilder = txBuilder.Send(new BitcoinScriptAddress(returnAddress, Network.Main),
                                           new Satoshis(utxoToReturn.AmountSatoshis - BitcoinUtility.EchoFeeSatoshis));
            }
            else
            {
                throw new ArgumentException("Unrecognized address format");
            }

            Transaction tx = txBuilder.BuildTransaction(true, SigHash.ForkId | SigHash.All);

            BitcoinUtility.BroadcastTransaction(tx, BitcoinChain.Cash);
            utxoToReturn.Delete();
            utxoAddress.UpdateTotal();

            // Update the ledger

            string tx2Description          = "Bitcoin echo test repayment";
            FinancialTransaction ledgerTx2 = FinancialTransaction.Create(this.Organization,
                                                                         DateTime.UtcNow, tx2Description);

            ledgerTx2.AddRow(this.Organization.FinancialAccounts.DebtsOther, satoshisToReturn, this.Person);
            ledgerTx2.AddRow(this.Organization.FinancialAccounts.AssetsBitcoinHot, -satoshisToReturn, this.Person);
            ledgerTx2.BlockchainHash = tx.GetHash().ToString();
        }
Exemple #4
0
        public void CloseBudget(Person closingPerson)
        {
            Int64 remainingFunds = -Budget.GetDeltaCents(CreatedDateTime, DateTime.Now);

            FinancialTransaction transaction = FinancialTransaction.Create(OrganizationId,
                                                                           DateTime.Now,
                                                                           "Closing conference #" + Identity);

            transaction.AddRow(Budget, remainingFunds, closingPerson);
            transaction.AddRow(Budget.Parent, -remainingFunds, closingPerson);
        }
Exemple #5
0
        public void SendInvoice()
        {
            DateTime invoiceDue    = DateTime.Today.AddDays(14);
            DateTime maxInvoiceDue = this.Parley.StartDate.AddDays(-10);

            if (invoiceDue > maxInvoiceDue)
            {
                invoiceDue = maxInvoiceDue;
            }

            OutboundInvoice invoice = OutboundInvoice.Create(this.Parley.Organization, this.Parley.Person, invoiceDue,
                                                             this.Parley.Budget, this.Person.Name, this.Person.Mail,
                                                             string.Empty,
                                                             this.Parley.Organization.DefaultCountry.Currency, true,
                                                             string.Empty);

            invoice.AddItem("Deltagarkostnad " + this.Parley.Name, this.Parley.AttendanceFeeCents);  // TODO: Localize

            ParleyOptions options = this.Options;

            foreach (ParleyOption option in options)
            {
                invoice.AddItem(option.Description, option.AmountCents);
            }


            // Create the financial transaction with rows

            FinancialTransaction transaction =
                FinancialTransaction.Create(this.Parley.OrganizationId, DateTime.Now,
                                            "Outbound Invoice #" + invoice.Identity + " to " + this.Person.Name);

            transaction.AddRow(this.Parley.Organization.FinancialAccounts.AssetsOutboundInvoices, invoice.AmountCents, null);
            transaction.AddRow(this.Parley.Budget, -invoice.AmountCents, null);

            // Make the transaction dependent on the outbound invoice

            transaction.Dependency = invoice;

            // Create the event for PirateBot-Mono to send off mails

            PWEvents.CreateEvent(EventSource.PirateWeb, EventType.OutboundInvoiceCreated,
                                 this.PersonId, this.Parley.OrganizationId, 1, this.PersonId,
                                 invoice.Identity, string.Empty);

            // Update the attendee as invoiced

            base.Invoiced          = true;
            base.OutboundInvoiceId = invoice.Identity;

            SwarmDb.GetDatabaseForWriting().SetParleyAttendeeInvoiced(this.Identity, invoice.Identity);
        }
Exemple #6
0
        public static void MatchTransactionOpenVatReport(int transactionId, int vatReportId)
        {
            if (transactionId == 0 || vatReportId == 0)
            {
                return;
            }

            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (
                !authData.Authority.HasAccess(new Access(authData.CurrentOrganization,
                                                         AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            FinancialTransaction transaction = FinancialTransaction.FromIdentity(transactionId);
            VatReport            vatReport   = VatReport.FromIdentity(vatReportId);

            if (authData.CurrentOrganization.Identity != transaction.OrganizationId ||
                authData.CurrentOrganization.Identity != vatReport.OrganizationId)
            {
                throw new InvalidOperationException("Organization mismatch");
            }

            Int64 diffCents = vatReport.VatInboundCents - vatReport.VatOutboundCents;

            if (transaction.Rows.AmountCentsTotal != diffCents)
            {
                throw new InvalidOperationException("Amount mismatch");
            }

            // Go ahead and close

            vatReport.CloseTransaction = transaction; // throws if already closed
            vatReport.Open             = false;

            if (diffCents > 0)
            {
                // Positive amount - asset account
                transaction.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsVatInboundReported, -diffCents,
                                   null);
            }
            else
            {
                // Negative amount - liability account
                transaction.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsVatOutboundReported, -diffCents,
                                   null);
            }
        }
        public static AjaxInputCallResult CreateTransaction(string dateTimeString, string amountString, string description,
                                                            int budgetId)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture();
            FinancialAccount   budget   = FinancialAccount.FromIdentity(budgetId);

            if (budget.OrganizationId != authData.CurrentOrganization.Identity)
            {
                throw new UnauthorizedAccessException();
            }
            if (!authData.Authority.HasAccess(new Access(authData.CurrentOrganization, AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            Int64 amountCents = Formatting.ParseDoubleStringAsCents(amountString);

            DateTime txTime = DateTime.Parse(dateTimeString);

            // TODO: Return better error codes/messages if one of these fail

            FinancialTransaction transaction = FinancialTransaction.Create(authData.CurrentOrganization, txTime,
                                                                           description);

            transaction.AddRow(budget, amountCents, authData.CurrentUser);

            AjaxInputCallResult result = new AjaxInputCallResult
            {
                Success        = true,
                ObjectIdentity = transaction.Identity,
                NewValue       = (amountCents / 100.0).ToString("N2")
            };

            return(result);
        }
Exemple #8
0
        public static AjaxCallResult ResyncSatoshisInLedger(string itemId)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (!authData.Authority.HasAccess(new Access(authData.CurrentOrganization, AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            Int64 cashSatoshisInLedger =
                authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot.GetForeignCurrencyBalanceDeltaCents(
                    Constants.DateTimeLow, Constants.DateTimeHigh).Cents;

            Int64 cashSatoshisInHotwallet =
                HotBitcoinAddresses.GetSatoshisInHotwallet(authData.CurrentOrganization)[BitcoinChain.Cash];

            Int64 adjustment = cashSatoshisInHotwallet - cashSatoshisInLedger;  // positive if ledger needs upward adjustment

            FinancialTransaction adjustmentTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                            DateTime.UtcNow, Resources.Pages.Ledgers.EndOfMonth_LedgerBitcoinBalanceTransactionDescription);

            adjustmentTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, 0, authData.CurrentUser).AmountForeignCents = new Money(adjustment, Currency.BitcoinCash);

            return(new AjaxCallResult
            {
                Success = true,
                DisplayMessage =
                    String.Format(Resources.Pages.Ledgers.EndOfMonth_Dialog_LedgerBitcoinBalanceMismatch,
                                  cashSatoshisInHotwallet / 100.0, cashSatoshisInLedger / 100.0)
            });
        }
Exemple #9
0
    protected void ButtonCreateTransaction_Click(object sender, EventArgs e)
    {
        if (!_authority.HasPermission(Permission.CanDoEconomyTransactions, _organizationId, -1, Authorization.Flag.ExactOrganization))
        {
            ScriptManager.RegisterStartupScript(this, Page.GetType(), "getlost",
                                                "alert ('You do not have access to financial records.');", true);
            return;
        }

        int accountId = Int32.Parse(this.DropAccountsCreate.SelectedValue);

        if (accountId == 0)
        {
            ScriptManager.RegisterStartupScript(this, Page.GetType(), "getlost",
                                                "alert ('Please select an account.');", true);
            return;
        }

        double   amount              = Double.Parse(this.TextAmountCreate.Text, new CultureInfo("sv-SE"));
        string   description         = this.TextDescriptionCreate.Text;
        DateTime transactionDateTime = (DateTime)this.DateCreate.SelectedDate;

        FinancialTransaction transaction = FinancialTransaction.Create(Organization.PPSEid, transactionDateTime, description);

        transaction.AddRow(FinancialAccount.FromIdentity(accountId), amount, _currentUser);

        // As the RadWindowManager and RadAjaxUpdate are part of the UpdatePanel we're rewriting, we
        // need to make the client call the function only when the ajax call has completed. We set
        // 200ms for this, but pretty much any amount of time should be ok, as long as it's delayed
        // past the actual ajax rewrite.

        ScriptManager.RegisterStartupScript(this, Page.GetType(), "finishthejob",
                                            "ShowTransactionFormDelayed (" + transaction.Identity + ");", true);
    }
    protected void ButtonExecute_Click(object sender, EventArgs e)
    {
        if (!_authority.HasPermission(Permission.CanDoEconomyTransactions, thisInvoice.OrganizationId, -1, Authorization.Flag.ExactOrganization))
        {
            throw new UnauthorizedAccessException("Access Denied");
        }

        if (this.RadioManualMap.Checked)
        {
            int transactionId = Int32.Parse(this.DropTransactions.SelectedValue);
            FinancialTransaction transaction = FinancialTransaction.FromIdentity(transactionId);

            Payment payment = Payment.CreateSingle(thisInvoice.Organization, transaction.DateTime, thisInvoice.Currency, thisInvoice.AmountCents, thisInvoice, _currentUser);
            payment.AddInformation(PaymentInformationType.Freeform, "Mapped manually in PirateWeb");

            transaction.AddRow(thisInvoice.Organization.FinancialAccounts.AssetsOutboundInvoices, -thisInvoice.AmountCents, _currentUser);
            transaction.Dependency = payment.Group;
        }
        else if (this.RadioCreditInvoice.Checked)
        {
            thisInvoice.Credit(_currentUser);
        }

        ClientScript.RegisterStartupScript(Page.GetType(), "mykey", "CloseAndRebind();", true);
    }
Exemple #11
0
        public static void BalanceTransactionManually(int transactionId, int accountId)
        {
            if (transactionId == 0 || accountId == 0)
            {
                return;
            }

            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (
                !authData.Authority.HasAccess(new Access(authData.CurrentOrganization,
                                                         AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            FinancialTransaction transaction = FinancialTransaction.FromIdentity(transactionId);
            FinancialAccount     account     = FinancialAccount.FromIdentity(accountId);

            if (transaction.OrganizationId != authData.CurrentOrganization.Identity || account.OrganizationId != authData.CurrentOrganization.Identity)
            {
                throw new UnauthorizedAccessException();
            }

            transaction.AddRow(account, -transaction.Rows.AmountCentsTotal, authData.CurrentUser);
        }
Exemple #12
0
        public static void MatchTransactionOpenPayout(int transactionId, int payoutId)
        {
            if (transactionId == 0 || payoutId == 0)
            {
                return;
            }

            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (
                !authData.Authority.HasAccess(new Access(authData.CurrentOrganization,
                                                         AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            FinancialTransaction transaction = FinancialTransaction.FromIdentity(transactionId);
            Payout payout = Payout.FromIdentity(payoutId);

            if (transaction.OrganizationId != authData.CurrentOrganization.Identity || payout.OrganizationId != authData.CurrentOrganization.Identity)
            {
                throw new UnauthorizedAccessException();
            }

            Int64 transactionCents = transaction.Rows.AmountCentsTotal;
            Int64 payoutCents      = payout.AmountCents;

            FinancialAccount forexSpillAccount =
                authData.CurrentOrganization.FinancialAccounts.IncomeCurrencyFluctuations;

            if (forexSpillAccount == null && payoutCents != -transactionCents)                           // the tx-negative is because it's a payout
            {
                throw new InvalidOperationException("Need forex gain/loss accounts for this operation"); // TODO: Autocreate?
            }

            if ((-transactionCents) > payoutCents)  // the tx-negative is because it's a payout
            {
                // This is a forex loss, not a gain which is the default
                forexSpillAccount = authData.CurrentOrganization.FinancialAccounts.CostsCurrencyFluctuations;
            }

            if (-transactionCents != payoutCents)
            {
                // Forex adjust
                transaction.AddRow(forexSpillAccount, -(payoutCents + transactionCents),
                                                          // plus because transactionCents is negative
                                   authData.CurrentUser); // Adds the forex adjustment so we can bind payout to tx and close
            }

            // The amounts should match now

            if (transaction.Rows.AmountCentsTotal != -payout.AmountCents)
            {
                throw new InvalidOperationException();
            }


            payout.BindToTransactionAndClose(transaction, authData.CurrentUser);
        }
Exemple #13
0
        public static void MatchTransactionOpenPayoutForeign(int transactionId, int payoutId)
        {
            // This is like the non-foreign version except this one chalks up the difference to forex gain/loss accounts

            if (transactionId == 0 || payoutId == 0)
            {
                return;
            }

            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (
                !authData.Authority.HasAccess(new Access(authData.CurrentOrganization,
                                                         AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            FinancialAccount forexSpillAccount =
                authData.CurrentOrganization.FinancialAccounts.IncomeCurrencyFluctuations;

            if (forexSpillAccount == null)
            {
                throw new InvalidOperationException("Need forex gain/loss accounts for this operation");  // TODO: Autocreate?
            }

            FinancialTransaction transaction = FinancialTransaction.FromIdentity(transactionId);
            Payout payout = Payout.FromIdentity(payoutId);

            if (transaction.OrganizationId != authData.CurrentOrganization.Identity || payout.OrganizationId != authData.CurrentOrganization.Identity)
            {
                throw new UnauthorizedAccessException();
            }

            if (-transaction.Rows.AmountCentsTotal > payout.AmountCents)
            {
                // This is a forex loss, not a gain which is the default
                forexSpillAccount = authData.CurrentOrganization.FinancialAccounts.CostsCurrencyFluctuations;
            }

            transaction.AddRow(forexSpillAccount, -(payout.AmountCents + transaction.Rows.AmountCentsTotal), // plus because AmountCentsTotal is negative
                               authData.CurrentUser);                                                        // Adds the forex adjustment so we can bind payout to tx and close

            if (transaction.Rows.AmountCentsTotal != -payout.AmountCents)
            {
                throw new InvalidOperationException();
            }

            payout.BindToTransactionAndClose(transaction, authData.CurrentUser);
        }
    protected void ButtonAutoBalance_Click(object sender, EventArgs e)
    {
        Person    currentUser = Person.FromIdentity(Int32.Parse(HttpContext.Current.User.Identity.Name));
        Authority authority   = currentUser.GetAuthority();

        if (!authority.HasPermission(Permission.CanDoEconomyTransactions, Int32.Parse(this.DropOrganizations.SelectedValue), -1, Authorization.Flag.ExactOrganization))
        {
            ScriptManager.RegisterStartupScript(this, Page.GetType(), "validationfailed",
                                                "alert ('You do not have access to changing financial records.');",
                                                true);
            return;
        }


        if (this.DropAutoBalanceAccount.SelectedValue == "0")
        {
            ScriptManager.RegisterStartupScript(this, Page.GetType(), "validationfailed",
                                                "alert ('Please select an account to auto-balance against.');",
                                                true);
            return;
        }

        FinancialAccount autoBalanceAccount = FinancialAccount.FromIdentity(Int32.Parse(this.DropAutoBalanceAccount.SelectedValue));

        if (this.GridTransactions.SelectedIndexes.Count == 0)
        {
            ScriptManager.RegisterStartupScript(this, Page.GetType(), "validationfailed",
                                                "alert ('Please select one or more transactions to auto-balance.');",
                                                true);

            return;
        }

        foreach (string indexString in this.GridTransactions.SelectedIndexes)
        {
            int index         = Int32.Parse(indexString);
            int transactionId = (int)this.GridTransactions.MasterTableView.DataKeyValues[index]["Identity"];

            FinancialTransaction transaction = FinancialTransaction.FromIdentity(transactionId);

            transaction.AddRow(autoBalanceAccount, -transaction.Rows.AmountCentsTotal, currentUser);
        }

        PopulateGrid();
        this.GridTransactions.Rebind();
        this.DropAutoBalanceAccount.SelectedValue = "0";
    }
Exemple #15
0
        public static bool AddTransactionRow(int txId, int accountId, string amountString)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (
                !authData.Authority.HasAccess(new Access(authData.CurrentOrganization,
                                                         AccessAspect.Bookkeeping, AccessType.Write)))
            {
                return(false); // fail
            }

            FinancialTransaction transaction = FinancialTransaction.FromIdentity(txId);

            if (authData.CurrentOrganization.Parameters.FiscalBooksClosedUntilYear >= transaction.DateTime.Year)
            {
                return(false); // can't edit closed books
            }

            FinancialAccount account = FinancialAccount.FromIdentity(accountId);

            Double amountFloat = Double.Parse(amountString);
            Int64  amountCents = (Int64)(amountFloat * 100.0);

            if (account.OrganizationId != authData.CurrentOrganization.Identity)
            {
                throw new InvalidOperationException("Account/Organization mismatch");
            }

            if (amountCents == 0)
            {
                return(false);
            }

            transaction.AddRow(account, amountCents, authData.CurrentUser);

            return(true);
        }
Exemple #16
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 #17
0
    protected void ButtonSubmitInvoice_Click(object sender, EventArgs e)
    {
        // If args were invalid, abort

        if (!Page.IsValid)
        {
            return;
        }

        if (this.TextNewItemDescription.Text.Length > 0)
        {
            AddItemsInEdit(false);
        }

        // Read the form data

        string           customer       = this.TextCustomer.Text;
        string           paperAddress   = this.TextPaperAddress.Text;
        string           mailAddress    = this.TextMailAddress.Text;
        int              organizationId = Int32.Parse(this.DropOrganizations.SelectedValue);
        bool             domestic       = this.CheckDomestic.Checked;
        string           theirReference = this.TextTheirReference.Text;
        FinancialAccount budget         = this.DropBudgets.SelectedFinancialAccount;
        DateTime         created        = DateTime.Now;
        DateTime         dueDate        = (DateTime)this.DatePicker.SelectedDate;
        string           items          = (string)ViewState["Items"];

        // If no line items, abort

        if (items.Length < 2)
        {
            Page.ClientScript.RegisterStartupScript(typeof(Page), "FailMessage", @"alert ('Add line items before sending the invoice.');", true);
            return;
        }

        // Create the invoice record

        OutboundInvoice newInvoice = OutboundInvoice.Create(Organization.FromIdentity(organizationId), _currentUser,
                                                            dueDate, budget, customer, mailAddress, paperAddress,
                                                            Currency.FromCode("SEK"), domestic, theirReference);

        // Add the invoice items

        string[] itemArray = items.Split('|');

        for (int index = 0; index < itemArray.Length; index += 2)
        {
            double amount = Double.Parse(itemArray[index + 1], CultureInfo.InvariantCulture);

            newInvoice.AddItem(itemArray[index], amount);
        }


        // Create the financial transaction with rows

        FinancialTransaction transaction =
            FinancialTransaction.Create(organizationId, created,
                                        "Outbound Invoice #" + newInvoice.Identity + " to " + customer);

        transaction.AddRow(Organization.FromIdentity(organizationId).FinancialAccounts.AssetsOutboundInvoices, newInvoice.AmountCents, _currentUser);
        transaction.AddRow(budget, -newInvoice.AmountCents, _currentUser);

        // Make the transaction dependent on the outbound invoice

        transaction.Dependency = newInvoice;

        // Create the event for PirateBot-Mono to send off mails

        Activizr.Logic.Support.PWEvents.CreateEvent(EventSource.PirateWeb, EventType.OutboundInvoiceCreated,
                                                    _currentUser.Identity, organizationId, 1, _currentUser.Identity,
                                                    newInvoice.Identity, string.Empty);

        Page.ClientScript.RegisterStartupScript(typeof(Page), "OkMessage", @"alert ('The outbound invoice was registered and will be sent shortly.');", true);

        // Clear the text fields

        this.TextCustomer.Text           = string.Empty;
        this.TextMailAddress.Text        = string.Empty;
        this.TextPaperAddress.Text       = string.Empty;
        this.TextTheirReference.Text     = string.Empty;
        ViewState["Items"]               = string.Empty;
        this.TextNewItemDescription.Text = string.Empty;
        this.TextNewItemAmount.Text      = "0,00";

        this.LiteralLeftItemSpacer.Text = "&nbsp;";
        this.LiteralItems.Text          = string.Empty;

        this.GridOutboundInvoices.Reload();
    }
    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;
    }
        protected void ButtonCreate_Click(object sender, EventArgs e)  // TODO
        {
            // The data has been validated client-side already. We'll throw unfriendly exceptions if invalid data is passed here.
            // People who choose to disable JavaScript and then submit bad input almost deserve to be hurt.

            Int64 amountCents    = this.CurrencyAmount.Cents;
            Int64 amountVatCents = this.CurrencyVat.Cents;

            string description = this.TextPurpose.Text;

            DateTime dueDate = DateTime.Parse(this.TextDueDate.Text);


            FinancialAccount budget = this.ComboBudgets.SelectedAccount;

            // sanity check

            if (budget.Organization.Identity != CurrentOrganization.Identity)
            {
                throw new InvalidOperationException("Budget-organization mismatch; won't file invoice");
            }


            // Get documents; check that documents have been uploaded

            Documents documents = Documents.RecentFromDescription(this.FileUpload.GuidString);

            if (documents.Count == 0)
            {
                throw new InvalidOperationException("No documents uploaded");
            }

            OutboundInvoice newInvoice = OutboundInvoice.Create(CurrentOrganization, dueDate, budget, this.TextClient.Text, string.Empty, string.Empty, CurrentOrganization.Currency, false, this.TextReference.Text, CurrentUser);

            newInvoice.AddItem(this.TextPurpose.Text, amountCents);

            // TODO: VAT -- needs to be PER ITEM, and dbfields must update for this, quite a large work item, do not short circuit hack this

            documents.SetForeignObjectForAll(newInvoice);

            // Create financial transaction in the ledger (this logic should not be in the presentation layer at all, move it to a better OutboundInvoice.Create that takes OutboundInvoiceItems as parameter)

            FinancialTransaction txOut = FinancialTransaction.Create(CurrentOrganization, DateTime.UtcNow,
                                                                     "Outbound Invoice #" + newInvoice.OrganizationSequenceId.ToString("N0"));

            txOut.AddRow(CurrentOrganization.FinancialAccounts.AssetsOutboundInvoices, amountCents, CurrentUser);
            if (amountVatCents > 0)
            {
                txOut.AddRow(CurrentOrganization.FinancialAccounts.DebtsVatOutboundUnreported, -amountVatCents,
                             CurrentUser);
                txOut.AddRow(budget, -(amountCents - amountVatCents), CurrentUser); // Sales value
            }
            else
            {
                txOut.AddRow(budget, -amountCents, CurrentUser);
            }


            // Make the transaction dependent on the inbound invoice

            txOut.Dependency = newInvoice;

            // If invoice is denominated in a non-presentation currency, record the native values for proper payment

            if (this.CurrencyAmount.NonPresentationCurrencyUsed)
            {
                Money currencyEntered = this.CurrencyAmount.NonPresentationCurrencyAmount;
                newInvoice.NativeCurrencyAmount = currencyEntered;
            }

            // Display success message

            this._invoiceId = newInvoice.OrganizationSequenceId; // a property returns the localized string

            // Reset all fields for next invoice

            this.FileUpload.Reset();
            this.TextClient.Text      = String.Empty;
            this.TextPurpose.Text     = String.Empty;
            this.TextReference.Text   = String.Empty;
            this.CurrencyAmount.Cents = 0;
            this.CurrencyVat.Cents    = 0;
            this.TextDueDate.Text     = DateTime.Today.AddDays(30).ToShortDateString(); // Use current culture

            // the easyUI combo fields should reset automatically on form submission unless we explicitly reconstruct

            this.TextClient.Focus();
        }
Exemple #20
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);
    }
Exemple #21
0
        public static void MatchTransactionOpenOutboundInvoice(int transactionId, int invoiceId)
        {
            if (transactionId == 0 || invoiceId == 0)
            {
                return;
            }

            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (
                !authData.Authority.HasAccess(new Access(authData.CurrentOrganization,
                                                         AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            FinancialTransaction transaction     = FinancialTransaction.FromIdentity(transactionId);
            OutboundInvoice      outboundInvoice = OutboundInvoice.FromIdentity(invoiceId);

            if (transaction.OrganizationId != authData.CurrentOrganization.Identity || outboundInvoice.OrganizationId != authData.CurrentOrganization.Identity)
            {
                throw new UnauthorizedAccessException();
            }

            Int64 transactionCents = transaction.Rows.AmountCentsTotal;
            Int64 invoiceCents     = outboundInvoice.AmountCents;



            FinancialAccount forexSpillAccount =
                authData.CurrentOrganization.FinancialAccounts.IncomeCurrencyFluctuations;

            if (forexSpillAccount == null && invoiceCents != transactionCents)
            {
                throw new InvalidOperationException("Need forex gain/loss accounts for this operation");  // TODO: Autocreate?
            }

            if (transaction.Rows.AmountCentsTotal < outboundInvoice.AmountCents)
            {
                // This is a forex loss, not a gain which is the default
                forexSpillAccount = authData.CurrentOrganization.FinancialAccounts.CostsCurrencyFluctuations;
            }

            // Close invoice

            Payment payment = Payment.CreateSingle(authData.CurrentOrganization, transaction.DateTime,
                                                   authData.CurrentOrganization.Currency, outboundInvoice.AmountCents, outboundInvoice,
                                                   authData.CurrentUser);

            // TODO: Notify?

            // TODO: Log?

            // Close transaction

            if (transactionCents != invoiceCents)
            {
                transaction.AddRow(forexSpillAccount, invoiceCents - transactionCents,
                                   authData.CurrentUser); // Adds the forex adjustment so we can bind payout to tx and close
            }

            transaction.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsOutboundInvoices,
                               -outboundInvoice.AmountCents, authData.CurrentUser);
        }
Exemple #22
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);
        }
Exemple #23
0
        static public AjaxCallResult ProcessTransactionReceived(string guid, string txHash)
        {
            BitcoinChain chain = BitcoinChain.Cash;

            AuthenticationData authData = GetAuthenticationDataAndCulture(); // just to make sure we're called properly

            string bitcoinAddress = (string)GuidCache.Get(guid);

            if (BitcoinUtility.TestUnspents(chain, bitcoinAddress))
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddress.FromAddress(chain, bitcoinAddress).Unspents;

                // TODO: Update the HotBitcoinAddress with the new amount?

                HotBitcoinAddressUnspent unspent = null;
                Int64 satoshisReceived           = 0;

                foreach (HotBitcoinAddressUnspent potentialUnspent in unspents)
                {
                    if (potentialUnspent.TransactionHash == txHash)
                    {
                        satoshisReceived = potentialUnspent.AmountSatoshis;
                        unspent          = potentialUnspent;
                    }
                }

                if (unspent == null)  // Supplied transaction hash was not found in collection
                {
                    Debugger.Break(); // TODO: Something else than break the debugger
                }

                Swarmops.Logic.Financial.Money moneyReceived = new Swarmops.Logic.Financial.Money(satoshisReceived,
                                                                                                  Currency.BitcoinCash);

                // Make sure that the hotwallet native currency is bitcoin cash
                authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot.ForeignCurrency = Currency.BitcoinCash;

                // Create success message and ledger transaction
                string successMessage = string.Empty;

                FinancialTransaction testTransaction = null;
                try
                {
                    testTransaction = FinancialTransaction.FromBlockchainHash(authData.CurrentOrganization, txHash);

                    // We've already seen this donation! Something is seriously bogus here
                    Debugger.Break();
                    return(new AjaxCallResult()
                    {
                        DisplayMessage = successMessage, Success = true
                    });
                }
                catch (ArgumentException)
                {
                    // This exception is expected - the transaction should not yet exist
                }

                if (authData.CurrentOrganization.Currency.IsBitcoinCash)
                {
                    // The ledger is native bitcoin cash, so units are Satoshis

                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Donation (bitcoin to hotwallet)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.IncomeDonations, -satoshisReceived, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, satoshisReceived, authData.CurrentUser);
                    ledgerTx.BlockchainHash = txHash;

                    if (satoshisReceived % 100 == 0)
                    {
                        successMessage = string.Format(Resources.Pages.Financial.Donate_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N0"));
                    }
                    else
                    {
                        successMessage = string.Format(Resources.Pages.Financial.Donate_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N2"));
                    }
                }
                else
                {
                    // The ledger is NOT native bitcoin, so we'll need to convert currencies

                    long orgNativeCents           = moneyReceived.ToCurrency(authData.CurrentOrganization.Currency).Cents;
                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Donation (bitcoin to hotwallet)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.IncomeDonations, -orgNativeCents, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, orgNativeCents, authData.CurrentUser).AmountForeignCents = new Swarmops.Logic.Financial.Money(satoshisReceived, Currency.BitcoinCash);
                    ledgerTx.BlockchainHash = txHash;

                    successMessage = string.Format(Resources.Pages.Financial.Donate_FundsReceived,
                                                   authData.CurrentOrganization.Currency.DisplayCode, orgNativeCents / 100.0, satoshisReceived / 100.0);
                }

                return(new AjaxCallResult()
                {
                    DisplayMessage = successMessage, Success = true
                });

                // TODO: Ack donation via mail?
                // TODO: Notify CFO/etc of donation?
            }

            return(new AjaxCallResult()
            {
                Success = false
            });
        }
Exemple #24
0
        public void Attest(Person attester)
        {
            if (Attested)
            {
                return;
            }

            // If needed, create new account for parley

            FinancialAccount ourBudget;
            FinancialAccount parentBudget;
            int year = DateTime.Today.Year;

            if (base.BudgetId < 0) // no account created yet
            {
                ourBudget = FinancialAccount.Create(Budget.Organization,
                                                    "Conf: " + Name,
                                                    FinancialAccountType.Cost,
                                                    Budget);

                parentBudget = Budget;

                base.BudgetId   = ourBudget.Identity;
                ourBudget.Owner = Person;
                SwarmDb.GetDatabaseForWriting().SetParleyBudget(Identity, ourBudget.Identity);
            }
            else
            {
                // The budget has been created already - we should already be initialized. Verify this
                // by checking that we were already attested once.

                if (!AttestedOnce)
                {
                    throw new InvalidOperationException(
                              "Budget exists despite parley not having been attested. This should not be possible.");
                }

                ourBudget    = Budget;
                parentBudget = ParentBudget;
            }

            ourBudget.SetBudgetCents(DateTime.Today.Year, -BudgetCents);
            parentBudget.SetBudgetCents(DateTime.Today.Year,
                                        parentBudget.GetBudgetCents(year) + BudgetCents); // cost budgets are negative

            // Reserve the guarantee money

            FinancialTransaction guaranteeFundsTx = FinancialTransaction.Create(OrganizationId, DateTime.Now,
                                                                                "Conference #" +
                                                                                Identity + " Guarantee");

            guaranteeFundsTx.AddRow(Budget, -GuaranteeCents, attester);
            guaranteeFundsTx.AddRow(Budget.Parent, GuaranteeCents, attester);

            // Finally, set as attested

            PWEvents.CreateEvent(
                EventSource.PirateWeb, EventType.ParleyAttested, attester.Identity,
                OrganizationId, 0, 0, Identity, string.Empty);

            base.Attested = true;
            SwarmDb.GetDatabaseForWriting().SetParleyAttested(Identity, true);
            SwarmDb.GetDatabaseForWriting().CreateFinancialValidation(FinancialValidationType.Attestation,
                                                                      FinancialDependencyType.Parley, Identity,
                                                                      DateTime.Now, attester.Identity, (double)(GuaranteeDecimal));
        }
        static public AjaxCallResult ProcessTransactionReceived(string guid, string txHash)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture(); // just to make sure we're called properly
            BitcoinChain       chain    = BitcoinChain.Cash;

            string bitcoinAddress = (string)GuidCache.Get(guid);

            if (BitcoinUtility.TestUnspents(chain, bitcoinAddress))
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddress.FromAddress(chain, bitcoinAddress).Unspents;
                Int64 satoshisReceived             = unspents.Last().AmountSatoshis;

                if (unspents.Last().TransactionHash != txHash && txHash.Length > 0)
                {
                    // Race condition.
                    Debugger.Break();
                }

                HotBitcoinAddressUnspent utxoToReturn = unspents.Last();

                Swarmops.Logic.Financial.Money moneyReceived = new Swarmops.Logic.Financial.Money(satoshisReceived,
                                                                                                  Currency.BitcoinCash);

                // Create success message and ledger transaction
                string successMessage = string.Empty;

                // TODO: Get the tx, get the input

                string returnAddress = BitcoinUtility.GetInputAddressesForTransaction(chain, txHash) [0]; // assumes at least one input address

                // Return the money, too. Set fee for a 300-byte transaction.

                ReturnBitcoinEchoUtxoOrder backendOrder = new ReturnBitcoinEchoUtxoOrder(utxoToReturn);
                backendOrder.Create(authData.CurrentOrganization, authData.CurrentUser);

                string tx1Description = "Bitcoin technical echo test (will be repaid immediately)";


                if (authData.CurrentOrganization.Currency.IsBitcoinCash)
                {
                    // The ledger is native bitcoin, so cent units are satoshis

                    FinancialTransaction ledgerTx1 = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                 DateTime.UtcNow, tx1Description);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -(satoshisReceived), authData.CurrentUser);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, satoshisReceived, authData.CurrentUser);
                    ledgerTx1.BlockchainHash = txHash;

                    // The return payment will be logged when made, so its hash can be recorded

                    if (satoshisReceived % 100 == 0)
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N0"));
                    }
                    else
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N2"));
                    }
                }
                else
                {
                    // The ledger is NOT native bitcoin, so we'll need to convert currencies

                    long orgNativeCents            = moneyReceived.ToCurrency(authData.CurrentOrganization.Currency).Cents;
                    FinancialTransaction ledgerTx1 = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                 DateTime.UtcNow, tx1Description);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -orgNativeCents, authData.CurrentUser);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, orgNativeCents, authData.CurrentUser).AmountForeignCents = new Swarmops.Logic.Financial.Money(satoshisReceived, Currency.BitcoinCash);
                    ledgerTx1.BlockchainHash = txHash;

                    // The second transaction is logged when executed in the back-end order

                    successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceived,
                                                   authData.CurrentOrganization.Currency.DisplayCode, orgNativeCents / 100.0, satoshisReceived / 100.0);
                }

                return(new AjaxCallResult()
                {
                    DisplayMessage = successMessage, Success = true
                });

                // TODO: Ack donation via mail?
                // TODO: Notify CFO/etc of donation?
            }

            return(new AjaxCallResult()
            {
                Success = false
            });
        }
        static public AjaxCallResult CheckTransactionReceived(string guid, string txHash)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture(); // just to make sure we're called properly

            string bitcoinAddress = (string)GuidCache.Get(guid);

            if (BitcoinUtility.TestUnspents(bitcoinAddress))
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddress.FromAddress(bitcoinAddress).Unspents;

                // TODO: Update the HotBitcoinAddress with the new amount?

                Int64 satoshisReceived = unspents.Last().AmountSatoshis;

                if (unspents.Last().TransactionHash != txHash && txHash.Length > 0)
                {
                    // Race condition.
                    Debugger.Break();
                }

                Swarmops.Logic.Financial.Money moneyReceived = new Swarmops.Logic.Financial.Money(satoshisReceived,
                                                                                                  Currency.Bitcoin);

                // Make sure that the hotwallet native currency is bitcoin
                authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot.ForeignCurrency = Currency.Bitcoin;

                // Create success message and ledger transaction
                string successMessage = string.Empty;

                // TODO: Get the tx, get the input

                string returnAddress = BitcoinUtility.GetInputAddressesForTransaction(txHash) [0]; // assumes at least one input address

                if (authData.CurrentOrganization.Currency.IsBitcoin)
                {
                    // The ledger is native bitcoin, so units are Satoshis

                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Bitcoin echo test (will be repaid immediately)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -satoshisReceived, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, satoshisReceived, authData.CurrentUser);
                    ledgerTx.BlockchainHash = txHash;

                    if (satoshisReceived % 100 == 0)
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N0"));
                    }
                    else
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N2"));
                    }

                    // TODO: Log the payout back, as an inbound invoice for immediate payout
                }
                else
                {
                    // The ledger is NOT native bitcoin, so we'll need to convert currencies

                    long orgNativeCents           = moneyReceived.ToCurrency(authData.CurrentOrganization.Currency).Cents;
                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Bitcoin echo test (will be repaid immediately)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -orgNativeCents, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, orgNativeCents, authData.CurrentUser).AmountForeignCents = new Swarmops.Logic.Financial.Money(satoshisReceived, Currency.Bitcoin);
                    ledgerTx.BlockchainHash = txHash;

                    successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceived,
                                                   authData.CurrentOrganization.Currency.DisplayCode, orgNativeCents / 100.0, satoshisReceived / 100.0);

                    // TODO: Create a payout back for this amount -- needs to be specified in bitcoin -- as an inbound invoice
                }

                return(new AjaxCallResult()
                {
                    DisplayMessage = successMessage, Success = true
                });

                // TODO: Ack donation via mail?
                // TODO: Notify CFO/etc of donation?
            }

            return(new AjaxCallResult()
            {
                Success = false
            });
        }
Exemple #27
0
        public static AjaxCallResult MarkDirectPurchase(int transactionId, int budgetId, string vatAmountString,
                                                        string newDescription, string guid)
        {
            if (transactionId == 0 | budgetId == 0)
            {
                return(new AjaxCallResult {
                    Success = false
                });
            }

            AuthenticationData authData = GetAuthenticationDataAndCulture();

            if (
                !authData.Authority.HasAccess(new Access(authData.CurrentOrganization,
                                                         AccessAspect.BookkeepingDetails)))
            {
                throw new UnauthorizedAccessException();
            }

            FinancialAccount budget = FinancialAccount.FromIdentity(budgetId);

            if (authData.CurrentOrganization.Identity != budget.Organization.Identity)
            {
                throw new UnauthorizedAccessException();
            }

            newDescription = newDescription.Trim();
            if (newDescription.Length < 1)
            {
                return(new AjaxCallResult
                {
                    Success = false,
                    DisplayMessage = Resources.Pages.Ledgers.BalanceTransactions_Error_NeedDescription
                });
            }

            Documents docs = Documents.RecentFromDescription(guid).WhereNotAssociated;

            if (docs.Count < 1)
            {
                return(new AjaxCallResult
                {
                    Success = false,
                    DisplayMessage = Resources.Pages.Ledgers.BalanceTransactions_Error_NeedDocumentation
                });
            }

            Int64 vatCents   = 0;
            bool  vatEnabled = authData.CurrentOrganization.VatEnabled;

            if (vatEnabled)
            {
                try
                {
                    vatCents = Swarmops.Logic.Support.Formatting.ParseDoubleStringAsCents(vatAmountString);
                }
                catch (ArgumentException)
                {
                    return(new AjaxCallResult
                    {
                        Success = false,
                        DisplayMessage = Resources.Pages.Ledgers.BalanceTransactions_Error_VatAmountParseError
                    });

                    throw;
                }
            }

            // We're FINALLY ready to update the transaction

            FinancialTransaction tx = FinancialTransaction.FromIdentity(transactionId);

            tx.Description = newDescription;
            docs.SetForeignObjectForAll(tx);

            Int64 centsDiff = tx.Rows.AmountCentsTotal;

            if (vatEnabled)
            {
                tx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsVatInboundUnreported, vatCents, authData.CurrentUser);
                tx.AddRow(budget, (-centsDiff) - vatCents, authData.CurrentUser);
            }
            else
            {
                tx.AddRow(budget, -centsDiff, authData.CurrentUser);
            }

            return(new AjaxCallResult {
                Success = true
            });
        }
Exemple #28
0
        public static ResyncResults ExecuteResync(string guid)
        {
            AuthenticationData authenticationData = GetAuthenticationDataAndCulture();

            if (
                !authenticationData.Authority.HasAccess(new Access(authenticationData.CurrentOrganization,
                                                                   AccessAspect.Bookkeeping, AccessType.Write)))
            {
                throw new UnauthorizedAccessException();
            }

            ResyncResults results = new ResyncResults();

            ExternalBankMismatchingDateTime[] mismatchArray =
                (ExternalBankMismatchingDateTime[])
                HttpContext.Current.Session["LedgersResync" + guid + "MismatchArray"];

            FinancialAccount account =
                (FinancialAccount)HttpContext.Current.Session["LedgersResync" + guid + "Account"];

            long             autoDepositDonationCents = 1000 * 100;
            FinancialAccount autoDonationAccount      = account.Organization.FinancialAccounts.IncomeDonations;

            if (authenticationData.CurrentOrganization.Identity != account.OrganizationId)
            {
                throw new InvalidOperationException("Mismatching org");
            }

            foreach (ExternalBankMismatchingDateTime mismatchDateTime in mismatchArray)
            {
                foreach (
                    ExternalBankMismatchingRecordDescription mismatchingRecord in mismatchDateTime.MismatchingRecords)
                {
                    for (int index = 0; index < mismatchingRecord.MasterCents.Length; index++)
                    {
                        results.RecordsTotal++;
                        long cents = mismatchingRecord.MasterCents[index];

                        bool unhandled = false;
                        bool handlable = true;

                        FinancialTransaction tx = mismatchingRecord.Transactions[index];

                        if (tx != null && tx.Dependency != null)
                        {
                            unhandled = true;

                            IHasIdentity dependency = tx.Dependency;

                            if (dependency is PaymentGroup &&
                                mismatchingRecord.ResyncActions[index] ==
                                ExternalBankMismatchResyncAction.RewriteSwarmops)
                            {
                                if (cents == (dependency as PaymentGroup).SumCents)
                                {
                                    // Amount checks out with dependency; rewrite requested; this is handlable on auto

                                    Dictionary <int, long> newTx = new Dictionary <int, long>();
                                    newTx[account.Identity] = cents;
                                    newTx[account.Organization.FinancialAccounts.AssetsOutboundInvoices.Identity] =
                                        -cents;
                                    tx.RecalculateTransaction(newTx, authenticationData.CurrentUser);
                                    unhandled = false;
                                }
                            }


                            handlable = false; // need to fix this
                        }


                        if (handlable)
                        {
                            switch (mismatchingRecord.ResyncActions[index])
                            {
                            case ExternalBankMismatchResyncAction.DeleteSwarmops:
                                if (tx == null)
                                {
                                    throw new InvalidOperationException(
                                              "Can't have Delete op on a null transaction");
                                }

                                tx.Description = tx.Description + " (killed/zeroed in resync)";
                                tx.RecalculateTransaction(new Dictionary <int, long>(),
                                                          authenticationData.CurrentUser); // zeroes out
                                break;

                            case ExternalBankMismatchResyncAction.RewriteSwarmops:
                                if (tx == null)
                                {
                                    throw new InvalidOperationException(
                                              "Can't have Rewrite op on a null transaction");
                                }

                                Dictionary <int, long> newTx = new Dictionary <int, long>();
                                newTx[account.Identity] = cents;
                                if (cents > 0 && cents < autoDepositDonationCents)
                                {
                                    newTx[autoDonationAccount.Identity] = -cents;     // negative; P&L account
                                }
                                tx.RecalculateTransaction(newTx, authenticationData.CurrentUser);
                                break;

                            case ExternalBankMismatchResyncAction.CreateSwarmops:
                                if (tx != null)
                                {
                                    throw new InvalidOperationException("Transaction seems to already exist");
                                }

                                tx = FinancialTransaction.Create(account.OwnerPersonId, mismatchDateTime.DateTime,
                                                                 mismatchingRecord.Description);
                                tx.AddRow(account, cents, authenticationData.CurrentUser);

                                if (cents > 0 && cents < autoDepositDonationCents)
                                {
                                    tx.AddRow(autoDonationAccount, -cents, authenticationData.CurrentUser);
                                }
                                break;

                            case ExternalBankMismatchResyncAction.NoAction:
                                // no action
                                break;

                            default:
                                // not handled
                                unhandled = true;
                                break;
                            }
                        }

                        if (unhandled)
                        {
                            results.RecordsFail++;
                        }
                        else
                        {
                            results.RecordsSuccess++;
                        }
                    }
                }
            }

            return(results);
        }
    protected void Page_Load(object sender, EventArgs e)
    {
        this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.Bookkeeping, AccessType.Write);

        this.PageTitle = "Close Ledgers";
        this.PageIcon  = "iconshock-calculator-lock";

        // Check if on a closable year

        if (this.CurrentOrganization.Parameters.EconomyEnabled == false || this.CurrentOrganization.Parameters.FiscalBooksClosedUntilYear == DateTime.Today.Year - 1)
        {
            this.PanelCannotClose.Visible           = true;
            this.PanelSuccess.Visible               = false;
            this.LabelCannotCloseLedgersReason.Text = "Ledgers are already closed as far as possible. [LOC]";
            return; // a return out of Page_Load is kind of unusual, see it as a "break" or "abort"
        }

        // Check if all transactions are balanced, so we can close

        FinancialTransactions unbalancedTransactions = FinancialTransactions.GetUnbalanced(this.CurrentOrganization); // TODO: this fn should move to Organization

        int closingYear = this.CurrentOrganization.Parameters.FiscalBooksClosedUntilYear + 1;

        bool hasOpenTxForClosingYear = false;

        foreach (FinancialTransaction unbalancedTransaction in unbalancedTransactions)
        {
            if (unbalancedTransaction.DateTime.Year <= closingYear)
            {
                hasOpenTxForClosingYear = true;
            }
        }

        if (hasOpenTxForClosingYear)
        {
            this.PanelCannotClose.Visible = true;
            this.PanelSuccess.Visible     = false;
            return; // a return out of Page_Load is kind of unusual, see it as a "break" or "abort"
        }

        // Start actually closing the ledgers


        // First, roll over virtual balances.

        //if (false) // if this.CurrentOrganization.Parameters.VirtualBankingEnabled
        //{
        //    FinancialAccount rootAccount = FinancialAccount.FromIdentity(29);  // HACK: Hardcoded account; should be _organization.FinancialAccount.CostsVirtualBankingRoot
        //    FinancialAccount tempAccount = FinancialAccount.FromIdentity(98);  // HACK: Hardcoded account; should be _organization.FinancialAccount.AssetsVirtualRollover

        //    FinancialAccounts localAccounts = rootAccount.GetTree();

        //    foreach (FinancialAccount account in localAccounts)
        //    {
        //        Int64 currentBalanceCents = account.GetDeltaCents(new DateTime(closingYear, 1, 1), new DateTime(closingYear+1, 1, 1));
        //        Int64 budgetCents = -account.GetBudgetCents(closingYear);
        //        Int64 carryOverCents = budgetCents - currentBalanceCents;

        //        if (carryOverCents != 0)
        //        {
        //            FinancialTransaction transactionOldYear = FinancialTransaction.Create(1,
        //                                                                                  new DateTime(closingYear, 12,
        //                                                                                               31, 23, 50,
        //                                                                                               00),
        //                                                                                  "Budgetrest " + account.Name);
        //                // HACK: Localize rollover label

        //            transactionOldYear.AddRow(account, carryOverCents, null);
        //            transactionOldYear.AddRow(tempAccount, -carryOverCents, null);

        //            FinancialTransaction transactionNewYear = FinancialTransaction.Create(1,
        //                                                                                  new DateTime(closingYear + 1,
        //                                                                                               1, 1, 0, 10, 0),
        //                                                                                  "Budgetrest " +
        //                                                                                  closingYear.ToString() + " " +
        //                                                                                  account.Name);

        //            transactionNewYear.AddRow(account, -carryOverCents, null);
        //            transactionNewYear.AddRow(tempAccount, carryOverCents, null);
        //        }
        //    }
        //}

        // Then, actually close the ledgers.

        FinancialAccounts accounts          = FinancialAccounts.ForOrganization(this.CurrentOrganization);
        Int64             balanceDeltaCents = 0;
        Int64             resultsDeltaCents = 0;

        foreach (FinancialAccount account in accounts)
        {
            Int64 accountBalanceCents;

            if (account.AccountType == FinancialAccountType.Asset || account.AccountType == FinancialAccountType.Debt)
            {
                accountBalanceCents = account.GetDeltaCents(new DateTime(2006, 1, 1), new DateTime(closingYear + 1, 1, 1));
                balanceDeltaCents  += accountBalanceCents;
            }
            else
            {
                accountBalanceCents = account.GetDeltaCents(new DateTime(closingYear, 1, 1), new DateTime(closingYear + 1, 1, 1));
                resultsDeltaCents  += accountBalanceCents;
            }
        }

        if (balanceDeltaCents == -resultsDeltaCents && closingYear < DateTime.Today.Year)
        {
            FinancialTransaction resultTransaction = FinancialTransaction.Create(this.CurrentOrganization.Identity, new DateTime(closingYear, 12, 31, 23, 59, 00), "Årets resultat " + closingYear.ToString());  // TODO: Localize string
            resultTransaction.AddRow(this.CurrentOrganization.FinancialAccounts.CostsYearlyResult, -resultsDeltaCents, null);
            resultTransaction.AddRow(this.CurrentOrganization.FinancialAccounts.DebtsEquity, -balanceDeltaCents, null);

            // Ledgers are now at zero-sum for the year's result accounts and from the start up until end-of-closing-year for the balance accounts.

            Organization.PPSE.Parameters.FiscalBooksClosedUntilYear = closingYear;
        }
        else
        {
            Console.WriteLine("NOT creating transaction.");
        }
    }