Beispiel #1
0
        public static ExpenseClaim Create(Person claimer, Organization organization, FinancialAccount budget,
                                          DateTime expenseDate, string description, Int64 amountCents)
        {
            ExpenseClaim newClaim = FromIdentityAggressive(SwarmDb.GetDatabaseForWriting().CreateExpenseClaim(claimer.Identity, organization.Identity,
                                                                                                              budget.Identity, expenseDate, description, amountCents));
            // Create the financial transaction with rows

            string transactionDescription = "Expense #" + newClaim.Identity + ": " + description;  // TODO: Localize

            if (transactionDescription.Length > 64)
            {
                transactionDescription = transactionDescription.Substring(0, 61) + "...";
            }

            FinancialTransaction transaction =
                FinancialTransaction.Create(organization.Identity, DateTime.Now,
                                            transactionDescription);

            transaction.AddRow(organization.FinancialAccounts.DebtsExpenseClaims, -amountCents, claimer);
            transaction.AddRow(budget, amountCents, claimer);

            // Make the transaction dependent on the expense claim

            transaction.Dependency = newClaim;

            // Create notifications

            OutboundComm.CreateNotificationAttestationNeeded(budget, claimer, string.Empty, (double)amountCents / 100.0, description, NotificationResource.ExpenseClaim_Created); // Slightly misplaced logic, but failsafer here
            OutboundComm.CreateNotificationFinancialValidationNeeded(organization, (double)amountCents / 100.0,
                                                                     NotificationResource.Receipts_Filed);
            SwarmopsLogEntry.Create(claimer,
                                    new ExpenseClaimFiledLogEntry(claimer /*filing person*/, claimer /*beneficiary*/, (double)amountCents / 100.0, budget, description), newClaim);

            return(newClaim);
        }
Beispiel #2
0
        public static InboundInvoice Create (Organization organization, DateTime dueDate, Int64 amountCents,
            FinancialAccount budget, string supplier, string description, string payToAccount, string ocr, 
            string invoiceReference, Person creatingPerson)
        {
            InboundInvoice newInvoice = FromIdentity(SwarmDb.GetDatabaseForWriting().
                CreateInboundInvoice(organization.Identity, dueDate, budget.Identity,
                    supplier, payToAccount, ocr,
                    invoiceReference, amountCents, creatingPerson.Identity));

            newInvoice.Description = description;  // Not in original schema; not cause for schema update

            // Create a corresponding financial transaction with rows

            FinancialTransaction transaction =
                FinancialTransaction.Create(organization.Identity, DateTime.Now,
                "Invoice #" + newInvoice.Identity + " from " + supplier);

            transaction.AddRow(organization.FinancialAccounts.DebtsInboundInvoices, -amountCents, creatingPerson);
            transaction.AddRow(budget, amountCents, creatingPerson);

            // Make the transaction dependent on the inbound invoice

            transaction.Dependency = newInvoice;

            // Create notification (slightly misplaced logic, but this is failsafest place)

            OutboundComm.CreateNotificationAttestationNeeded(budget, creatingPerson, supplier, (double)amountCents / 100.0, description, NotificationResource.InboundInvoice_Created); // Slightly misplaced logic, but failsafer here
            SwarmopsLogEntry.Create(creatingPerson,
                                    new InboundInvoiceCreatedLogEntry(creatingPerson, supplier, description, (double)amountCents / 100.0, budget), newInvoice);

            return newInvoice;
        }
        private void AddRow(int financialAccountId, Int64 amountCents, int personId)
        {
            if (this.DateTime.Year <= FinancialAccount.FromIdentity(financialAccountId).Organization.Parameters.FiscalBooksClosedUntilYear)
            {
                // Recurse down into continuation transactions to write row in first nonclosed year

                FinancialTransaction transactionContinued = this.ContinuedTransaction;

                if (transactionContinued == null)
                {
                    // No continuation; create one

                    transactionContinued = FinancialTransaction.Create(this.OrganizationId, DateTime.Now,
                                                                       "Continued Tx #" + this.Identity.ToString());
                    transactionContinued.AddRow(financialAccountId, amountCents, personId);
                    transactionContinued.Dependency = this;
                }
                else
                {
                    // Recurse

                    transactionContinued.AddRow(financialAccountId, amountCents, personId);
                }

                return;
            }

            SwarmDb.GetDatabaseForWriting().CreateFinancialTransactionRow(Identity, financialAccountId, amountCents, personId);
        }
Beispiel #4
0
        private static FinancialAccountRows RowsNotInVatReport(FinancialAccount account, DateTime endDateTime)
        {
            Organization organization = account.Organization;
            SwarmDb      dbRead       = SwarmDb.GetDatabaseForReading();

            FinancialAccountRows rowsIntermediate =
                FinancialAccountRows.FromArray(
                    SwarmDb.GetDatabaseForReading().GetAccountRowsNotInVatReport(account.Identity, endDateTime));

            FinancialAccountRows rowsFinal = new FinancialAccountRows();

            foreach (FinancialAccountRow row in rowsIntermediate)
            {
                // Check if this row _closes_ an _existing_ VAT report, in which case it should _not_ be included

                int vatReportOpenId  = dbRead.GetVatReportIdFromCloseTransaction(row.FinancialTransactionId);
                int vatReportCloseId = dbRead.GetVatReportIdFromOpenTransaction(row.FinancialTransactionId);
                if (vatReportOpenId == 0 && vatReportCloseId == 0)
                {
                    // This particular transaction doesn't close an existing VAT report
                    rowsFinal.Add(row);
                }
            }

            return(rowsFinal);
        }
