protected void ButtonAutoRemap_Click(object sender, EventArgs e) { Payouts.AutomatchAgainstUnbalancedTransactions(Organization.FromIdentity(Int32.Parse(this.DropOrganizations.SelectedValue))); PopulateGrid(); PopulatePayouts(Int32.Parse(this.DropOrganizations.SelectedValue)); this.GridPayouts.Rebind(); this.GridTransactions.Rebind(); }
protected void Page_Load(object sender, EventArgs e) { this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.Bookkeeping, AccessType.Write); this.PageTitle = "Debug Ledgers"; this.PageIcon = "iconshock-tester"; // Initialize by mapping all // TODO: If this O(n^2) matching becomes teh suckage, optimize using hashtables over amounts Payouts.AutomatchAgainstUnbalancedTransactions(this.CurrentOrganization); // Iterate over all open payment groups and try to map them to unbalanced transactions FinancialTransactions unbalancedTransactions = FinancialTransactions.GetUnbalanced(this.CurrentOrganization); // TODO: this fn should move to Organization PaymentGroups openGroups = PaymentGroups.ForOrganization(this.CurrentOrganization, false); foreach (PaymentGroup openGroup in openGroups) { openGroup.MapTransaction(unbalancedTransactions); } }
protected void Page_Load(object sender, EventArgs e) { FinancialAccount bankAccount = FinancialAccount.FromIdentity(29); Organization euroPirates = Organization.FromIdentity(7); FinancialTransactions unbalancedTransactions = FinancialTransactions.GetUnbalanced(euroPirates); // assume all in bank account foreach (FinancialTransaction transaction in unbalancedTransactions) { if (transaction.Description[0] == 'H' && Char.IsDigit(transaction.Description[1]) && transaction.Rows.BalanceCentsDelta > 0) { // outbound intl invoice. Add an untracked intl invoice 10 days prior, map it to this OutboundInvoice newInvoice = OutboundInvoice.Create(euroPirates, transaction.DateTime, euroPirates.FinancialAccounts.IncomeSales, "Untracked", "*****@*****.**", string.Empty, euroPirates.Currency, false, string.Empty, Person.FromIdentity(1)); newInvoice.AddItem("Untracked item", transaction.Rows.BalanceCentsDelta); // Doesn't close // Add transaction // Create the financial transaction with rows FinancialTransaction invoiceTransaction = FinancialTransaction.Create(euroPirates.Identity, transaction.DateTime.AddDays(-10), "Outbound Invoice #" + newInvoice.Identity); invoiceTransaction.AddRow(euroPirates.FinancialAccounts.AssetsOutboundInvoices, newInvoice.AmountCents, null); invoiceTransaction.AddRow(euroPirates.FinancialAccounts.IncomeSales, -newInvoice.AmountCents, null); invoiceTransaction.Dependency = newInvoice; transaction.AddRow(euroPirates.FinancialAccounts.AssetsOutboundInvoices, -newInvoice.AmountCents, null); Payment payment = Payment.CreateSingle(euroPirates, transaction.DateTime, euroPirates.Currency, newInvoice.AmountCents, newInvoice, null); transaction.Dependency = payment.Group; payment.Group.Open = false; } if ((transaction.Description == "Kostnad" || transaction.Description == "Bank charges") && transaction.Rows.BalanceCentsDelta < 0 && transaction.Rows.BalanceCentsDelta > -125000) { // Bank fee. transaction.AddRow(euroPirates.FinancialAccounts.CostsBankFees, -transaction.Rows.BalanceCentsDelta, null); } if (transaction.Description.StartsWith("Paypal ")) { // Bank fee. transaction.AddRow(euroPirates.FinancialAccounts.CostsBankFees, -transaction.Rows.BalanceCentsDelta, null); } } Payouts.AutomatchAgainstUnbalancedTransactions(euroPirates); }
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); } }
protected void ProcessImportedData(ImportResult import) { FinancialAccount assetAccount = FinancialAccount.FromIdentity(Int32.Parse(this.DropAssetAccount.SelectedValue)); FinancialAccount autoDepositAccount = FinancialAccount.FromIdentity(Int32.Parse(this.DropAutoDeposits.SelectedValue)); FinancialAccount autoWithdrawalAccount = FinancialAccount.FromIdentity(Int32.Parse(this.DropAutoWithdrawals.SelectedValue)); int autoDepositLimit = Int32.Parse(this.TextDepositLimit.Text); int autoWithdrawalLimit = Int32.Parse(this.TextWithdrawalLimit.Text); int organizationId = Int32.Parse(this.DropOrganizations.SelectedValue); Organization organization = Organization.FromIdentity(organizationId); int importedTransactionCount = 0; foreach (ImportedRow row in import.Rows) { // Each row is at least a stub, probably more. // If too old, ignore. if (row.DateTime < new DateTime(2008, 12, 4)) { continue; } string importKey = row.SuppliedTransactionId; // If importKey is empty, construct a hash from the data fields. if (string.IsNullOrEmpty(importKey)) { string hashKey = row.HashBase + row.Comment + (row.AmountCentsNet / 100.0).ToString(CultureInfo.InvariantCulture) + row.CurrentBalance.ToString(CultureInfo.InvariantCulture) + row.DateTime.ToString("yyyy-MM-dd-hh-mm-ss"); importKey = SHA1.Hash(hashKey).Replace(" ", ""); } if (importKey.Length > 30) { importKey = importKey.Substring(0, 30); } Int64 amountCents = row.AmountCentsNet; if (amountCents == 0) { amountCents = row.AmountCentsGross; } FinancialTransaction transaction = FinancialTransaction.ImportWithStub(organizationId, row.DateTime, assetAccount.Identity, amountCents, row.Comment, importKey, _currentUser.Identity); if (transaction != null) { // The transaction was created. Examine if the autobook criteria are true. importedTransactionCount++; FinancialAccounts accounts = FinancialAccounts.FromBankTransactionTag(row.Comment); if (accounts.Count == 1) { // This is a labelled local donation. Geography geography = accounts[0].AssignedGeography; FinancialAccount localAccount = accounts[0]; transaction.AddRow(organization.FinancialAccounts.IncomeDonations, -amountCents, _currentUser); transaction.AddRow(organization.FinancialAccounts.CostsLocalDonationTransfers, amountCents, _currentUser); transaction.AddRow(localAccount, -amountCents, _currentUser); Activizr.Logic.Support.PWEvents.CreateEvent(EventSource.PirateWeb, EventType.LocalDonationReceived, _currentUser.Identity, organizationId, geography.Identity, 0, transaction.Identity, localAccount.Identity.ToString()); } else if (row.Comment.ToLowerInvariant().StartsWith("bg 451-0061 ")) // TODO: Organization.Parameters.FinancialTrackedTransactionPrefix { // Check for previously imported payment group PaymentGroup group = PaymentGroup.FromTag(organization, "SEBGM" + DateTime.Today.Year.ToString() + // TODO: Get tagging from org row.Comment.Substring(11)); if (group != null) { // There was a previously imported and not yet closed payment group matching this transaction // Close the payment group and match the transaction against accounts receivable transaction.Dependency = group; group.Open = false; transaction.AddRow(organization.FinancialAccounts.AssetsOutboundInvoices, -amountCents, _currentUser); } } else if (amountCents < 0) { if ((-amountCents) < autoWithdrawalLimit * 100) { // Book against autoWithdrawal account. transaction.AddRow(autoWithdrawalAccount, -amountCents, _currentUser); } } else if (amountCents > 0) { if (row.Fee < 0) { // This is always an autodeposit, if there is a fee (which is never > 0.0) transaction.AddRow(organization.FinancialAccounts.CostsBankFees, -row.Fee, _currentUser); transaction.AddRow(autoDepositAccount, -row.AmountCentsGross, _currentUser); } else if (amountCents < autoDepositLimit * 100) { // Book against autoDeposit account. transaction.AddRow(autoDepositAccount, -amountCents, _currentUser); } } } } // Import complete. Examine if we expect more transactions -- if the imported balance differs from // the database balance: double databaseAccountBalance = assetAccount.BalanceTotal; bool mismatch = false; if (databaseAccountBalance != import.CurrentBalance) { mismatch = true; } string message = importedTransactionCount.ToString() + " transactions were imported."; if (importedTransactionCount == 0) { message = "No transactions were imported. "; } else if (importedTransactionCount == 1) { message = "One transaction was imported. "; } if (import.CurrentBalance > 0) { if (mismatch) { message += " Transactions are missing from the database. Import more transactions."; } else { message += " The account balance is up to date. No further import is necessary."; ScriptManager.RegisterStartupScript(this, Page.GetType(), "alldone", "alert ('The account balance is up to date. No further import is necessary.');", true); // Auto-match open payouts against new transactions Payouts.AutomatchAgainstUnbalancedTransactions(organization); } } this.LabelImportResultText.Text = message; }
public static ImportExternalTransactionDataResults ImportExternalTransactionData(ExternalBankData import, ImportExternalTransactionDataArgs args) { FinancialAccount assetAccount = args.Account; FinancialAccount autoDepositAccount = args.Organization.FinancialAccounts.IncomeDonations; int autoDepositLimit = 0; // Disabled; TODO: this.CurrentOrganization.Parameters.AutoDonationLimit; bool autosetInitialBalance = false; ImportExternalTransactionDataResults result = new ImportExternalTransactionDataResults(); int count = 0; int progressUpdateInterval = import.Records.Length / 40; Int64 importedCentsTotal = 0; if (progressUpdateInterval > 100) { progressUpdateInterval = 100; } ProgressBarBackend progressDisplay = new ProgressBarBackend(args.Guid); Currency organizationCurrency = assetAccount.Organization.Currency; Currency accountCurrency = assetAccount.ForeignCurrency; if (accountCurrency == null) { accountCurrency = organizationCurrency; } FinancialAccountRows existingRows = assetAccount.GetRows(Constants.DateTimeLow, Constants.DateTimeHigh); // gets all if (existingRows.Count == 0) { autosetInitialBalance = true; } foreach (ExternalBankDataRecord row in import.Records) { // Update progress. count++; if (progressUpdateInterval < 2 || count % progressUpdateInterval == 0) { int percent = (count * 99) / import.Records.Length; progressDisplay.Set(percent); } // Update high- and low-water marks. if (row.DateTime < result.EarliestTransaction) { result.EarliestTransaction = row.DateTime; } if (row.DateTime > result.LatestTransaction) { result.LatestTransaction = row.DateTime; } string importKey = row.ImportHash; Int64 amountCents = row.TransactionNetCents; if (amountCents == 0) // defensive programming - these _should_ be duplicated in the interpreter if no "fee" field { amountCents = row.TransactionGrossCents; } Int64 foreignCents = amountCents; importedCentsTotal += amountCents; if (accountCurrency.Identity != organizationCurrency.Identity) { amountCents = new Money(amountCents, accountCurrency, row.DateTime).ToCurrency(organizationCurrency).Cents; } FinancialTransaction transaction = FinancialTransaction.ImportWithStub(args.Organization.Identity, row.DateTime, assetAccount.Identity, amountCents, row.Description, importKey, Sha256.Compute(row.RawData), args.CurrentUser.Identity); if (transaction != null) { // The transaction was created. result.TransactionsImported++; // If non-presentation currency, log the account currency amount as well. if (accountCurrency.Identity != organizationCurrency.Identity) { transaction.Rows[0].AmountForeignCents = new Money(foreignCents, accountCurrency); } if (row.Description.ToLowerInvariant().StartsWith(args.Organization.IncomingPaymentTag)) { // Check for previously imported payment group // TODO: MAKE FLEXIBLE - CALL PAYMENTREADERINTERFACE! // HACK HACK HACK HACK PaymentGroup group = PaymentGroup.FromTag(args.Organization, "SEBGM" + DateTime.Today.Year + // TODO: Get tags from org row.Description.Substring(args.Organization.IncomingPaymentTag.Length).Trim()); if (group != null && group.Open) { // There was a previously imported and not yet closed payment group matching this transaction // Close the payment group and match the transaction against accounts receivable transaction.Dependency = group; group.Open = false; transaction.AddRow(args.Organization.FinancialAccounts.AssetsOutboundInvoices, -amountCents, args.CurrentUser); } } else if (amountCents < 0) { // Autowithdrawal mechanisms removed, condition kept because of downstream else-if conditions } else if (amountCents > 0) { if (row.FeeCents < 0) { // This is always an autodeposit, if there is a fee (which is never > 0.0) transaction.AddRow(args.Organization.FinancialAccounts.CostsBankFees, -row.FeeCents, args.CurrentUser); transaction.AddRow(autoDepositAccount, -row.TransactionGrossCents, args.CurrentUser); } else if (amountCents < autoDepositLimit * 100) { // Book against autoDeposit account. transaction.AddRow(autoDepositAccount, -amountCents, args.CurrentUser); } } } else { // Transaction was not imported; assume duplicate result.DuplicateTransactions++; } } // Import complete. Return true if the bookkeeping account matches the bank data. Int64 databaseAccountBalanceCents; if (accountCurrency.Identity == organizationCurrency.Identity) { databaseAccountBalanceCents = assetAccount.BalanceTotalCents; } else { // foreign-currency account databaseAccountBalanceCents = assetAccount.ForeignCurrencyBalance.Cents; } // Subtract any transactions made after the most recent imported transaction. // This is necessary in case of Paypal and others which continuously feed the // bookkeeping account with new transactions; it will already have fed transactions // beyond the end-of-file. Int64 beyondEofCents = assetAccount.GetDeltaCents(result.LatestTransaction.AddSeconds(1), DateTime.Now.AddDays(2)); // Caution: the "AddSeconds(1)" is not foolproof, there may be other new txs on the same second. if (databaseAccountBalanceCents - beyondEofCents == import.LatestAccountBalanceCents) { Payouts.AutomatchAgainstUnbalancedTransactions(args.Organization); OutboundInvoices.AutomatchAgainstUnbalancedTransactions(args.Organization, args.CurrentUser); result.AccountBalanceMatchesBank = true; result.BalanceMismatchCents = 0; } else { result.AccountBalanceMatchesBank = false; result.BalanceMismatchCents = (databaseAccountBalanceCents - beyondEofCents) - import.LatestAccountBalanceCents; if (autosetInitialBalance) { Int64 newInitialBalanceCents = -result.BalanceMismatchCents; Money initialBalance = new Money(newInitialBalanceCents, accountCurrency); assetAccount.InitialBalance = initialBalance; result.InitialBalanceCents = newInitialBalanceCents; result.InitialBalanceCurrencyCode = accountCurrency.Code; // make an approximation of conversion rate set for initial balance in presentation to tell user initialBalance.ValuationDateTime = new DateTime(assetAccount.Organization.FirstFiscalYear, 1, 1); result.BalanceMismatchCents = initialBalance.ToCurrency(assetAccount.Organization.Currency).Cents; } } result.CurrencyCode = args.Organization.Currency.Code; GuidCache.Set(args.Guid + "-Results", result); return(result); }