Esempio n. 1
0
        public Organizations GetOrganizations(RoleType[] forRoles)
        {
            if (HasRoleType(RoleType.SystemAdmin))
            {
                return(Organizations.GetAll());
            }

            List <int> orgIdentities = new List <int>();

            foreach (BasicPersonRole role in OrganizationPersonRoles)
            {
                if (Array.IndexOf(forRoles, role.Type) >= 0)
                {
                    orgIdentities.Add(role.OrganizationId);
                }
            }

            foreach (BasicPersonRole role in LocalPersonRoles)
            {
                if (Array.IndexOf(forRoles, role.Type) >= 0)
                {
                    orgIdentities.Add(role.OrganizationId);
                }
            }

            return(Organizations.FromIdentities(orgIdentities.ToArray()));
        }
Esempio n. 2
0
 protected void Page_Load(object sender, EventArgs e)
 {
     if (_authority.HasPermission(Permission.CanEditMemberships, Organization.PPFIid, Geography.FinlandId, Authorization.Flag.ExactGeographyExactOrganization))
     {
         loadCntry = Country.FromCode("FI");
         geotree   = loadCntry.Geography.GetTree();
         nowValue  = DateTime.Now;
         if (!IsPostBack)
         {
             foreach (Organization org in Organizations.GetAll())
             {
                 if (org.DefaultCountryId == loadCntry.Identity)
                 {
                     DropDownListOrgs.Items.Add(new ListItem(org.Name, "" + org.Identity));
                 }
             }
         }
         else
         {
             currentOrg = Organization.FromIdentity(Convert.ToInt32(DropDownListOrgs.SelectedValue));
         }
     }
     else
     {
         Response.Write("Access not allowed");
         Response.End();
     }
 }
Esempio n. 3
0
        internal static void RemindExpiries(DateTime dateExpiry)
        {
            Organizations orgs = Organizations.GetAll();

            foreach (Organization org in orgs)
            {
                Memberships memberships = Memberships.GetExpiring(org, dateExpiry);

                // Mail each expiring member

                foreach (Membership membership in memberships)
                {
                    try
                    {
                        SendReminderMail(membership);
                        PWLog.Write(PWLogItem.Person, membership.PersonId,
                                    PWLogAction.MembershipRenewReminder,
                                    "Mail was sent to " + membership.Person.Mail +
                                    " reminding to renew membership in " + membership.Organization.Name + ".", string.Empty);
                    }
                    catch (Exception ex)
                    {
                        ExceptionMail.Send(
                            new Exception("Failed to create reminder mail for person " + membership.PersonId, ex));
                    }
                }
            }
        }
Esempio n. 4
0
        public static Org[] Select()
        {
            List <Org> res = new List <Org>();

            Organizations orgs = Organizations.GetAll();

            foreach (Organization org in orgs)
            {
                res.Add(new Org(org));
            }

            return(res.ToArray());
        }
Esempio n. 5
0
        private static void OnMondayMorning()
        {
            try
            {
                Dictionary <int, bool> accountTested = new Dictionary <int, bool>();

                // Check the bitcoin hotwallets for forex profit/loss.

                Organizations allOrganizations = Organizations.GetAll();

                foreach (Organization organization in allOrganizations)
                {
                    FinancialAccount hotWalletAccount = organization.FinancialAccounts.AssetsBitcoinHot;

                    if (hotWalletAccount != null)
                    {
                        BitcoinUtility.CheckHotwalletForexProfitLoss(hotWalletAccount);
                        accountTested[hotWalletAccount.Identity] = true;
                    }
                }

                // Detect and log any forex difference exceeding 100 cents.

                FinancialAccounts allAccounts = FinancialAccounts.GetAll(); // across ALL ORGS!

                foreach (FinancialAccount account in allAccounts)
                {
                    // For every account, if it's based on foreign currency, and not already checked,
                    // then check for forex gains/losses

                    if (account.ForeignCurrency != null && !accountTested.ContainsKey(account.Identity))
                    {
                        account.CheckForexProfitLoss();
                    }
                }
            }
            catch (Exception e)
            {
                ExceptionMail.Send(e, true);
                if (testMode)
                {
                    Console.WriteLine(e.ToString());
                }
            }
        }
Esempio n. 6
0
    private void PopulateOrganizations()
    {
        dropOrganizations.Items.Clear();

        Person person = Person.FromIdentity(Convert.ToInt32("" + Request["id"]));

        Memberships memberships = person.GetMemberships();

        // Generate combined list of orgs: available at this geography vs. available through this authority


        //Organizations organizations = Organizations.GetAllOrganizationsAvailableAtGeography(person.GeographyId);
        Organizations organizations  = Organizations.GetAll();
        Organizations authorizedOrgs = _authority.GetOrganizations(RoleTypes.AllRoleTypes).ExpandAll();

        organizations = organizations.LogicalAnd(authorizedOrgs);


        // Which orgs is the person already a member of?

        foreach (BasicOrganization organization in organizations)
        {
            if (organization.AcceptsMembers)
            {
                bool isMemberAlready = false;

                foreach (Membership membership in memberships)
                {
                    if (membership.OrganizationId == organization.OrganizationId)
                    {
                        isMemberAlready = true;
                    }
                }

                if (!isMemberAlready)
                {
                    this.dropOrganizations.Items.Add(new ListItem(organization.Name,
                                                                  organization.OrganizationId.ToString()));
                }
            }
        }
    }