Beispiel #5
0
        public static InboundInvoice Create(Organization organization, DateTime dueDate, Int64 amountCents,
                                            FinancialAccount budget, string supplier, string description, string payToAccount, string ocr,
                                            string invoiceReference, Person creatingPerson)
        {
            InboundInvoice newInvoice = FromIdentity(SwarmDb.GetDatabaseForWriting().
                                                     CreateInboundInvoice(organization.Identity, dueDate, budget.Identity,
                                                                          supplier, payToAccount, ocr,
                                                                          invoiceReference, amountCents, creatingPerson.Identity));

            newInvoice.Description = description; // Not in original schema; not cause for schema update

            // Create a corresponding financial transaction with rows

            FinancialTransaction transaction =
                FinancialTransaction.Create(organization.Identity, DateTime.Now,
                                            "Invoice #" + newInvoice.Identity + " from " + supplier);

            transaction.AddRow(organization.FinancialAccounts.DebtsInboundInvoices, -amountCents, creatingPerson);
            transaction.AddRow(budget, amountCents, creatingPerson);

            // Make the transaction dependent on the inbound invoice

            transaction.Dependency = newInvoice;

            // Create notification (slightly misplaced logic, but this is failsafest place)

            OutboundComm.CreateNotificationAttestationNeeded(budget, creatingPerson, supplier, amountCents / 100.0,
                                                             description, NotificationResource.InboundInvoice_Created);
            // Slightly misplaced logic, but failsafer here
            SwarmopsLogEntry.Create(creatingPerson,
                                    new InboundInvoiceCreatedLogEntry(creatingPerson, supplier, description, amountCents / 100.0, budget),
                                    newInvoice);

            return(newInvoice);
        }
Beispiel #6
0
        public static ExpenseClaim Create(Person claimer, Organization organization, FinancialAccount budget, 
                                      DateTime expenseDate, string description, Int64 amountCents)
        {
            ExpenseClaim newClaim = FromIdentityAggressive (SwarmDb.GetDatabaseForWriting().CreateExpenseClaim (claimer.Identity, organization.Identity,
                                                                       budget.Identity, expenseDate, description, amountCents));
            // Create the financial transaction with rows

            string transactionDescription = "Expense #" + newClaim.Identity + ": " + description;  // TODO: Localize

            if (transactionDescription.Length > 64)
            {
                transactionDescription = transactionDescription.Substring(0, 61) + "...";
            }

            FinancialTransaction transaction =
                FinancialTransaction.Create(organization.Identity, DateTime.Now,
                transactionDescription);

            transaction.AddRow(organization.FinancialAccounts.DebtsExpenseClaims, -amountCents, claimer);
            transaction.AddRow(budget, amountCents, claimer);
             
            // Make the transaction dependent on the expense claim

            transaction.Dependency = newClaim;

            // Create notifications

            OutboundComm.CreateNotificationAttestationNeeded(budget, claimer, string.Empty, (double)amountCents / 100.0, description, NotificationResource.ExpenseClaim_Created); // Slightly misplaced logic, but failsafer here
            OutboundComm.CreateNotificationFinancialValidationNeeded(organization, (double) amountCents/100.0,
                                                                     NotificationResource.Receipts_Filed);
            SwarmopsLogEntry.Create(claimer,
                                    new ExpenseClaimFiledLogEntry(claimer /*filing person*/, claimer /*beneficiary*/, (double) amountCents/100.0, budget, description), newClaim);

            return newClaim;
        }
        private FinancialTransactionRow AddRow(int financialAccountId, Int64 amountCents, int personId)
        {
            // private function that actually executes the row adding

            FinancialAccount account = FinancialAccount.FromIdentity(financialAccountId);

            if (DateTime.Year <= account.Organization.Parameters.FiscalBooksClosedUntilYear)
            {
                // Recurse down into continuation transactions to write row in first nonclosed year

                FinancialTransactionRow newRow = null;

                FinancialTransaction transactionContinued = ContinuedTransaction;

                if (transactionContinued == null)
                {
                    // No continuation; create one

                    transactionContinued = Create(OrganizationId, DateTime.Now,
                                                  "Continued Tx #" + Identity);
                    newRow = transactionContinued.AddRow(financialAccountId, amountCents, personId);
                    transactionContinued.Dependency = this;
                }
                else
                {
                    // Recurse

                    newRow = transactionContinued.AddRow(financialAccountId, amountCents, personId);
                }

                return(newRow);
            }

            FinancialTransactionRow addedRow = FinancialTransactionRow.FromIdentityAggressive(SwarmDb.GetDatabaseForWriting()
                                                                                              .CreateFinancialTransactionRow(Identity, financialAccountId, amountCents, personId));

            // If we're running from web, and this was a P&L account, then also notify the server that the P&L has changed
            // (doing this here means that the server can get pinged multiple times, but that's more defensive coding than
            // having to remember doing it everywhere at the UI level)

            if (account.AccountType == FinancialAccountType.Income || account.AccountType == FinancialAccountType.Cost)
            {
                if (SupportFunctions.OperatingTopology == OperatingTopology.FrontendWeb)
                {
                    SocketMessage newMessage = new SocketMessage
                    {
                        MessageType            = "ProfitLossChanged",
                        OrganizationId         = account.Organization.Identity,
                        FinancialTransactionId = this.Identity
                    };

                    newMessage.SendUpstream();
                }
            }

            return(addedRow);
        }
