Esempio n. 1
0
        public static void AutomatchAgainstUnbalancedTransactions(Organization organization)
        {
            // Matches unbalanced financial transactions against unclosed payouts

            // Should this be in bot?

            Payouts payouts = ForOrganization(organization);

            FinancialTransactions transactions = FinancialTransactions.GetUnbalanced(organization);

            foreach (FinancialTransaction transaction in transactions)
            {
                // Console.WriteLine("Looking at transaction #{0} ({1:yyyy-MM-dd}, {2:N2}).", transaction.Identity, transaction.DateTime, transaction.Rows.AmountTotal);

                // First, establish that there are no similar transactions within 7 days. N^2 search.

                DateTime timeLow  = transaction.DateTime.AddDays(-7);
                DateTime timeHigh = transaction.DateTime.AddDays(7);

                bool foundCompeting = false;

                foreach (FinancialTransaction possiblyCompetingTransaction in transactions)
                {
                    if (possiblyCompetingTransaction.Rows.AmountCentsTotal == transaction.Rows.AmountCentsTotal &&
                        possiblyCompetingTransaction.DateTime >= timeLow &&
                        possiblyCompetingTransaction.DateTime <= timeHigh &&
                        possiblyCompetingTransaction.Identity != transaction.Identity)
                    {
                        foundCompeting = true;
                        // Console.WriteLine(" - Transaction #{0} ({1:yyyy-MM-dd} is competing, aborting", possiblyCompetingTransaction.Identity, possiblyCompetingTransaction.DateTime);
                    }
                }

                if (foundCompeting)
                {
                    continue;
                }

                // Console.WriteLine(" - no competing transactions...\r\n - transaction description is \"{0}\".", transaction.Description);

                // Console.WriteLine(" - looking for matching payouts");

                int foundCount    = 0;
                int payoutIdFound = 0;

                // As the amount of payouts grow, this becomes less efficient exponentially.

                foreach (Payout payout in payouts)
                {
                    // Ugly hack to fix cash advance payouts

                    DateTime payoutLowerTimeLimit = timeLow;
                    DateTime payoutUpperTimeLimit = timeHigh;

                    if (payout.AmountCents == -transaction.Rows.AmountCentsTotal &&
                        (payout.DependentCashAdvancesPayout.Count > 0 || payout.DependentCashAdvancesPayback.Count > 0))
                    {
                        // HACK: While PW5 doesn't have a manual-debug interface, special case for cash advances

                        payoutLowerTimeLimit = transaction.DateTime.AddDays(-60);
                        payoutUpperTimeLimit = transaction.DateTime.AddDays(60);
                    }

                    // HACK: Allow for up to 20 days beyond scheduled payment to catch tax payments

                    if (payout.DependentSalariesTax.Count > 0)
                    {
                        payoutLowerTimeLimit = transaction.DateTime.AddDays(-25);
                        payoutUpperTimeLimit = transaction.DateTime.AddDays(3);  // nobody pays taxes early...
                    }

                    if (payout.ExpectedTransactionDate >= payoutLowerTimeLimit &&
                        payout.ExpectedTransactionDate <= payoutUpperTimeLimit &&
                        payout.AmountCents == -transaction.Rows.AmountCentsTotal)
                    {
                        // Console.WriteLine(" - - payout #{0} matches ({1}, {2:yyyy-MM-dd})", payout.Identity, payout.Recipient, payout.ExpectedTransactionDate);

                        try
                        {
                            // If this succeeds, there is a transaction already

                            FinancialTransaction.FromDependency(payout);

                            break;
                        }
                        catch (Exception)
                        {
                            // There isn't such a transaction, which is what we want
                        }

                        foundCount++;
                        payoutIdFound = payout.Identity;
                    }
                }

                if (foundCount == 0)
                {
                    // Console.WriteLine(" - none found");
                }
                else if (foundCount > 1)
                {
                    // Console.WriteLine(" - multiple found, not autoprocessing");
                }
                else
                {
                    Payout payout = Payout.FromIdentity(payoutIdFound);
                    payout.BindToTransactionAndClose(transaction, null);
                }
            }
        }
Esempio n. 2
0
        public static void AutomatchAgainstUnbalancedTransactions(Organization organization, Person person)
        {
            // Matches unbalanced financial transactions against unclosed outbound invoices

            // Should this be in bot?

            OutboundInvoices invoices = ForOrganization(organization); // gets all open

            // build a hash of all invoice reference numbers, ours and theirs (and let's hope for no collision...)

            // TODO: Collision detection

            Dictionary <string, OutboundInvoice> invoiceLookup = new Dictionary <string, OutboundInvoice>();

            foreach (OutboundInvoice invoice in invoices)
            {
                invoiceLookup[RemoveNoise(invoice.TheirReference)] = invoice;
                invoiceLookup[RemoveNoise(invoice.Reference)]      = invoice;
            }

            FinancialTransactions transactions = FinancialTransactions.GetUnbalanced(organization);

            foreach (FinancialTransaction transaction in transactions)
            {
                string[]        words             = transaction.Description.Split(' ');
                bool            collision         = false;
                int             invoiceId         = 0;
                OutboundInvoice identifiedInvoice = null;

                foreach (string word in words)
                {
                    string cleanWord = RemoveNoise(word);
                    if (invoiceLookup.ContainsKey(cleanWord))
                    {
                        OutboundInvoice invoice = invoiceLookup[cleanWord];
                        if (transaction.Rows.AmountCentsTotal == invoice.AmountCents)
                        {
                            // Matching description and amount

                            if (invoiceId != 0 && invoiceId != invoice.Identity)
                            {
                                collision = true;
                            }
                            else if (invoice.Open) // double check it wasn't closed previously in loop
                            {
                                invoiceId         = invoice.Identity;
                                identifiedInvoice = invoice;
                            }
                        }
                    }
                }

                // If invoiceId != 0 and collision == false, we've found exactly one match

                if (invoiceId != 0 && !collision)
                {
                    Payment.CreateSingle(organization, transaction.DateTime, identifiedInvoice.Currency,
                                         identifiedInvoice.AmountCents, identifiedInvoice, person);

                    // Balance transaction against outbound invoices

                    transaction.AddRow(organization.FinancialAccounts.AssetsOutboundInvoices,
                                       -identifiedInvoice.AmountCents, person);
                }
            }
        }