Esempio n. 7
0
        private static void OnMondayMorning()
        {
            try
            {
                // Detect and log any forex difference exceeding 100 cents.

                FinancialAccounts allAccounts = FinancialAccounts.GetAll(); // across ALL ORGS!

                foreach (FinancialAccount account in allAccounts)
                {
                    // For every account, if it's based on foreign currency, check for forex gains/losses

                    if (account.ForeignCurrency != null)
                    {
                        account.CheckForexProfitLoss();
                    }
                }

                // Also check the bitcoin hotwallets for forex profit/loss as a special case of the above.

                Organizations allOrganizations = Organizations.GetAll();

                foreach (Organization organization in allOrganizations)
                {
                    FinancialAccount hotWalletAccount = organization.FinancialAccounts.AssetsBitcoinHot;

                    if (hotWalletAccount != null)
                    {
                        BitcoinUtility.CheckHotwalletForexProfitLoss(hotWalletAccount);
                    }
                }
            }
            catch (Exception e)
            {
                ExceptionMail.Send(e, true);
                if (testMode)
                {
                    Console.WriteLine(e.ToString());
                }
            }
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            Response.ContentType = "application/json";


            /*
             * ignore cache - reconstruct every time, this is too individual and volatile
             *
             * // Is this stuff in cache already?
             *
             * string cacheKey = "ExpensableBudgets-Json-" +
             *                this.CurrentOrganization.Identity.ToString((CultureInfo.InvariantCulture));
             *
             * string accountsJson =
             *  (string) Cache[cacheKey];
             *
             * if (accountsJson != null)
             * {
             *  Response.Output.WriteLine(accountsJson);
             *  Response.End();
             *  return;
             * }
             */


            // Not in cache. Construct.

            // Get organizations

            Organizations allOrganizations = Organizations.GetAll();

            // List accessible organizations. That would be those where this person:
            // a) has a role of responsibility;
            // b) is a member or other form of minion;
            // c) has a role of responsibility in a parent organization;
            // d) has a system-wide role that makes it necessary to access orgs.

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

            Dictionary <int, Organizations> treeMap = new Dictionary <int, Organizations>();

            foreach (Organization organization in allOrganizations)
            {
                if (!treeMap.ContainsKey(organization.ParentIdentity))
                {
                    treeMap[organization.ParentIdentity] = new Organizations();
                }

                treeMap[organization.ParentIdentity].Add(organization);
            }

            Response.Output.WriteLine(RecurseTreeMap(treeMap, 0));

            /* TODO: Cache, if master installation grows large
             *
             * Cache.Insert(cacheKey, accountsJson, null, DateTime.Now.AddMinutes(5), TimeSpan.Zero);
             * // cache lasts for five minutes, no sliding expiration
             * Response.Output.WriteLine(accountsJson); */

            Response.End();
        }