Beispiel #8
0
        public void BindToTransactionAndClose(FinancialTransaction transaction, Person bindingPerson)
        {
            Dictionary <int, Int64> accountDebitLookup = new Dictionary <int, Int64>();
            Organization            organization       = Organization;

            accountDebitLookup[organization.FinancialAccounts.DebtsExpenseClaims.Identity]            = 0;
            accountDebitLookup[organization.FinancialAccounts.DebtsInboundInvoices.Identity]          = 0;
            accountDebitLookup[organization.FinancialAccounts.DebtsSalary.Identity]                   = 0;
            accountDebitLookup[organization.FinancialAccounts.DebtsTax.Identity]                      = 0;
            accountDebitLookup[organization.FinancialAccounts.AssetsOutstandingCashAdvances.Identity] = 0;

            if (this.DependentExpenseClaims.Count > 0)
            {
                accountDebitLookup[organization.FinancialAccounts.DebtsExpenseClaims.Identity] +=
                    this.DependentExpenseClaims.TotalAmountCents;
            }
            if (this.DependentInvoices.Count > 0)
            {
                accountDebitLookup[organization.FinancialAccounts.DebtsInboundInvoices.Identity] +=
                    this.DependentInvoices.TotalAmountCents;
            }
            if (this.DependentSalariesNet.Count > 0)
            {
                accountDebitLookup[organization.FinancialAccounts.DebtsSalary.Identity] +=
                    this.DependentSalariesNet.TotalAmountCentsNet;
            }
            if (this.DependentSalariesTax.Count > 0)
            {
                accountDebitLookup[organization.FinancialAccounts.DebtsTax.Identity] +=
                    this.DependentSalariesTax.TotalAmountCentsTax;
            }
            if (this.DependentCashAdvancesPayout.Count > 0)
            {
                accountDebitLookup[organization.FinancialAccounts.AssetsOutstandingCashAdvances.Identity] +=
                    this.DependentCashAdvancesPayout.TotalAmountCents;
            }
            if (this.DependentCashAdvancesPayback.Count > 0)
            {
                accountDebitLookup[organization.FinancialAccounts.AssetsOutstandingCashAdvances.Identity] -=
                    // observe the minus
                    this.DependentCashAdvancesPayback.TotalAmountCents;
            }

            foreach (int financialAccountId in accountDebitLookup.Keys)
            {
                if (accountDebitLookup[financialAccountId] != 0)
                {
                    transaction.AddRow(FinancialAccount.FromIdentity(financialAccountId),
                                       accountDebitLookup[financialAccountId], bindingPerson);
                }
            }

            transaction.Dependency = this;
            Open = false;
        }
 public static FinancialAccountDocument Create(FinancialAccount account, FinancialAccountDocumentType documentType, Person uploader, DateTime concernsStart, DateTime concernsEnd, string rawText)
 {
     return(FromIdentityAggressive(
                SwarmDb.GetDatabaseForWriting().CreateFinancialAccountDocument(
                    account.Identity,
                    documentType,
                    uploader == null? 0: uploader.Identity,
                    concernsStart,
                    concernsEnd,
                    rawText
                    )));
 }
Beispiel #10
0
        public static Parley Create (Organization organization, Person person, FinancialAccount budgetInitial, string name, Geography geography, string description, string informationUrl, DateTime startDate, DateTime endDate, Int64 budgetCents, Int64 guaranteeCents, Int64 attendanceFeeCents)
        {
            Parley newParley = 
                FromIdentity(SwarmDb.GetDatabaseForWriting().CreateParley(organization.Identity, person.Identity,
                                                                 -(budgetInitial.Identity), name, geography.Identity,
                                                                 description, informationUrl, startDate, endDate,
                                                                 budgetCents, guaranteeCents, attendanceFeeCents));

            PWEvents.CreateEvent(EventSource.PirateWeb, EventType.ParleyCreated, person.Identity, organization.Identity, 0, 0, newParley.Identity, string.Empty); 

            return newParley;
        }
Beispiel #11
0
        public static OutboundInvoice Create (Organization organization, Person createdByPerson, DateTime dueDate, FinancialAccount budget, string customerName, string invoiceAddressMail, string invoiceAddressPaper, Currency currency, bool domestic, string theirReference)
        {
            OutboundInvoice invoice = FromIdentity (SwarmDb.GetDatabaseForWriting().CreateOutboundInvoice(organization.Identity, createdByPerson != null? createdByPerson.Identity : 0, dueDate,
                                                         budget.Identity, customerName, invoiceAddressPaper,
                                                         invoiceAddressMail, currency.Identity, string.Empty, domestic, Authentication.CreateRandomPassword(6), theirReference));

            // Set reference

            invoice.Reference =
                Formatting.AddLuhnChecksum(Formatting.ReverseString(DateTime.Now.ToString("yyyyMMddHHmm")) +
                                           invoice.Identity.ToString());

            return invoice;
        }
 public ExpenseClaimFiledLogEntry(Person filingPerson, Person beneficiaryPerson, double amount, FinancialAccount budget, string reason)
 {
     this.Amount = amount;
     this.Currency = budget.Organization.Currency.Code;
     this.DateTime = DateTime.UtcNow;
     this.Description = reason;
     this.OrganizationId = budget.OrganizationId;
     this.FinancialAccountId = budget.Identity;
     this.FinancialAccountName = budget.Name; // redundancy in case of future name changes
     this.OwnerPersonId = budget.OwnerPersonId;
     this.OwnerPersonName = budget.OwnerPersonId != 0 ? budget.Owner.Name : string.Empty;
     this.ActingPersonId = filingPerson.Identity; // do not save name for data retention reasons
     this.BeneficiaryPersonId = beneficiaryPerson.Identity;
 }
Beispiel #13
0
        public static CashAdvance Create(Organization organization, Person forPerson, Person createdByPerson, Int64 amountCents, FinancialAccount budget, string description)
        {
            CashAdvance newAdvance = FromIdentityAggressive(SwarmDb.GetDatabaseForWriting().CreateCashAdvance(forPerson.Identity,
                                                                                          createdByPerson.Identity,
                                                                                          organization.Identity,
                                                                                          budget.Identity, amountCents,
                                                                                          description));

            OutboundComm.CreateNotificationAttestationNeeded(budget, forPerson, string.Empty, (double) amountCents/100.0, description, NotificationResource.CashAdvance_Requested); // Slightly misplaced logic, but failsafer here
            SwarmopsLogEntry.Create(forPerson,
                                    new CashAdvanceRequestedLogEntry(createdByPerson, forPerson, (double) amountCents/100.0, budget, description),
                                    newAdvance);

            return newAdvance;
        }
 public InboundInvoiceCreatedLogEntry(Person creatingPerson, string supplier, string description, double amount, FinancialAccount budget)
 {
     this.Amount = amount;
     this.Currency = budget.Organization.Currency.Code;
     this.DateTime = DateTime.UtcNow;
     this.Description = description;
     this.OrganizationId = budget.OrganizationId;
     this.FinancialAccountId = budget.Identity;
     this.FinancialAccountName = budget.Name; // redundancy in case of future name changes
     this.OwnerPersonId = budget.OwnerPersonId;
     this.OwnerPersonName = budget.OwnerPersonId != 0 ? budget.Owner.Name : string.Empty;
     this.ActingPersonId = creatingPerson.Identity; // do not save name for data retention reasons
     this.BeneficiaryPersonId = 0;
     this.Supplier = supplier;
 }
        public static void CheckColdStorageForOrganization(Organization organization)
        {
            FinancialAccount coldRoot = organization.FinancialAccounts.AssetsBitcoinCold;

            if (coldRoot == null)
            {
                return; // no cold assets to check
            }

            // Construct the address-to-account map only once for the entire recursion chain

            Dictionary <string, int> addressAccountLookup = GetAddressAccountLookup(organization);

            // Recurse

            CheckColdStorageRecurse(coldRoot, addressAccountLookup);
        }
        private static void GetAddressAccountLookupRecurse(FinancialAccount account, Dictionary <string, int> result)
        {
            if (account == null)
            {
                return;
            }

            foreach (FinancialAccount child in account.Children)
            {
                GetAddressAccountLookupRecurse(child, result);
            }

            if (!string.IsNullOrEmpty(account.BitcoinAddress) || IsValidBitcoinAddress(account.Name))  // TODO: Add a special property for the address instead of using name
            {
                result [account.Name] = account.Identity;
            }
        }
        public Int64 this [FinancialAccount account]
        {
            get
            {
                Int64 result = 0;

                FinancialTransactionRows rows = Rows;

                foreach (FinancialTransactionRow row in rows)
                {
                    if (row.FinancialAccountId == account.Identity)
                    {
                        result += row.AmountCents;
                    }
                }

                return(result);
            }
        }
