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())); }
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(); } }
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)); } } } }
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()); }
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()); } } }
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())); } } } }
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(); }
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); } }
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)); } } } }
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); */ }
// 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."); } } } } }
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"); }
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); } }
// 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."); } } } } }