Esempio n. 9
0
        public static void PerformAutomated(BitcoinChain chain)
        {
            // Perform all waiting hot payouts for all orgs in the installation

            throw new NotImplementedException("Waiting for rewrite for Bitcoin Cash");

            // TODO

            DateTime utcNow = DateTime.UtcNow;

            foreach (Organization organization in Organizations.GetAll())
            {
                // If this org doesn't do hotwallet, continue
                if (organization.FinancialAccounts.AssetsBitcoinHot == null)
                {
                    continue;
                }

                Payouts orgPayouts     = Payouts.Construct(organization);
                Payouts bitcoinPayouts = new Payouts();
                Dictionary <string, Int64> satoshiPayoutLookup     = new Dictionary <string, long>();
                Dictionary <string, Int64> nativeCentsPayoutLookup = new Dictionary <string, long>();
                Dictionary <int, Int64>    satoshiPersonLookup     = new Dictionary <int, long>();
                Dictionary <int, Int64>    nativeCentsPersonLookup = new Dictionary <int, long>();
                Int64 satoshisTotal = 0;

                string currencyCode = organization.Currency.Code;

                // For each ready payout that can automate, add an output to a constructed transaction

                TransactionBuilder txBuilder = null; // TODO TODO TODO TODO new TransactionBuilder();
                foreach (Payout payout in orgPayouts)
                {
                    if (payout.ExpectedTransactionDate > utcNow)
                    {
                        continue; // payout is not due yet
                    }

                    if (payout.RecipientPerson != null && payout.RecipientPerson.BitcoinPayoutAddress.Length > 2 &&
                        payout.Account.Length < 4)
                    {
                        // If the payout address is still in quarantine, don't pay out yet

                        string addressSetTime = payout.RecipientPerson.BitcoinPayoutAddressTimeSet;
                        if (addressSetTime.Length > 4 && DateTime.Parse(addressSetTime, CultureInfo.InvariantCulture).AddHours(48) > utcNow)
                        {
                            continue; // still in quarantine
                        }

                        // Test the payout address - is it valid and can we handle it?

                        if (!BitcoinUtility.IsValidBitcoinAddress(payout.RecipientPerson.BitcoinPayoutAddress))
                        {
                            // Notify person that address is invalid, then clear it

                            NotificationStrings       primaryStrings   = new NotificationStrings();
                            NotificationCustomStrings secondaryStrings = new NotificationCustomStrings();
                            primaryStrings[NotificationString.OrganizationName] = organization.Name;
                            secondaryStrings["BitcoinAddress"] = payout.RecipientPerson.BitcoinPayoutAddress;

                            OutboundComm.CreateNotification(organization, NotificationResource.BitcoinPayoutAddress_Bad,
                                                            primaryStrings, secondaryStrings,
                                                            People.FromSingle(payout.RecipientPerson));

                            payout.RecipientPerson.BitcoinPayoutAddress = string.Empty;

                            continue; // do not add this payout
                        }

                        // Ok, so it seems we're making this payout at this time.

                        bitcoinPayouts.Add(payout);

                        int recipientPersonId = payout.RecipientPerson.Identity;

                        if (!satoshiPersonLookup.ContainsKey(recipientPersonId))
                        {
                            satoshiPersonLookup[recipientPersonId]     = 0;
                            nativeCentsPersonLookup[recipientPersonId] = 0;
                        }

                        nativeCentsPersonLookup[recipientPersonId] += payout.AmountCents;

                        // Find the amount of satoshis for this payout

                        if (organization.Currency.IsBitcoinCore)
                        {
                            satoshiPayoutLookup[payout.ProtoIdentity]     = payout.AmountCents;
                            nativeCentsPayoutLookup[payout.ProtoIdentity] = payout.AmountCents;
                            satoshisTotal += payout.AmountCents;
                            satoshiPersonLookup[recipientPersonId] += payout.AmountCents;
                        }
                        else
                        {
                            // Convert currency
                            Money payoutAmount = new Money(payout.AmountCents, organization.Currency);
                            Int64 satoshis     = payoutAmount.ToCurrency(Currency.BitcoinCore).Cents;
                            satoshiPayoutLookup[payout.ProtoIdentity]     = satoshis;
                            nativeCentsPayoutLookup[payout.ProtoIdentity] = payout.AmountCents;
                            satoshisTotal += satoshis;
                            satoshiPersonLookup[recipientPersonId] += satoshis;
                        }
                    }
                    else if (payout.RecipientPerson != null && payout.RecipientPerson.BitcoinPayoutAddress.Length < 3 && payout.Account.Length < 4)
                    {
                        // There is a payout for this person, but they don't have a bitcoin payout address set. Send notification to this effect once a day.

                        if (utcNow.Minute != 0)
                        {
                            continue;
                        }
                        if (utcNow.Hour != 12)
                        {
                            continue;
                        }

                        NotificationStrings primaryStrings = new NotificationStrings();
                        primaryStrings[NotificationString.OrganizationName] = organization.Name;
                        OutboundComm.CreateNotification(organization, NotificationResource.BitcoinPayoutAddress_PleaseSet, primaryStrings, People.FromSingle(payout.RecipientPerson));
                    }
                    else if (payout.Account.StartsWith("bitcoin:"))
                    {
                    }
                }

                if (bitcoinPayouts.Count == 0)
                {
                    // no automated payments pending for this organization, nothing more to do
                    continue;
                }

                // We now have our desired payments. The next step is to find enough inputs to reach the required amount (plus fees; we're working a little blind here still).

                BitcoinTransactionInputs inputs      = null;
                Int64 satoshisMaximumAnticipatedFees = BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(chain) * 20; // assume max 20k transaction size

                try
                {
                    inputs = BitcoinUtility.GetInputsForAmount(organization, satoshisTotal + satoshisMaximumAnticipatedFees);
                }
                catch (NotEnoughFundsException)
                {
                    // If we're at the whole hour, send a notification to people responsible for refilling the hotwallet

                    if (utcNow.Minute != 0)
                    {
                        continue; // we're not at the whole hour, so continue with next org instead
                    }

                    // Send urgent notification to top up the damn wallet so we can make payments

                    NotificationStrings primaryStrings = new NotificationStrings();
                    primaryStrings[NotificationString.CurrencyCode]     = organization.Currency.Code;
                    primaryStrings[NotificationString.OrganizationName] = organization.Name;
                    NotificationCustomStrings secondaryStrings = new NotificationCustomStrings();
                    Int64 satoshisAvailable = HotBitcoinAddresses.ForOrganization(organization).BalanceSatoshisTotal;

                    secondaryStrings["AmountMissingMicrocoinsFloat"] =
                        ((satoshisTotal - satoshisAvailable + satoshisMaximumAnticipatedFees) / 100.0).ToString("N2");

                    if (organization.Currency.IsBitcoinCore)
                    {
                        secondaryStrings["AmountNeededFloat"] = ((satoshisTotal + satoshisMaximumAnticipatedFees) / 100.0).ToString("N2");
                        secondaryStrings["AmountWalletFloat"] = (satoshisAvailable / 100.0).ToString("N2");
                    }
                    else
                    {
                        // convert to org native currency

                        secondaryStrings["AmountNeededFloat"] =
                            (new Money(satoshisTotal, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents / 100.0).ToString("N2");
                        secondaryStrings["AmountWalletFloat"] =
                            (new Money(satoshisAvailable, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents / 100.0).ToString("N2");
                    }

                    OutboundComm.CreateNotification(organization,
                                                    NotificationResource.Bitcoin_Shortage_Critical, primaryStrings, secondaryStrings, People.FromSingle(Person.FromIdentity(1)));

                    continue; // with next organization
                }

                // If we arrive at this point, the previous function didn't throw, and we have enough money.
                // Ensure the existence of a cost account for bitcoin miner fees.

                organization.EnsureMinerFeeAccountExists();

                // Add the inputs to the transaction.

                txBuilder = txBuilder.AddCoins(inputs.Coins);
                txBuilder = txBuilder.AddKeys(inputs.PrivateKeys);
                Int64 satoshisInput = inputs.AmountSatoshisTotal;

                // Add outputs and prepare notifications

                Int64 satoshisUsed = 0;
                Dictionary <int, List <string> > notificationSpecLookup   = new Dictionary <int, List <string> >();
                Dictionary <int, List <Int64> >  notificationAmountLookup = new Dictionary <int, List <Int64> >();
                Payout            masterPayoutPrototype = Payout.Empty;
                HotBitcoinAddress changeAddress         = HotBitcoinAddress.OrganizationWalletZero(organization, BitcoinChain.Core); // TODO: CHAIN!

                foreach (Payout payout in bitcoinPayouts)
                {
                    int recipientPersonId = payout.RecipientPerson.Identity;
                    if (!notificationSpecLookup.ContainsKey(recipientPersonId))
                    {
                        notificationSpecLookup[recipientPersonId]   = new List <string>();
                        notificationAmountLookup[recipientPersonId] = new List <Int64>();
                    }
                    notificationSpecLookup[recipientPersonId].Add(payout.Specification);
                    notificationAmountLookup[recipientPersonId].Add(payout.AmountCents);

                    if (payout.RecipientPerson.BitcoinPayoutAddress.StartsWith("1"))  // regular address
                    {
                        txBuilder = txBuilder.Send(new BitcoinPubKeyAddress(payout.RecipientPerson.BitcoinPayoutAddress),
                                                   new Satoshis(satoshiPayoutLookup[payout.ProtoIdentity]));
                    }
                    else if (payout.RecipientPerson.BitcoinPayoutAddress.StartsWith("3"))  // multisig
                    {
                        txBuilder = txBuilder.Send(new BitcoinScriptAddress(payout.RecipientPerson.BitcoinPayoutAddress, Network.Main),
                                                   new Satoshis(satoshiPayoutLookup[payout.ProtoIdentity]));
                    }
                    else
                    {
                        throw new InvalidOperationException("Unhandled bitcoin address type in Payouts.PerformAutomated(): " + payout.RecipientPerson.BitcoinPayoutAddress);
                    }

                    satoshisUsed += satoshiPayoutLookup[payout.ProtoIdentity];

                    payout.MigrateDependenciesTo(masterPayoutPrototype);
                }

                // Set change address to wallet slush

                txBuilder.SetChange(new BitcoinPubKeyAddress(changeAddress.Address));

                // Add fee

                int transactionSizeBytes = txBuilder.EstimateSize(txBuilder.BuildTransaction(false)) + inputs.Count;
                // +inputs.Count for size variability

                Int64 feeSatoshis = (transactionSizeBytes / 1000 + 1) * BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(chain);

                txBuilder     = txBuilder.SendFees(new Satoshis(feeSatoshis));
                satoshisUsed += feeSatoshis;

                // Sign transaction - ready to execute

                Transaction txReady = txBuilder.BuildTransaction(true);

                // Verify that transaction is ready

                if (!txBuilder.Verify(txReady))
                {
                    // Transaction was not signed with the correct keys. This is a serious condition.

                    NotificationStrings primaryStrings = new NotificationStrings();
                    primaryStrings[NotificationString.OrganizationName] = organization.Name;

                    OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_PrivateKeyError,
                                                    primaryStrings);

                    throw new InvalidOperationException("Transaction is not signed enough");
                }

                // Broadcast transaction

                BitcoinUtility.BroadcastTransaction(txReady, BitcoinChain.Cash);

                // Note the transaction hash

                string transactionHash = txReady.GetHash().ToString();

                // Delete all old inputs, adjust balance for addresses (re-register unused inputs)

                inputs.AsUnspents.DeleteAll();

                // Log the new unspent created by change (if there is any)

                if (satoshisInput - satoshisUsed > 0)
                {
                    SwarmDb.GetDatabaseForWriting()
                    .CreateHotBitcoinAddressUnspentConditional(changeAddress.Identity, transactionHash,
                                                               +/* the change address seems to always get index 0? is this a safe assumption? */ 0, satoshisInput - satoshisUsed, /* confirmation count*/ 0);
                }

                // Register new balance of change address, should have increased by (satoshisInput-satoshisUsed)

                // TODO

                // Send notifications

                foreach (int personId in notificationSpecLookup.Keys)
                {
                    Person person = Person.FromIdentity(personId);

                    string spec = string.Empty;
                    for (int index = 0; index < notificationSpecLookup[personId].Count; index++)
                    {
                        spec += String.Format(" * {0,-40} {1,14:N2} {2,-4}\r\n", notificationSpecLookup[personId][index], notificationAmountLookup[personId][index] / 100.0, currencyCode);
                    }

                    spec = spec.TrimEnd();

                    NotificationStrings       primaryStrings   = new NotificationStrings();
                    NotificationCustomStrings secondaryStrings = new NotificationCustomStrings();

                    primaryStrings[NotificationString.OrganizationName]         = organization.Name;
                    primaryStrings[NotificationString.CurrencyCode]             = organization.Currency.DisplayCode;
                    primaryStrings[NotificationString.EmbeddedPreformattedText] = spec;
                    secondaryStrings["AmountFloat"]        = (nativeCentsPersonLookup[personId] / 100.0).ToString("N2");
                    secondaryStrings["BitcoinAmountFloat"] = (satoshiPersonLookup[personId] / 100.0).ToString("N2");
                    secondaryStrings["BitcoinAddress"]     = person.BitcoinPayoutAddress; // warn: potential rare race condition here

                    OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_PaidOut, primaryStrings,
                                                    secondaryStrings, People.FromSingle(person));
                }

                // Create the master payout from its prototype

                Payout masterPayout = Payout.CreateBitcoinPayoutFromPrototype(organization, masterPayoutPrototype, txReady.GetHash().ToString());

                // Finally, create ledger entries and notify

                NotificationStrings       masterPrimaryStrings   = new NotificationStrings();
                NotificationCustomStrings masterSecondaryStrings = new NotificationCustomStrings();

                masterPrimaryStrings[NotificationString.OrganizationName] = organization.Name;
                masterPrimaryStrings[NotificationString.CurrencyCode]     = organization.Currency.DisplayCode;
                masterSecondaryStrings["AmountFloat"] =
                    (new Swarmops.Logic.Financial.Money(satoshisUsed, Currency.BitcoinCore).ToCurrency(
                         organization.Currency).Cents / 100.0).ToString("N2", CultureInfo.InvariantCulture);
                masterSecondaryStrings["BitcoinAmountFloat"] = (satoshisUsed / 100.0).ToString("N2", CultureInfo.InvariantCulture);
                masterSecondaryStrings["PaymentCount"]       = bitcoinPayouts.Count.ToString("N0", CultureInfo.InvariantCulture);

                OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_Hotwallet_Outflow,
                                                masterPrimaryStrings, masterSecondaryStrings);

                // TODO: special case for native-bitcoin organizations vs. fiat-currency organizations

                FinancialTransaction ledgerTransaction = FinancialTransaction.Create(organization, utcNow,
                                                                                     "Bitcoin automated payout");

                if (organization.Currency.IsBitcoinCore)
                {
                    ledgerTransaction.AddRow(organization.FinancialAccounts.AssetsBitcoinHot, -(masterPayoutPrototype.AmountCents + feeSatoshis), null);
                    ledgerTransaction.AddRow(organization.FinancialAccounts.CostsBitcoinFees, feeSatoshis, null);
                }
                else
                {
                    // If the ledger isn't using bitcoin natively, we need to translate the miner fee paid to ledger cents before entering it into ledger

                    Int64 feeCentsLedger = new Money(feeSatoshis, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents;
                    ledgerTransaction.AddRow(organization.FinancialAccounts.AssetsBitcoinHot, -(masterPayoutPrototype.AmountCents + feeCentsLedger), null).AmountForeignCents = new Money(satoshisUsed, Currency.BitcoinCore);
                    ledgerTransaction.AddRow(organization.FinancialAccounts.CostsBitcoinFees, feeCentsLedger, null);
                }

                ledgerTransaction.BlockchainHash = transactionHash;

                masterPayout.BindToTransactionAndClose(ledgerTransaction, null);
            }
        }