Beispiel #18
0
        public static CashAdvance Create(Organization organization, Person forPerson, Person createdByPerson,
                                         Int64 amountCents, FinancialAccount budget, string description)
        {
            CashAdvance newAdvance =
                FromIdentityAggressive(SwarmDb.GetDatabaseForWriting().CreateCashAdvance(forPerson.Identity,
                                                                                         createdByPerson.Identity,
                                                                                         organization.Identity,
                                                                                         budget.Identity, amountCents,
                                                                                         description));

            OutboundComm.CreateNotificationAttestationNeeded(budget, forPerson, string.Empty, amountCents / 100.0,
                                                             description, NotificationResource.CashAdvance_Requested);
            // Slightly misplaced logic, but failsafer here
            SwarmopsLogEntry.Create(forPerson,
                                    new CashAdvanceRequestedLogEntry(createdByPerson, forPerson, amountCents / 100.0, budget, description),
                                    newAdvance);

            return(newAdvance);
        }
        public FinancialAccount this[OrganizationFinancialAccountType accountType]
        {
            get
            {
                int accountId = SwarmDb.GetDatabaseForReading().GetOrganizationFinancialAccountId(_organizationId,
                                                                                                  accountType);

                if (accountId == 0)
                {
                    return(null); // not set
                }

                return(FinancialAccount.FromIdentity(accountId));
            }
            set
            {
                SwarmDb.GetDatabaseForWriting().SetOrganizationFinancialAccountId(_organizationId, accountType,
                                                                                  value.Identity);
            }
        }
Beispiel #20
0
        public static OutboundInvoice Create(Organization organization, Person createdByPerson, DateTime dueDate,
                                             FinancialAccount budget, string customerName, string invoiceAddressMail, string invoiceAddressPaper,
                                             Currency currency, bool domestic, string theirReference)
        {
            OutboundInvoice invoice =
                FromIdentity(SwarmDb.GetDatabaseForWriting()
                             .CreateOutboundInvoice(organization.Identity,
                                                    createdByPerson != null ? createdByPerson.Identity : 0,
                                                    dueDate,
                                                    budget.Identity, customerName, invoiceAddressPaper,
                                                    invoiceAddressMail, currency.Identity, string.Empty, domestic,
                                                    Authentication.CreateRandomPassword(6), theirReference));

            // Set reference

            invoice.Reference =
                Formatting.AddLuhnChecksum(Formatting.ReverseString(DateTime.Now.ToString("yyyyMMddHHmm")) +
                                           invoice.Identity);

            return(invoice);
        }
        public static FinancialAccountDocuments ForAccount(FinancialAccount account, FinancialAccountDocumentType typeRequested = FinancialAccountDocumentType.Unknown)
        {
            FinancialAccountDocuments result = FromArray(SwarmDb.GetDatabaseForReading().GetFinancialAccountDocuments(account));

            result.Sort(new FinancialAccountDocumentsSorterByTypeThenDate());

            if (typeRequested != FinancialAccountDocumentType.Unknown)
            {
                FinancialAccountDocuments subset = new FinancialAccountDocuments();

                foreach (FinancialAccountDocument document in result)
                {
                    if (document.Type == typeRequested)
                    {
                        subset.Add(document);
                    }
                }

                return(subset);
            }

            return(result);
        }
 public void AddRow(FinancialAccount account, Int64 amountCents, Person person)
 {
     AddRow(account.Identity, amountCents, person != null ? person.Identity : 0);
 }
Beispiel #23
0
        public static OutboundComm CreateNotificationAttestationNeeded(FinancialAccount budget, Person concernedPerson, string supplier, double amountRequested, string purpose, NotificationResource notification)
        {
            NotificationPayload payload = new NotificationPayload(notification.ToString());
            payload.Strings[NotificationString.CurrencyCode] = budget.Organization.Currency.Code;
            payload.Strings[NotificationString.OrganizationName] = budget.Organization.Name;
            payload.Strings[NotificationString.BudgetAmountFloat] = amountRequested.ToString(CultureInfo.InvariantCulture);
            payload.Strings[NotificationString.RequestPurpose] = purpose;
            payload.Strings[NotificationString.Description] = purpose;
            payload.Strings[NotificationString.Supplier] = supplier;
            payload.Strings[NotificationString.BudgetName] = budget.Name;
            payload.Strings[NotificationString.ConcernedPersonName] = concernedPerson.Canonical;

            OutboundComm comm = OutboundComm.Create(null, null, budget.Organization, CommResolverClass.Unknown, null,
                                                    CommTransmitterClass.CommsTransmitterMail,
                                                    new PayloadEnvelope(payload).ToXml(),
                                                    OutboundCommPriority.Low);

            if (budget.OwnerPersonId == 0)
            {
                comm.AddRecipient(Person.FromIdentity(1)); // Add admin - not good, should be org admins // HACK // TODO
            }
            else
            {
                comm.AddRecipient(budget.Owner);
            }

            comm.Resolved = true;

            return comm;

        }
 public void AddRow (FinancialAccount account, double amount, Person person)
 {
     AddRow (account, (Int64) (amount * 100), person);
 }
