Exemple #1
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);
    }
    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();
        }
    }
        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 #4
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 #5
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);
    }
Exemple #6
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 #7
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 #8
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 #9
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);
        }
Exemple #10
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);
    }
        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
            });
        }
Exemple #12
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 #13
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();
    }
        public static ChangeAccountDataResult SetAccountInitialBalance(int accountId, string newInitialBalanceString)
        {
            try
            {
                AuthenticationData authData = GetAuthenticationDataAndCulture();
                FinancialAccount   account  = FinancialAccount.FromIdentity(accountId);

                if (!PrepareAccountChange(account, authData, false) || authData.CurrentOrganization.Parameters.FiscalBooksClosedUntilYear >= authData.CurrentOrganization.FirstFiscalYear)
                {
                    return(new ChangeAccountDataResult
                    {
                        Result = ChangeAccountDataOperationsResult.NoPermission
                    });
                }

                Int64 desiredInitialBalanceCents =
                    (Int64)
                    (Double.Parse(newInitialBalanceString,
                                  NumberStyles.AllowThousands | NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint,
                                  CultureInfo.CurrentCulture) * 100.0);

                Int64 currentInitialBalanceCents = account.GetDeltaCents(new DateTime(1900, 1, 1),
                                                                         new DateTime(authData.CurrentOrganization.FirstFiscalYear, 1, 1));

                Int64 deltaCents = desiredInitialBalanceCents - currentInitialBalanceCents;

                // Find or create "Initial Balances" transaction

                FinancialAccountRows testRows = FinancialAccountRows.ForOrganization(authData.CurrentOrganization,
                                                                                     new DateTime(1900, 1, 1), new DateTime(authData.CurrentOrganization.FirstFiscalYear, 1, 1));

                FinancialTransaction initialBalancesTransaction = null;

                foreach (FinancialAccountRow row in testRows)
                {
                    if (row.Transaction.Description == "Initial Balances")
                    {
                        initialBalancesTransaction = row.Transaction;
                        break;
                    }
                }

                if (initialBalancesTransaction == null)
                {
                    // create transaction

                    initialBalancesTransaction = FinancialTransaction.Create(authData.CurrentOrganization.Identity,
                                                                             new DateTime(authData.CurrentOrganization.FirstFiscalYear - 1, 12, 31), "Initial Balances");
                }

                Dictionary <int, Int64> recalcBase = initialBalancesTransaction.GetRecalculationBase();
                int equityAccountId = authData.CurrentOrganization.FinancialAccounts.DebtsEquity.Identity;

                if (!recalcBase.ContainsKey(accountId))
                {
                    recalcBase[accountId] = 0;
                }
                if (!recalcBase.ContainsKey(equityAccountId))
                {
                    recalcBase[equityAccountId] = 0;
                }

                recalcBase[accountId]       += deltaCents;
                recalcBase[equityAccountId] -= deltaCents;
                initialBalancesTransaction.RecalculateTransaction(recalcBase, authData.CurrentUser);
                return(new ChangeAccountDataResult
                {
                    Result = ChangeAccountDataOperationsResult.Changed,
                    NewData = (desiredInitialBalanceCents / 100.0).ToString("N2", CultureInfo.CurrentCulture)
                });
            }
            catch (Exception weirdException)
            {
                SwarmDb.GetDatabaseForWriting()
                .CreateExceptionLogEntry(DateTime.UtcNow, "AccountPlan-SetInitBalance", weirdException);

                throw;
            }
        }
    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.");
        }
    }
        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 #17
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));
        }
        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();
        }