Esempio n. 10
0
        public static void CreateAnnualStatements(int year)
        {
            // This function is supposed to be running once a year, and summarize the past year of salaries.

            // Get all organizations.

            Organizations organizations = Organizations.GetAll();

            foreach (Organization organization in organizations)
            {
                // Get all salaries for this organization (filter year in logic - this function is allowed to be expensive, rather than
                // adding a custom database query for this particular operation)

                Salaries allSalaries = ForOrganization(organization, true);

                Salaries yearSalaries = new Salaries();
                yearSalaries.AddRange(allSalaries.Where(salary => salary.PayoutDate.Year == year));

                Dictionary <int, Dictionary <int, Salary> > personSalaryLookup = new Dictionary <int, Dictionary <int, Salary> >();

                // Go through the salaries and store them in the dictionary, so we can process the statement by person later

                foreach (Salary salary in yearSalaries)
                {
                    int personId = salary.PayrollItem.PersonId;

                    if (!personSalaryLookup.ContainsKey(personId))
                    {
                        personSalaryLookup[personId] = new Dictionary <int, Salary>();
                    }

                    personSalaryLookup[personId][salary.PayoutDate.Month] = salary;
                }

                // Once here, salaries are arranged by person and month. Iterate over people, create statements.

                foreach (int personId in personSalaryLookup.Keys)
                {
                    Person person      = Person.FromIdentity(personId);
                    Int64  grossTotal  = 0;
                    Int64  subTaxTotal = 0;
                    Int64  addTaxTotal = 0;
                    Int64  netTotal    = 0;

                    string preFormattedStatement = string.Empty;

                    for (int monthNumber = 1; monthNumber <= 12; monthNumber++)
                    {
                        string monthName = new DateTime(year, monthNumber, 1).ToString("MMM",
                                                                                       new CultureInfo(person.PreferredCulture));
                        monthName = monthName.Substring(0, 3);  // believe it or not some .Net localizations don't respect the three letter limit
                        string lineItem = "  " + monthName;

                        if (personSalaryLookup[personId].ContainsKey(monthNumber) && personSalaryLookup[personId][monthNumber].GrossSalaryCents > 0)
                        {
                            Salary monthSalary = personSalaryLookup[personId][monthNumber];

                            // TODO: replace "bitcoin" with actual payout method - this is just for show at the moment

                            lineItem += String.Format("  {0,10:N2}  {1,11:N2}  {2,10:N2}  bitcoin",
                                                      monthSalary.GrossSalaryCents / 100.0, -monthSalary.SubtractiveTaxCents / 100.0,
                                                      monthSalary.NetSalaryCents / 100.0);

                            grossTotal  += monthSalary.GrossSalaryCents;
                            addTaxTotal += monthSalary.AdditiveTaxCents;
                            subTaxTotal += monthSalary.SubtractiveTaxCents;
                            netTotal    += monthSalary.NetSalaryCents;
                        }

                        preFormattedStatement += lineItem + "\r\n";
                    }

                    NotificationStrings       notificationStrings = new NotificationStrings();
                    NotificationCustomStrings customStrings       = new NotificationCustomStrings();

                    notificationStrings[NotificationString.CurrencyCode]             = organization.Currency.DisplayCode;
                    notificationStrings[NotificationString.EmbeddedPreformattedText] = preFormattedStatement;
                    customStrings["LastYear"]                 = year.ToString(CultureInfo.InvariantCulture);
                    customStrings["GrossSalaryTotal"]         = String.Format("{0,10:N2}", grossTotal / 100.0);
                    customStrings["TaxDeductedTotal"]         = String.Format("{0,11:N2}", -subTaxTotal / 100.0);
                    customStrings["NetSalaryTotal"]           = String.Format("{0,10:N2}", netTotal / 100.0);
                    customStrings["TaxAdditiveTotalUnpadded"] = String.Format("{0:N2}", addTaxTotal / 100.0);

                    // Send notification if gross is nonzero

                    if (grossTotal > 0)
                    {
                        OutboundComm.CreateNotification(organization, NotificationResource.Salary_LastYearSummary,
                                                        notificationStrings, customStrings, People.FromSingle(person));
                    }
                }
            }
        }