Beispiel #25
0
        public static PayrollItem Create(Person person, Organization organization, DateTime employedDate,
                                         Person reportsToPerson, Country country, Int64 baseSalaryCents, FinancialAccount budget,
                                         double additiveTaxLevel, int subtractiveTaxLevelId, bool isContractor)
        {
            int payrollItemId = SwarmDb.GetDatabaseForWriting()
                                .CreatePayrollItem(person.Identity, organization.Identity, employedDate, reportsToPerson.Identity,
                                                   country.Identity, baseSalaryCents, budget.Identity, additiveTaxLevel, subtractiveTaxLevelId,
                                                   isContractor);

            return(FromIdentityAggressive(payrollItemId));
        }
Beispiel #26
0
 public void SetBudget(FinancialAccount budget, Person settingPerson)
 {
     base.BudgetId = budget.Identity;
     SwarmDb.GetDatabaseForWriting().SetInboundInvoiceBudget(Identity, budget.Identity);
     UpdateTransaction(settingPerson);
 }
 public void AddRow (FinancialAccount account, Int64 amountCents, Person person)
 {
     AddRow(account.Identity, amountCents, person != null ? person.Identity : 0);
 }
        public Int64 this[FinancialAccount account]
        {
            get
            {
                Int64 result = 0;

                FinancialTransactionRows rows = this.Rows;

                foreach (FinancialTransactionRow row in rows)
                {
                    if (row.FinancialAccountId == account.Identity)
                    {
                        result += row.AmountCents;
                    }
                }

                return result;
            }
        }
Beispiel #29
0
 public void SetBudget(FinancialAccount budget, Person settingPerson)
 {
     SwarmDb.GetDatabaseForWriting().SetExpenseClaimBudget(this.Identity, budget.Identity);
     base.BudgetId = budget.Identity;
     UpdateFinancialTransaction(settingPerson);
 }
Beispiel #30
0
 public void SetBudget (FinancialAccount budget, Person settingPerson)
 {
     SwarmDb.GetDatabaseForWriting().SetExpenseClaimBudget (this.Identity, budget.Identity);
     base.BudgetId = budget.Identity;
     UpdateFinancialTransaction(settingPerson);
 }
Beispiel #31
0
        public static ExpenseClaim Create(Person claimer, Organization organization, FinancialAccount budget,
                                          DateTime expenseDate, string description, Int64 amountCents, Int64 vatCents, ExpenseClaimGroup group = null)
        {
            ExpenseClaim newClaim =
                FromIdentityAggressive(SwarmDb.GetDatabaseForWriting()
                                       .CreateExpenseClaim(claimer.Identity, organization?.Identity ?? 0,
                                                           budget?.Identity ?? 0, expenseDate, description, amountCents)); // budget can be 0 initially if created with a group

            if (vatCents > 0)
            {
                newClaim.VatCents = vatCents;
            }

            if (group != null)
            {
                newClaim.Group = group;
            }

            if (budget != null && organization != null)
            {
                // Create the financial transaction with rows

                string transactionDescription = "Expense #" + newClaim.OrganizationSequenceId + ": " + description;
                // TODO: Localize

                if (transactionDescription.Length > 64)
                {
                    transactionDescription = transactionDescription.Substring(0, 61) + "...";
                }


                DateTime expenseTxDate      = expenseDate;
                int      ledgersClosedUntil = organization.Parameters.FiscalBooksClosedUntilYear;

                if (ledgersClosedUntil >= expenseDate.Year)
                {
                    expenseTxDate = DateTime.UtcNow; // If ledgers are closed for the actual expense time, account now
                }

                FinancialTransaction transaction =
                    FinancialTransaction.Create(organization.Identity, expenseTxDate,
                                                transactionDescription);

                transaction.AddRow(organization.FinancialAccounts.DebtsExpenseClaims, -amountCents, claimer);
                if (vatCents > 0)
                {
                    transaction.AddRow(budget, amountCents - vatCents, claimer);
                    transaction.AddRow(organization.FinancialAccounts.AssetsVatInboundUnreported, vatCents, claimer);
                }
                else
                {
                    transaction.AddRow(budget, amountCents, claimer);
                }

                // Make the transaction dependent on the expense claim

                transaction.Dependency = newClaim;

                // Create notifications

                OutboundComm.CreateNotificationAttestationNeeded(budget, claimer, string.Empty,
                                                                 newClaim.BudgetAmountCents / 100.0,
                                                                 description, NotificationResource.ExpenseClaim_Created);
                // Slightly misplaced logic, but failsafer here
                OutboundComm.CreateNotificationFinancialValidationNeeded(organization, newClaim.AmountCents / 100.0,
                                                                         NotificationResource.Receipts_Filed);
                SwarmopsLogEntry.Create(claimer,
                                        new ExpenseClaimFiledLogEntry(claimer /*filing person*/, claimer /*beneficiary*/,
                                                                      newClaim.BudgetAmountCents / 100.0,
                                                                      vatCents / 100.0, budget, description), newClaim);

                // Clear a cache
                FinancialAccount.ClearAttestationAdjustmentsCache(organization);
            }

            return(newClaim);
        }
 public void AddRow(FinancialAccount account, double amount, Person person)
 {
     AddRow(account, (Int64)(amount * 100), person);
 }
Beispiel #33
0
        public static AnnualReport Create(Organization organization, int year, FinancialAccountType accountType)
        {
            AnnualReport report = new AnnualReport();

            report.Organization = organization;
            report.Year         = year;
            report._accountType = accountType;

            // Get accounts

            FinancialAccounts accounts = FinancialAccounts.ForOrganization(organization, accountType);

            // Remove unwanted accounts

            FinancialAccount resultsAccount = organization.FinancialAccounts.CostsYearlyResult;

            foreach (FinancialAccount account in accounts)
            {
                // For now, just remove the "results" account. TODO: Remove inactive accounts, too.

                if (account.Identity == resultsAccount.Identity)
                {
                    accounts.Remove(account);
                    break;
                }
            }

            // Build tree (there should be a template for this)

            report._treeMap = new Dictionary <int, List <FinancialAccount> >();

            foreach (FinancialAccount account in accounts)
            {
                if (!report._treeMap.ContainsKey(account.ParentIdentity))
                {
                    report._treeMap[account.ParentIdentity] = new List <FinancialAccount>();
                }

                report._treeMap[account.ParentIdentity].Add(account);
            }


            FinancialAccounts orderedList = new FinancialAccounts();

            // This list is guaranteed to have parents before children

            report.PopulateOrderedList(orderedList, 0); // recursively add nodes parents-first
            report.PopulateLookups(orderedList);        // populate the lookup tables for results per account
            report.PopulateTotals();

            report.ReportLines = new List <AnnualReportLine>();
            report.RecurseAddLines(report.ReportLines, 0);

            // Aggregate accounts, if appropriate

            if (report._treeMap[0].Count > 3)
            {
                // regroup list

                report.AggregateAccounts();
            }

            return(report);
        }
        private static void CheckColdStorageRecurse(FinancialAccount parent, Dictionary <string, int> addressAccountLookup)
        {
            foreach (FinancialAccount child in parent.Children)
            {
                CheckColdStorageRecurse(child, addressAccountLookup);
            }

            // After recursing, get all transactions for this account and verify against our records

            // is the account name a valid bitcoin address on the main network?

            string address = parent.BitcoinAddress;

            if (string.IsNullOrEmpty(address))
            {
                if (BitcoinUtility.IsValidBitcoinAddress(parent.Name.Trim()))
                {
                    parent.BitcoinAddress = address = parent.Name.Trim();
                }
                else
                {
                    return; // not a bitcoin address but something else; do not process
                }
            }

            Organization organization = parent.Organization;
            bool         organizationLedgerUsesBitcoin = organization.Currency.IsBitcoinCore;

            JObject addressData = JObject.Parse(
                new WebClient().DownloadString("https://blockchain.info/address/" + address +
                                               "?format=json&api_key=" +
                                               SystemSettings.BlockchainSwarmopsApiKey));

            //int transactionCount = (int) (addressData["n_tx"]);

            foreach (JObject txJson in addressData["txs"])
            {
                FinancialTransaction      ourTx          = null;
                Dictionary <Int64, Int64> satoshisLookup = new Dictionary <long, long>(); // map from ledgercents to satoshis
                BlockchainTransaction     blockchainTx   = BlockchainTransaction.FromBlockchainInfoJson(txJson);

                try
                {
                    ourTx = FinancialTransaction.FromBlockchainHash(parent.Organization, blockchainTx.TransactionHash);
                    // If the transaction was fetched fine, we have already seen this transaction, but need to re-check it
                }
                catch (ArgumentException)
                {
                    // We didn't have this transaction, so we need to create it

                    ourTx = FinancialTransaction.Create(parent.Organization, blockchainTx.TransactionDateTimeUtc, "Blockchain tx");
                    ourTx.BlockchainHash = blockchainTx.TransactionHash;

                    // Did we lose or gain money?
                    // Find all in- and outpoints, determine which are ours (hot and cold wallet) and which aren't
                }

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

                // Note the non-blockchain rows in this tx, keep them for reconstruction

                foreach (FinancialTransactionRow row in ourTx.Rows)
                {
                    if (!addressAccountLookup.ContainsValue(row.FinancialAccountId))  // not optimal but n is small
                    {
                        // This is not a bitcoin address account, so note it for reconstruction

                        if (!transactionReconstructedRows.ContainsKey(row.FinancialAccountId))
                        {
                            transactionReconstructedRows[row.FinancialAccountId] = 0; // init
                        }

                        transactionReconstructedRows[row.FinancialAccountId] += row.AmountCents;
                    }
                    else
                    {
                        // this is a known blockchain row, note its ledgered value in satoshis
                        if (!organizationLedgerUsesBitcoin)
                        {
                            Money nativeMoney = row.AmountForeignCents;
                            if (nativeMoney != null && nativeMoney.Currency.IsBitcoinCore) // it damn well should be, but just checking
                            {
                                satoshisLookup[row.AmountCents] = row.AmountForeignCents.Cents;
                            }
                        }
                    }
                }

                // Reconstruct the blockchain rows: input, output, fees, in that order

                // -- inputs

                foreach (BlockchainTransactionRow inputRow in blockchainTx.Inputs)
                {
                    if (addressAccountLookup.ContainsKey(inputRow.Address))
                    {
                        // this input is ours

                        int financialAccountId = addressAccountLookup[inputRow.Address];

                        if (!transactionReconstructedRows.ContainsKey(financialAccountId))
                        {
                            transactionReconstructedRows[financialAccountId] = 0; // initialize
                        }

                        if (organizationLedgerUsesBitcoin)
                        {
                            transactionReconstructedRows[financialAccountId] +=
                                -inputRow.ValueSatoshis; // note the negation!
                        }
                        else
                        {
                            Int64 ledgerCents =
                                new Money(inputRow.ValueSatoshis, Currency.BitcoinCore, ourTx.DateTime).ToCurrency(
                                    organization.Currency).Cents;
                            transactionReconstructedRows[financialAccountId] +=
                                -ledgerCents; // note the negation!
                            satoshisLookup[ledgerCents] = inputRow.ValueSatoshis;
                        }
                    }
                }

                // -- outputs

                foreach (BlockchainTransactionRow outputRow in blockchainTx.Outputs)
                {
                    if (addressAccountLookup.ContainsKey(outputRow.Address))
                    {
                        // this output is ours

                        int financialAccountId = addressAccountLookup[outputRow.Address];

                        if (!transactionReconstructedRows.ContainsKey(financialAccountId))
                        {
                            transactionReconstructedRows[financialAccountId] = 0; // initialize
                        }

                        if (organizationLedgerUsesBitcoin)
                        {
                            transactionReconstructedRows[financialAccountId] +=
                                outputRow.ValueSatoshis;
                        }
                        else
                        {
                            Int64 ledgerCents =
                                new Money(outputRow.ValueSatoshis, Currency.BitcoinCore, ourTx.DateTime).ToCurrency(
                                    organization.Currency).Cents;
                            transactionReconstructedRows[financialAccountId] +=
                                ledgerCents;
                            satoshisLookup[ledgerCents] = outputRow.ValueSatoshis;
                        }
                    }
                }

                // -- fees

                if (addressAccountLookup.ContainsKey(blockchainTx.Inputs[0].Address))
                {
                    // if the first input is ours, we're paying the fee (is there any case where this is not true?)

                    if (organizationLedgerUsesBitcoin)
                    {
                        transactionReconstructedRows[organization.FinancialAccounts.CostsBitcoinFees.Identity] =
                            blockchainTx.FeeSatoshis;
                    }
                    else
                    {
                        Int64 feeLedgerCents =
                            new Money(blockchainTx.FeeSatoshis, Currency.BitcoinCore,
                                      blockchainTx.TransactionDateTimeUtc).ToCurrency(organization.Currency).Cents;
                        transactionReconstructedRows[organization.FinancialAccounts.CostsBitcoinFees.Identity] =
                            feeLedgerCents;
                    }
                }


                // Rewrite the transaction (called always, but the function won't do anything if everything matches)

                ourTx.RecalculateTransaction(transactionReconstructedRows, /* loggingPerson*/ null);

                // Finally, add foreign cents, if any

                if (!organizationLedgerUsesBitcoin)
                {
                    foreach (FinancialTransactionRow row in ourTx.Rows)
                    {
                        if (addressAccountLookup.ContainsValue(row.Account.Identity))  // "ContainsValue" is bad, but n is low
                        {
                            // Do we have a foreign amount for this row already?

                            Money foreignMoney = row.AmountForeignCents;
                            if (foreignMoney == null || foreignMoney.Cents == 0)
                            {
                                // no we didn't; create one

                                if (satoshisLookup.ContainsKey(row.AmountCents))
                                {
                                    row.AmountForeignCents = new Money(satoshisLookup[row.AmountCents], Currency.BitcoinCore, ourTx.DateTime);
                                }
                                else if (satoshisLookup.ContainsKey(-row.AmountCents))  // the negative counterpart
                                {
                                    row.AmountForeignCents = new Money(-satoshisLookup[-row.AmountCents], Currency.BitcoinCore, ourTx.DateTime);
                                }
                                else
                                {
                                    // There's a last case which may happen if the row is an addition to a previous row; if so, calculate
                                    row.AmountForeignCents = new Money(row.AmountCents, organization.Currency, ourTx.DateTime).ToCurrency(Currency.BitcoinCore);
                                }
                            }
                        }
                    }
                }
            }
        }