Esempio n. 11
0
        public static void RemindExpiriesMail()
        {
            // Get expiring

            Console.WriteLine("Inside RemindExpiriesMail()");

            Organizations orgs = Organizations.GetAll();

            Dictionary <int, bool>   personLookup = new Dictionary <int, bool>();
            Dictionary <string, int> dateLookup   = new Dictionary <string, int>();

            DateTime      lowerBound      = DateTime.Today;
            DateTime      upperBound      = lowerBound.AddDays(31);
            List <string> failedReminders = new List <string>();

            int weekDayInteger = (int)DateTime.Today.DayOfWeek;

            foreach (Organization org in orgs)
            {
                Memberships memberships = Memberships.GetExpiring(org, lowerBound, upperBound);

                foreach (Membership membership in memberships)
                {
                    if (membership.OrganizationId % 7 != weekDayInteger)
                    {
                        continue;
                    }

                    try
                    {
                        Console.Write("Reminding " + membership.Person.Canonical + " about " +
                                      membership.Organization.Name + ".");
                        SendReminderMail(membership);
                        Console.Write(".");
                        PWLog.Write(PWLogItem.Person, membership.PersonId,
                                    PWLogAction.MembershipRenewReminder,
                                    "Mail was sent to " + membership.Person.Mail +
                                    " reminding to renew membership in " + membership.Organization.Name + ".", string.Empty);

                        Console.Write(".");

                        string dateString = membership.Expires.ToString("yyyy-MM-dd");
                        if (!dateLookup.ContainsKey(dateString))
                        {
                            dateLookup[dateString] = 0;
                        }
                        dateLookup[dateString]++;

                        Console.WriteLine(" done.");
                    }
                    catch (Exception x)
                    {
                        string logText = "FAILED sending mail to " + membership.Person.Mail +
                                         " for reminder of pending renewal in " + membership.Organization.Name + ".";
                        failedReminders.Add(membership.Person.Canonical);
                        PWLog.Write(PWLogItem.Person, membership.PersonId,
                                    PWLogAction.MembershipRenewReminder,
                                    logText, string.Empty);
                        ExceptionMail.Send(new Exception(logText, x));
                    }
                }
            }

            string notifyBody = String.Format("Sending renewal reminders to {0} people:\r\n\r\n", personLookup.Count);

            Console.WriteLine("Sending renewal reminders to {0} people", personLookup.Count);

            List <string> dateSummary = new List <string>();
            int           total       = 0;

            foreach (string dateString in dateLookup.Keys)
            {
                dateSummary.Add(string.Format("{0}: {1,5}", dateString, dateLookup[dateString]));
                total += dateLookup[dateString];
            }

            dateSummary.Sort();

            foreach (string dateString in dateSummary)
            {
                notifyBody += dateString + "\r\n";
                Console.WriteLine(dateString);
            }

            notifyBody += string.Format("Total sent: {0,5}\r\n\r\n", total);
            Console.WriteLine("Total sent: {0,5}\r\n\r\n", total);

            notifyBody += "FAILED reminders:\r\n";
            Console.WriteLine("FAILED reminders:");

            foreach (string failed in failedReminders)
            {
                notifyBody += failed + "\r\n";
                Console.WriteLine(failed);
            }

            if (failedReminders.Count == 0)
            {
                notifyBody += "none.\r\n";
                Console.WriteLine("none.");
            }

            /* no. just no. we should do a global search for "FromIdentity(1)"
             * Person.FromIdentity(1).SendOfficerNotice("Reminders sent today", notifyBody, 1);  */
        }