Beispiel #35
0
        public static Salary Create(PayrollItem payrollItem, DateTime payoutDate)
        {
            // Load the existing adjustments.

            PayrollAdjustments adjustments = PayrollAdjustments.ForPayrollItem(payrollItem);

            Int64 payCents = payrollItem.BaseSalaryCents;

            // Apply all before-tax adjustments

            foreach (PayrollAdjustment adjustment in adjustments)
            {
                if (adjustment.Type == PayrollAdjustmentType.GrossAdjustment)
                {
                    payCents += adjustment.AmountCents;
                }
            }

            Int64 subtractiveTaxCents = 0;
            Int64 additiveTaxCents    = 0;

            if (!payrollItem.IsContractor)
            {
                // calculate tax

                Money grossInOrgCurrency = new Money
                {
                    Cents             = payCents,
                    Currency          = payrollItem.Organization.Currency,
                    ValuationDateTime = DateTime.UtcNow
                };

                Money grossInTaxCurrency = grossInOrgCurrency.ToCurrency(payrollItem.Country.Currency);

                Money subtractiveTax = TaxLevels.GetTax(payrollItem.Country, payrollItem.SubtractiveTaxLevelId,
                                                        grossInTaxCurrency);

                Money subtractiveTaxInOrgCurrency = subtractiveTax.ToCurrency(payrollItem.Organization.Currency);

                subtractiveTaxCents = (Int64)(subtractiveTaxInOrgCurrency.Cents);

                additiveTaxCents = (Int64)(payCents * payrollItem.AdditiveTaxLevel);

                payCents -= subtractiveTaxCents;
            }

            // Apply all after-tax adjustments

            foreach (PayrollAdjustment adjustment in adjustments)
            {
                if (adjustment.Type == PayrollAdjustmentType.NetAdjustment)
                {
                    payCents += adjustment.AmountCents;
                }
            }

            // If net is negative, create rollover adjustment

            PayrollAdjustment rolloverAdjustment = null;

            if (payCents < 0)
            {
                rolloverAdjustment = PayrollAdjustment.Create(payrollItem, PayrollAdjustmentType.NetAdjustment,
                                                              -payCents,
                                                              "Deficit rolls over to next salary");

                PayrollAdjustment.Create(payrollItem, PayrollAdjustmentType.NetAdjustment,
                                         payCents, "Deficit rolled over from " +
                                         payoutDate.ToString("yyyy-MM-dd"));

                // keep second rollover open, so the deficit from this salary is carried to the next

                payCents = 0;
            }

            // Create salary, close adjustments

            Salary salary = Create(payrollItem, payoutDate, payCents, subtractiveTaxCents, additiveTaxCents);

            // For each adjustment, close and bind to salary

            foreach (PayrollAdjustment adjustment in adjustments)
            {
                adjustment.Close(salary);
            }

            if (rolloverAdjustment != null)
            {
                rolloverAdjustment.Close(salary);
            }

            // Add the financial transaction

            FinancialTransaction transaction =
                FinancialTransaction.Create(payrollItem.OrganizationId, DateTime.Now,
                                            "Salary #" + salary.Identity + ": " + payrollItem.PersonCanonical +
                                            " " +
                                            salary.PayoutDate.ToString("yyyy-MMM", CultureInfo.InvariantCulture));

            transaction.AddRow(payrollItem.Budget, salary.CostTotalCents, null);
            transaction.AddRow(payrollItem.Organization.FinancialAccounts.DebtsSalary, -salary.NetSalaryCents, null);
            if (salary.TaxTotalCents != 0)
            {
                transaction.AddRow(payrollItem.Organization.FinancialAccounts.DebtsTax, -salary.TaxTotalCents, null);
            }
            transaction.Dependency = salary;

            // Finally, check if net and/or tax are zero, and if so, mark them as already-paid (i.e. not due for payment)

            if (salary.NetSalaryCents == 0)
            {
                salary.NetPaid = true;
            }

            if (salary.TaxTotalCents == 0)
            {
                salary.TaxPaid = true;
            }

            // Clear a cache
            FinancialAccount.ClearAttestationAdjustmentsCache(payrollItem.Organization);



            return(salary);
        }
Beispiel #36
0
 public void SetBudget (FinancialAccount budget, Person settingPerson)
 {
     base.BudgetId = budget.Identity;
     SwarmDb.GetDatabaseForWriting().SetInboundInvoiceBudget(this.Identity, budget.Identity);
     UpdateTransaction(settingPerson);
 }
Beispiel #37
0
        public static VatReport Create(Organization organization, int year, int startMonth, int monthCount)
        {
            VatReport newReport = CreateDbRecord(organization, year, startMonth, monthCount);
            DateTime  endDate   = new DateTime(year, startMonth, 1).AddMonths(monthCount);

            FinancialAccount vatInbound  = organization.FinancialAccounts.AssetsVatInboundUnreported;
            FinancialAccount vatOutbound = organization.FinancialAccounts.DebtsVatOutboundUnreported;
            FinancialAccount sales       = organization.FinancialAccounts.IncomeSales;

            FinancialAccountRows inboundRows  = RowsNotInVatReport(vatInbound, endDate);
            FinancialAccountRows outboundRows = RowsNotInVatReport(vatOutbound, endDate);
            FinancialAccountRows turnoverRows = RowsNotInVatReport(sales, endDate);

            Dictionary <int, bool> transactionsIncludedLookup = new Dictionary <int, bool>();

            newReport.AddVatReportItemsFromAccountRows(inboundRows, transactionsIncludedLookup);
            newReport.AddVatReportItemsFromAccountRows(outboundRows, transactionsIncludedLookup);
            newReport.AddVatReportItemsFromAccountRows(turnoverRows, transactionsIncludedLookup);

            newReport.Release();

            // Create financial TX that moves this VAT from unreported to reported

            Int64 differenceCents = newReport.VatInboundCents - newReport.VatOutboundCents;

            if (differenceCents != 0 && newReport.VatInboundCents > 0)
            {
                // if there's anything to report

                FinancialTransaction vatReportTransaction = FinancialTransaction.Create(organization, endDate.AddDays(4).AddHours(9),
                                                                                        newReport.Description);

                if (newReport.VatInboundCents > 0)
                {
                    vatReportTransaction.AddRow(organization.FinancialAccounts.AssetsVatInboundUnreported,
                                                -newReport.VatInboundCents, null);
                }
                if (newReport.VatOutboundCents > 0)
                {
                    vatReportTransaction.AddRow(organization.FinancialAccounts.DebtsVatOutboundUnreported,
                                                newReport.VatOutboundCents, null);
                    // not negative, because our number is sign-different from the bookkeeping's
                }

                if (differenceCents < 0) // outbound > inbound
                {
                    vatReportTransaction.AddRow(organization.FinancialAccounts.DebtsVatOutboundReported,
                                                differenceCents, null); // debt, so negative as in our variable
                }
                else // inbound > outbound
                {
                    vatReportTransaction.AddRow(organization.FinancialAccounts.AssetsVatInboundReported,
                                                differenceCents, null); // asset, so positive as in our variable
                }

                vatReportTransaction.Dependency = newReport;
                newReport.OpenTransaction       = vatReportTransaction;
            }
            else
            {
                newReport.Open = false; // nothing to close, no tx created
            }

            return(newReport);
        }
Beispiel #38
0
 public void SetBudget(FinancialAccount newBudget, Person settingPerson)
 {
     this.Budget = newBudget; // ignore settingPerson
 }
Beispiel #39
0
        public static OutboundComm CreateNotificationOfFinancialValidation(FinancialAccount budget, Person concernedPerson, double amountRequested, string purpose, NotificationResource notification)
        {
            NotificationPayload payload = new NotificationPayload(notification.ToString());
            payload.Strings[NotificationString.CurrencyCode] = budget.Organization.Currency.Code;
            payload.Strings[NotificationString.OrganizationName] = budget.Organization.Name;
            payload.Strings[NotificationString.BudgetAmountFloat] = amountRequested.ToString(CultureInfo.InvariantCulture);
            payload.Strings[NotificationString.RequestPurpose] = purpose;
            payload.Strings[NotificationString.BudgetName] = budget.Name;
            payload.Strings[NotificationString.ConcernedPersonName] = concernedPerson.Canonical;

            OutboundComm comm = OutboundComm.Create(null, null, budget.Organization, CommResolverClass.Unknown, null,
                                                    CommTransmitterClass.CommsTransmitterMail,
                                                    new PayloadEnvelope(payload).ToXml(),
                                                    OutboundCommPriority.Low);

            comm.AddRecipient(concernedPerson);

            comm.Resolved = true;

            return comm;

        }
Beispiel #40
0
 public static FinancialAccount Create(Organization organization, string newAccountName,
                                       FinancialAccountType accountType, FinancialAccount parentAccount)
 {
     return(Create(organization.Identity, newAccountName, accountType,
                   parentAccount == null ? 0 : parentAccount.Identity));
 }
 public FinancialTransactionRow AddRow(FinancialAccount account, Int64 amountCents, Person person)
 {
     return(AddRow(account.Identity, amountCents, person != null ? person.Identity : 0));
 }