Esempio n. 12
0
        // This should run daily, suggested right after midnight.

        public static void ChurnExpiredMembers()
        {
            Organizations organizations = Organizations.GetAll();

            foreach (Organization organization in organizations)
            {
                Memberships memberships = Memberships.GetExpired(organization);
                // Mail each expiring member

                foreach (Membership membership in memberships)
                {
                    //only remove expired memberships
                    if (membership.Expires > DateTime.Now.Date)
                    {
                        continue;
                    }

                    Person person = membership.Person;

                    // Remove all roles and responsibilities for this person in the org

                    Authority authority = person.GetAuthority();

                    foreach (BasicPersonRole basicRole in authority.LocalPersonRoles)
                    {
                        PersonRole personRole = PersonRole.FromBasic(basicRole);
                        if (personRole.OrganizationId == membership.OrganizationId)
                        {
                            PWEvents.CreateEvent(EventSource.PirateBot, EventType.DeletedRole, person.Identity,
                                                 personRole.OrganizationId, personRole.GeographyId, person.Identity,
                                                 (int)personRole.Type,
                                                 string.Empty);
                            personRole.Delete();
                        }
                    }

                    // Mail

                    Memberships personMemberships   = person.GetMemberships();
                    Memberships membershipsToDelete = new Memberships();
                    foreach (Membership personMembership in personMemberships)
                    {
                        if (personMembership.Expires <= DateTime.Now.Date)
                        {
                            membershipsToDelete.Add(personMembership);
                        }
                    }


                    ExpiredMail expiredmail    = new ExpiredMail();
                    string      membershipsIds = "";

                    if (membershipsToDelete.Count > 1)
                    {
                        foreach (Membership personMembership in membershipsToDelete)
                        {
                            membershipsIds += "," + personMembership.MembershipId;
                        }
                        membershipsIds = membershipsIds.Substring(1);
                        string expiredMemberships = "";
                        foreach (Membership personMembership in membershipsToDelete)
                        {
                            if (personMembership.OrganizationId != organization.Identity)
                            {
                                expiredMemberships += ", " + personMembership.Organization.Name;
                            }
                        }
                        expiredMemberships      += ".  ";
                        expiredmail.pMemberships = expiredMemberships.Substring(2).Trim();
                    }

                    //TODO: URL for renewal, recieving end of this is NOT yet implemented...
                    // intended to recreate the memberships in MID
                    string tokenBase = person.PasswordHash + "-" + membership.Expires.Year;
                    string stdLink   = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?MemberId=" +
                                       person.Identity +
                                       "&SecHash=" + SHA1.Hash(tokenBase).Replace(" ", "").Substring(0, 8) +
                                       "&MID=" + membershipsIds;

                    expiredmail.pStdRenewLink = stdLink;
                    expiredmail.pOrgName      = organization.MailPrefixInherited;

                    person.SendNotice(expiredmail, organization.Identity);

                    person.DeleteSubscriptionData();

                    string orgIdString = string.Empty;

                    foreach (Membership personMembership in membershipsToDelete)
                    {
                        if (personMembership.Active)
                        {
                            orgIdString += " " + personMembership.OrganizationId;

                            personMembership.Terminate(EventSource.PirateBot, null, "Member churned in housekeeping.");
                        }
                    }
                }
            }
        }
Esempio n. 13
0
        private static void OnNoon()
        {
            BotLog.Write(0, "MainCycle", "Noon entry");

            try
            {
                if (!PilotInstallationIds.IsPilot(PilotInstallationIds.PiratePartySE))
                {
                    // PPSE is still running PW4 code for this, so don't run for PPSE

                    Payroll.ProcessMonthly(); // will only actually run on the 1st, but no harm in testing every noon
                }

                // Check all bitcoin accounts for previously-unseen transactions once a day

                Organizations allOrganizations = Organizations.GetAll();
                foreach (Organization organization in allOrganizations)
                {
                    // this actually checks hot storage too, but that's supposed
                    // to be up to date since we're the ones handling it
                    BitcoinUtility.CheckColdStorageForOrganization(organization);
                }

                if (!testMode)
                {
                    /*TestTrace("Running RosterHousekeeping.RemindAllExpiries()...");
                     * RosterHousekeeping.RemindAllExpiries();
                     * TestTrace(" done.\r\n");*/
                }
            }
            catch (Exception e)
            {
                TraceAndReport(e);
            }

            try
            {
                if (!testMode)
                {
                    /*TestTrace("Running SupportDatabase.NotifyBouncingEmails()...");
                     * SupportDatabase.NotifyBouncingEmails();
                     * TestTrace(" done.\r\n");*/
                }
            }
            catch (Exception e)
            {
                TraceAndReport(e);
            }

            try
            {
                /*TestTrace("Running SupportDatabase.CloseDelayWarnings()...");
                 * SupportDatabase.CloseDelayWarnings();
                 * TestTrace(" done.\r\n");*/
            }
            catch (Exception e)
            {
                TraceAndReport(e);
            }

            try
            {
                /*TestTrace("Running SupportMailReview.Run()...");
                 * SupportMailReview.Run();
                 * TestTrace(" done.\r\n");*/
            }
            catch (Exception e)
            {
                TraceAndReport(e);
            }


            BotLog.Write(0, "MainCycle", "Noon exit");
        }
Esempio n. 14
0
        private static void OnEveryHour()
        {
            try
            {
                BotLog.Write(0, "MainCycle", "One-hour entry");

                try
                {
                    // This will upgrade the database schema if and only if we failed to do so on entry.

                    if (SwarmDb.DbVersion < SwarmDb.DbVersionExpected)
                    {
                        BotLog.Write(1, "OneHour", "Entering DbUpgrade");
                        DatabaseMaintenance.UpgradeSchemata();
                        BotLog.Write(1, "OneHour", "Exited DbUpgrade");
                    }
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }


                try
                {
                    BotLog.Write(1, "OneHour", "Entering Automatch");
                    Organizations organizations = Organizations.GetAll();
                    foreach (Organization organization in organizations)
                    {
                        if (organization.IsEconomyEnabled)
                        {
                            BotLog.Write(1, "OneHour", "Automatching org #" + organization.Identity.ToString(CultureInfo.InvariantCulture));
                            Payouts.AutomatchAgainstUnbalancedTransactions(organization);
                        }
                    }
                    BotLog.Write(1, "OneHour", "Exited Automatch");

                    /*TestTrace("Running PaymentGroupMapper.Run()...");
                     * PaymentGroupMapper.Run();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                try
                {
                    /*TestTrace("Running BlogTop50Scraper.Run()...");
                     * BlogTop50Scraper.Run();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                try
                {
                    /*TestTrace("Running Mappery.CreatePiratpartietOrganizationStrengthCircuitMap()...");
                     * Mappery.CreatePiratpartietOrganizationStrengthCircuitMap();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                try
                {
                    /*TestTrace("Running Mappery.CreatePiratpartietOrganizationStrengthCityMap()...");
                     * Mappery.CreatePiratpartietOrganizationStrengthCityMap();
                     * TestTrace(" done.\r\n");*/
                }
                catch (Exception e)
                {
                    TraceAndReport(e);
                }

                BotLog.Write(0, "MainCycle", "One-hour exit");
            }
            catch (Exception e)
            {
                TraceAndReport(e);
            }
        }
Esempio n. 15
0
        // This should run daily, suggested right after midnight.

        public static void ChurnExpiredMembers()
        {
            Organizations organizations = Organizations.GetAll();

            foreach (Organization organization in organizations)
            {
                Participations participations = Participations.GetExpired(organization);
                // Mail each expiring member

                foreach (Participation membership in participations)
                {
                    //only remove expired memberships
                    if (membership.Expires > DateTime.Now.Date)
                    {
                        continue;
                    }

                    Person person = membership.Person;

                    // TODO: Check for positions that expire with membership


                    // Mail

                    Participations personParticipations   = person.GetParticipations();
                    Participations participationsToDelete = new Participations();
                    foreach (Participation personMembership in personParticipations)
                    {
                        if (personMembership.Expires <= DateTime.Now.Date)
                        {
                            participationsToDelete.Add(personMembership);
                        }
                    }


                    ExpiredMail expiredmail    = new ExpiredMail();
                    string      membershipsIds = "";

                    if (participationsToDelete.Count > 1)
                    {
                        foreach (Participation personMembership in participationsToDelete)
                        {
                            membershipsIds += "," + personMembership.MembershipId;
                        }
                        membershipsIds = membershipsIds.Substring(1);
                        string expiredMemberships = "";
                        foreach (Participation personMembership in participationsToDelete)
                        {
                            if (personMembership.OrganizationId != organization.Identity)
                            {
                                expiredMemberships += ", " + personMembership.Organization.Name;
                            }
                        }
                        expiredMemberships      += ".  ";
                        expiredmail.pMemberships = expiredMemberships.Substring(2).Trim();
                    }

                    //TODO: URL for renewal, recieving end of this is NOT yet implemented...
                    // intended to recreate the memberships in MID
                    string tokenBase = person.PasswordHash + "-" + membership.Expires.Year;
                    string stdLink   = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?MemberId=" +
                                       person.Identity +
                                       "&SecHash=" + SHA1.Hash(tokenBase).Replace(" ", "").Substring(0, 8) +
                                       "&MID=" + membershipsIds;

                    expiredmail.pStdRenewLink = stdLink;
                    expiredmail.pOrgName      = organization.MailPrefixInherited;

                    person.SendNotice(expiredmail, organization.Identity);

                    person.DeleteSubscriptionData();

                    string orgIdString = string.Empty;

                    foreach (Participation personMembership in participationsToDelete)
                    {
                        if (personMembership.Active)
                        {
                            orgIdString += " " + personMembership.OrganizationId;

                            personMembership.Terminate(EventSource.PirateBot, null, "Member churned in housekeeping.");
                        }
                    }
                }
            }
        }