Example #1
0
        private void LoadDependencies()
        {
            this.DependentExpenseClaims       = new ExpenseClaims();
            this.DependentInvoices            = new InboundInvoices();
            this.DependentSalariesNet         = new Salaries();
            this.DependentSalariesTax         = new Salaries();
            this.DependentCashAdvancesPayout  = new CashAdvances();
            this.DependentCashAdvancesPayback = new CashAdvances();

            BasicFinancialDependency[] dependencies = SwarmDb.GetDatabaseForReading().GetPayoutDependencies(Identity);

            foreach (BasicFinancialDependency dependency in dependencies)
            {
                switch (dependency.DependencyType)
                {
                case FinancialDependencyType.ExpenseClaim:
                    this.DependentExpenseClaims.Add(ExpenseClaim.FromIdentity(dependency.ForeignId));
                    break;

                case FinancialDependencyType.InboundInvoice:
                    this.DependentInvoices.Add(InboundInvoice.FromIdentity(dependency.ForeignId));
                    break;

                case FinancialDependencyType.Salary:
                    Salary salary = Salary.FromIdentity(dependency.ForeignId);
                    if (salary.NetSalaryCents == AmountCents)     // HACK: Assumes that tax total is not identical
                    {
                        this.DependentSalariesNet.Add(salary);
                    }
                    else
                    {
                        this.DependentSalariesTax.Add(salary);
                    }
                    break;

                case FinancialDependencyType.CashAdvance:
                    this.DependentCashAdvancesPayout.Add(CashAdvance.FromIdentity(dependency.ForeignId));
                    break;

                case FinancialDependencyType.CashAdvancePayback:
                    this.DependentCashAdvancesPayback.Add(CashAdvance.FromIdentity(dependency.ForeignId));
                    break;

                default:
                    throw new NotImplementedException(
                              "Unknown financial dependency type in Payout.LoadDependencies(): " + dependency);
                }
            }
        }
Example #2
0
        public static CashAdvance Create(Organization organization, Person forPerson, Person createdByPerson, Int64 amountCents, FinancialAccount budget, string description)
        {
            CashAdvance newAdvance = FromIdentityAggressive(SwarmDb.GetDatabaseForWriting().CreateCashAdvance(forPerson.Identity,
                                                                                                              createdByPerson.Identity,
                                                                                                              organization.Identity,
                                                                                                              budget.Identity, amountCents,
                                                                                                              description));

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

            return(newAdvance);
        }
Example #3
0
        private void LoadDependencies()
        {
            if (this.DependentCashAdvancesPayback != null && Identity == 0)
            {
                return; // if inited and identity zero, return
            }

            this.DependentExpenseClaims       = new ExpenseClaims();
            this.DependentInvoices            = new InboundInvoices();
            this.DependentSalariesNet         = new Salaries();
            this.DependentSalariesTax         = new Salaries();
            this.DependentCashAdvancesPayout  = new CashAdvances();
            this.DependentCashAdvancesPayback = new CashAdvances();

            if (Identity == 0)
            {
                return; // never progress past here if identity zero
            }

            BasicFinancialDependency[] dependencies = SwarmDb.GetDatabaseForReading().GetPayoutDependencies(Identity);

            foreach (BasicFinancialDependency dependency in dependencies)
            {
                switch (dependency.DependencyType)
                {
                case FinancialDependencyType.ExpenseClaim:
                    this.DependentExpenseClaims.Add(ExpenseClaim.FromIdentity(dependency.ForeignId));
                    break;

                case FinancialDependencyType.InboundInvoice:
                    this.DependentInvoices.Add(InboundInvoice.FromIdentity(dependency.ForeignId));
                    break;

                case FinancialDependencyType.Salary:
                    Salary salary = Salary.FromIdentity(dependency.ForeignId);
                    if (salary.NetSalaryCents == AmountCents || this.CreatedDateTime > new DateTime(2015, 11, 1))
                    {
                        this.DependentSalariesNet.Add(salary);
                    }
                    else     // LEGACY
                    {
                        this.DependentSalariesTax.Add(salary);
                    }
                    break;

                case FinancialDependencyType.SalaryTax:
                    Salary salaryTax = Salary.FromIdentity(dependency.ForeignId);
                    this.DependentSalariesTax.Add(salaryTax);
                    break;

                case FinancialDependencyType.CashAdvance:
                    this.DependentCashAdvancesPayout.Add(CashAdvance.FromIdentity(dependency.ForeignId));
                    break;

                case FinancialDependencyType.CashAdvancePayback:
                    this.DependentCashAdvancesPayback.Add(CashAdvance.FromIdentity(dependency.ForeignId));
                    break;

                default:
                    throw new NotImplementedException(
                              "Unknown financial dependency type in Payout.LoadDependencies(): " + dependency);
                }
            }
        }
Example #4
0
        public static Payout CreateFromProtoIdentity(Person creator, string protoIdentity)
        {
            string[] components = protoIdentity.Split('|');
            int      payoutId   = 0;

            // The components can EITHER be a series of expense claims OR a single invoice.

            if (components.Length == 0)
            {
                // nothing to construct. Exception or return null?

                return(null);
            }
            if (components[0][0] == 'A')
            {
                // Cash advance(s) to be paid out.

                string     bank           = string.Empty;
                string     account        = string.Empty;
                List <int> identityList   = new List <int>();
                Int64      amountCents    = 0;
                int        organizationId = 0;

                foreach (string component in components)
                {
                    int         advanceId = Int32.Parse(component.Substring(1));
                    CashAdvance advance   = CashAdvance.FromIdentity(advanceId);
                    identityList.Add(advanceId);
                    organizationId = advance.OrganizationId;
                    Organization organization = Organization.FromIdentity(advance.OrganizationId);

                    if (bank.Length < 1)
                    {
                        Person asker = advance.Person;
                        bank    = asker.BankName;
                        account = asker.BankAccount;
                    }

                    amountCents += advance.AmountCents;

                    advance.PaidOut = true;
                    // advance.Open remains true until the advance is repaid

                    SwarmopsLogEntry.Create(creator,
                                            new PayoutCreatedLogEntry(creator, advance.Person, organization,
                                                                      organization.Currency, amountCents / 100.0,
                                                                      "Cash Advance Paid Out"),
                                            advance.Person, advance);


                    OutboundComm.CreateNotificationOfFinancialValidation(advance.Budget, advance.Person,
                                                                         advance.AmountCents / 100.0, advance.Description, NotificationResource.CashAdvance_PaidOut);
                }

                string referenceString = string.Empty;

                if (identityList.Count == 1)
                {
                    referenceString = "Cash Advance #" + identityList[0].ToString(CultureInfo.InvariantCulture);
                }
                else
                {
                    identityList.Sort();
                    referenceString = "Cash Advances " + Formatting.GenerateRangeString(identityList);
                }

                payoutId = SwarmDb.GetDatabaseForWriting().CreatePayout(organizationId, bank, account,
                                                                        referenceString, amountCents, DateTime.Today.AddDays(1),
                                                                        creator.Identity);

                foreach (int advanceId in identityList)
                {
                    SwarmDb.GetDatabaseForWriting()
                    .CreatePayoutDependency(payoutId, FinancialDependencyType.CashAdvance,
                                            advanceId);
                }
            }
            else if (components[0][0] == 'C')
            {
                // Expense claims, possibly followed up by cash advance paybacks

                Person       beneficiaryPerson = null;
                Organization organization      = null;
                string       bank                       = string.Empty;
                string       account                    = string.Empty;
                List <int>   claimIdentityList          = new List <int>();
                List <int>   advancePaybackIdentityList = new List <int>();
                Int64        amountCents                = 0;
                int          organizationId             = 0;

                foreach (string component in components)
                {
                    int foreignId = Int32.Parse(component.Substring(1));

                    if (component[0] == 'C')
                    {
                        ExpenseClaim claim = ExpenseClaim.FromIdentity(foreignId);
                        claimIdentityList.Add(foreignId);

                        if (bank.Length < 1)
                        {
                            Person claimer = claim.Claimer;
                            bank           = claimer.BankName;
                            account        = claimer.BankAccount;
                            organizationId = claim.OrganizationId;
                        }

                        beneficiaryPerson = claim.Claimer;
                        organization      = claim.Organization;
                        amountCents      += claim.AmountCents;

                        claim.Repaid = true;
                        claim.Close();

                        OutboundComm.CreateNotificationOfFinancialValidation(claim.Budget, claim.Claimer,
                                                                             claim.AmountCents / 100.0, claim.Description, NotificationResource.ExpenseClaim_PaidOut);
                    }
                    else if (component[0] == 'a')
                    {
                        CashAdvance advancePayback = CashAdvance.FromIdentity(foreignId);
                        advancePaybackIdentityList.Add(foreignId);

                        amountCents        -= advancePayback.AmountCents;
                        advancePayback.Open = false;
                    }
                }

                string referenceString = string.Empty;

                if (claimIdentityList.Count == 1)
                {
                    referenceString = "Expense Claim #" + claimIdentityList[0].ToString(CultureInfo.InvariantCulture);
                }
                else
                {
                    claimIdentityList.Sort();
                    referenceString = "Expense Claims " + Formatting.GenerateRangeString(claimIdentityList);
                }

                SwarmopsLogEntry.Create(creator,
                                        new PayoutCreatedLogEntry(creator, beneficiaryPerson, organization,
                                                                  organization.Currency, amountCents / 100.0,
                                                                  referenceString),
                                        beneficiaryPerson);

                payoutId = SwarmDb.GetDatabaseForWriting().CreatePayout(organizationId, bank, account,
                                                                        referenceString, amountCents, DateTime.Today.AddDays(1),
                                                                        creator.Identity);

                foreach (int claimId in claimIdentityList)
                {
                    SwarmDb.GetDatabaseForWriting()
                    .CreatePayoutDependency(payoutId, FinancialDependencyType.ExpenseClaim,
                                            claimId);
                }

                foreach (int advancePaybackId in advancePaybackIdentityList)
                {
                    SwarmDb.GetDatabaseForWriting()
                    .CreatePayoutDependency(payoutId, FinancialDependencyType.CashAdvancePayback,
                                            advancePaybackId);
                }
            }
            else if (components[0][0] == 'I')
            {
                // There is just one invoice per payout

                InboundInvoice invoice = InboundInvoice.FromIdentity(Int32.Parse(components[0].Substring(1)));

                DateTime expectedPayment = invoice.DueDate;

                if (expectedPayment < DateTime.Today)
                {
                    expectedPayment = DateTime.Today;
                }

                payoutId = SwarmDb.GetDatabaseForWriting()
                           .CreatePayout(invoice.OrganizationId, string.Empty, invoice.PayToAccount,
                                         invoice.Ocr.Length > 0 ? "OCR " + invoice.Ocr : "Ref# " + invoice.InvoiceReference,
                                         invoice.AmountCents, expectedPayment,
                                         creator.Identity);

                SwarmDb.GetDatabaseForWriting()
                .CreatePayoutDependency(payoutId, FinancialDependencyType.InboundInvoice,
                                        invoice.Identity);

                invoice.Open = false;
            }
            else if (components[0][0] == 'S')
            {
                // Salary, net payment

                Salary salary = Salary.FromIdentity(Int32.Parse(components[0].Substring(1)));

                payoutId = SwarmDb.GetDatabaseForWriting()
                           .CreatePayout(salary.PayrollItem.OrganizationId, salary.PayrollItem.Person.BankName,
                                         salary.PayrollItem.Person.BankAccount,
                                         "Salary " + salary.PayoutDate.ToString("yyyy-MMM"),
                                         salary.NetSalaryCents, salary.PayoutDate,
                                         creator.Identity);

                SwarmDb.GetDatabaseForWriting().CreatePayoutDependency(payoutId, FinancialDependencyType.Salary,
                                                                       salary.Identity);

                salary.NetPaid = true;
            }
            else if (components[0][0] == 'T')
            {
                // Tax payment for multiple salaries.

                List <int> identityList   = new List <int>();
                Int64      amountCents    = 0;
                int        organizationId = 0;
                DateTime   payDay         = DateTime.Today.AddDays(1);

                foreach (string component in components)
                {
                    int    salaryId = Int32.Parse(component.Substring(1));
                    Salary salary   = Salary.FromIdentity(salaryId);
                    identityList.Add(salaryId);

                    if (organizationId == 0)
                    {
                        organizationId = salary.PayrollItem.OrganizationId;
                        payDay         = salary.PayoutDate;
                    }

                    amountCents += salary.TaxTotalCents;

                    salary.TaxPaid = true;
                }


                Organization organization = Organization.FromIdentity(organizationId);
                identityList.Sort();

                payoutId = SwarmDb.GetDatabaseForWriting()
                           .CreatePayout(organization.Identity, "The Tax Man", organization.Parameters.TaxAccount,
                                         organization.Parameters.TaxOcr,
                                         amountCents, payDay, creator.Identity);

                foreach (int salaryId in identityList)
                {
                    SwarmDb.GetDatabaseForWriting().CreatePayoutDependency(payoutId, FinancialDependencyType.SalaryTax,
                                                                           salaryId);
                }
            }
            else
            {
                throw new NotImplementedException();
            }

            return(FromIdentity(payoutId));
        }
Example #5
0
        public static Payout CreateBitcoinPayoutFromPrototype(Organization organization, Payout prototype, string transactionHash)
        {
            string[] components = prototype.ProtoIdentity.Split('|');
            int      payoutId   = 0;

            // This function is made for complex bitcoin payouts and will typically take many different types of payouts to many people at once.

            if (components.Length == 0)
            {
                // nothing to construct. Exception or return null?

                return(null);
            }

            payoutId = SwarmDb.GetDatabaseForWriting().CreatePayout(organization.Identity, "Bitcoin network", "Multiple",
                                                                    transactionHash, prototype.AmountCents, DateTime.UtcNow, 0);

            foreach (string component in components)
            {
                int foreignId = Int32.Parse(component.Substring(1));

                switch (component[0])
                {
                case 'A':
                    // Cash advance
                    CashAdvance advance = CashAdvance.FromIdentity(foreignId);
                    advance.PaidOut = true;

                    SwarmopsLogEntry.Create(null,
                                            new PayoutCreatedLogEntry(null, advance.Person, organization,
                                                                      organization.Currency, advance.AmountCents / 100.0,
                                                                      "Cash Advance Paid Out"),
                                            advance.Person, advance);

                    OutboundComm.CreateNotificationOfFinancialValidation(advance.Budget, advance.Person,
                                                                         advance.AmountCents / 100.0, advance.Description, NotificationResource.CashAdvance_PaidOut);
                    SwarmDb.GetDatabaseForWriting()
                    .CreatePayoutDependency(payoutId, FinancialDependencyType.CashAdvance,
                                            foreignId);
                    break;

                case 'a':
                    // This is a negative record - payback of cash advance
                    CashAdvance advancePayback = CashAdvance.FromIdentity(foreignId);
                    advancePayback.Open = false;

                    SwarmDb.GetDatabaseForWriting()
                    .CreatePayoutDependency(payoutId, FinancialDependencyType.CashAdvancePayback,
                                            foreignId);

                    break;

                case 'C':
                    // Expense claim
                    ExpenseClaim claim = ExpenseClaim.FromIdentity(foreignId);
                    claim.Repaid = true;
                    claim.Close();

                    OutboundComm.CreateNotificationOfFinancialValidation(claim.Budget, claim.Claimer,
                                                                         claim.AmountCents / 100.0, claim.Description, NotificationResource.ExpenseClaim_PaidOut);
                    SwarmDb.GetDatabaseForWriting()
                    .CreatePayoutDependency(payoutId, FinancialDependencyType.ExpenseClaim,
                                            foreignId);

                    break;

                case 'I':
                    // Invoice
                    InboundInvoice invoice         = InboundInvoice.FromIdentity(foreignId);
                    DateTime       expectedPayment = invoice.DueDate;

                    if (expectedPayment < DateTime.Today)
                    {
                        expectedPayment = DateTime.Today;
                    }

                    SwarmDb.GetDatabaseForWriting()
                    .CreatePayoutDependency(payoutId, FinancialDependencyType.InboundInvoice,
                                            invoice.Identity);

                    // TODO: NOTIFY PAID?

                    invoice.Open = false;
                    break;

                case 'S':
                    // Salary net

                    Salary salaryNet = Salary.FromIdentity(foreignId);
                    SwarmDb.GetDatabaseForWriting().CreatePayoutDependency(payoutId, FinancialDependencyType.Salary,
                                                                           salaryNet.Identity);
                    salaryNet.NetPaid = true;
                    break;

                case 'T':
                    // Tax payout, typically for multiple salaries

                    Salary salaryTax = Salary.FromIdentity(foreignId);
                    SwarmDb.GetDatabaseForWriting().CreatePayoutDependency(payoutId, FinancialDependencyType.SalaryTax,
                                                                           salaryTax.Identity);
                    salaryTax.TaxPaid = true;

                    break;

                default:
                    throw new NotImplementedException();
                }
            }

            // Return the new object by reloading it from database

            return(Payout.FromIdentity(payoutId));
        }
Example #6
0
        public static PaymentTransferInfo FromObject(IHasIdentity financialObject, Money amountToPay = null)
        {
            if (financialObject is ExpenseClaim)
            {
                ExpenseClaim claim = financialObject as ExpenseClaim;

                if (amountToPay == null)
                {
                    return(FromObject(claim.Claimer, new Money(claim.AmountCents, claim.Organization.Currency)));
                }
                else
                {
                    return(FromObject(claim.Claimer, amountToPay));
                }
            }

            if (financialObject is CashAdvance)
            {
                CashAdvance advance = financialObject as CashAdvance;

                if (amountToPay == null)
                {
                    return(FromObject(advance.Person, new Money(advance.AmountCents, advance.Organization.Currency)));
                }
                else
                {
                    return(FromObject(advance.Person, amountToPay));
                }
            }

            if (financialObject is Salary)
            {
                Salary salary = financialObject as Salary;
                return(FromObject(salary.PayrollItem.Person, new Money(salary.NetSalaryCents, salary.PayrollItem.Organization.Currency)));
            }

            if (financialObject is InboundInvoice)
            {
                // TODO: Create Supplier object, read from that instead

                InboundInvoice      invoice = financialObject as InboundInvoice;
                PaymentTransferInfo result  = new PaymentTransferInfo();

                // For now, do a switch on the pay-to-account string, before the payment targets are implemented
                // on invoices and expense claims:

                result.LocalizedPaymentInformation = new Dictionary <string, string>();

                if (invoice.PayToAccount.StartsWith("IBAN"))
                {
                    result.TargetType = PaymentTargetType.InternationalBankTransfer;

                    result.LocalizedPaymentInformation[
                        Logic_Financial_PaymentTransferInfo.PaymentTargetField_Iban] =
                        invoice.PayToAccount.Substring(5);

                    // this should have bic too but we don't have that information before payment targets
                    // are properly implemented
                }
                else if (invoice.PayToAccount.StartsWith("SEBG"))
                {
                    result.TargetType = PaymentTargetType.DomesticBankGiro; // for now
                    result.LocalizedPaymentInformation[
                        Logic_Financial_PaymentTransferInfo.PaymentTargetField_GiroNumber] =
                        invoice.PayToAccount.Substring(5);

                    // Check for SEBG OCR availability

                    if (invoice.InvoiceReference.Length > 2 && Formatting.CheckLuhnChecksum(invoice.InvoiceReference) &&
                        Formatting.CheckLuhnChecksum(invoice.PayToAccount))
                    {
                        result.OcrAvailable = true;
                        result.OcrData      = new string[3];
                        result.OcrData[0]   = invoice.InvoiceReference + " #";
                        result.OcrData[1]   = string.Format("{0} {1:00}   {2} >", // three spaces between the cents and the checksum
                                                            invoice.AmountCents / 100, invoice.AmountCents % 100,
                                                            Formatting.GetLuhnChecksum(invoice.AmountCents.ToString(CultureInfo.InvariantCulture)));
                        result.OcrData[2] = Formatting.CleanNumber(invoice.PayToAccount) + "#41#";
                    }
                }
                else
                {
                    // Assume domestic giro in lack of other information

                    result.TargetType = PaymentTargetType.DomesticBankGiro;
                    result.LocalizedPaymentInformation[
                        Logic_Financial_PaymentTransferInfo.PaymentTargetField_GiroNumber] =
                        invoice.PayToAccount; // the whole string, as opposed to case above
                }

                result.LocalizedPaymentMethodName =
                    Logic_Financial_PaymentTransferInfo.ResourceManager.GetString("PaymentTargetType_" +
                                                                                  result.TargetType.ToString());
                if (invoice.HasNativeCurrency)
                {
                    result.Currency       = invoice.NativeCurrencyAmount.Currency;
                    result.CurrencyAmount = result.Currency.Code + " " +
                                            (invoice.NativeCurrencyAmount.Cents / 100.0).ToString("N2");
                }
                else
                {
                    result.Currency       = invoice.Organization.Currency;
                    result.CurrencyAmount = result.Currency.Code + " " + (invoice.AmountCents / 100.0).ToString("N2");
                }

                result.Country = null; // TODO: Set to Supplier's PaymentTarget country

                result.Recipient = invoice.Supplier;

                return(result);
            }

            if (financialObject is Person)
            {
                Person person = financialObject as Person;

                // For now, assume the bank/clearing/account triplet
                // TODO: Implement payment targets on teh Person object

                PaymentTransferInfo result = new PaymentTransferInfo();
                result.TargetType = PaymentTargetType.DomesticBankTransfer;

                // We must know the amount at this point

                if (amountToPay == null)
                {
                    throw new ArgumentNullException("amountToPay", @"Cannot determine payment transfer information without knowing origin currency");
                }

                if (string.IsNullOrEmpty(person.BankName) || string.IsNullOrEmpty(person.BankClearing) ||
                    string.IsNullOrEmpty(person.BankAccount))
                {
                    throw new NotImplementedException("Cannot provide payment transfer information for Person #" + person.Identity.ToString());
                }

                result.TargetType = PaymentTargetType.DomesticBankTransfer;
                result.Country    = person.CountryId == 0? null: person.Country;
                result.Currency   = amountToPay.Currency; // TODO: The currency will need to come from the payment method instead

                result.CurrencyAmount = result.Currency.Code + " " + (amountToPay.ToCurrency(result.Currency).Cents / 100.0).ToString("N2");

                result.LocalizedPaymentMethodName =
                    Logic_Financial_PaymentTransferInfo.ResourceManager.GetString("PaymentTargetType_" +
                                                                                  result.TargetType.ToString());
                result.Recipient = person.Canonical;

                result.LocalizedPaymentInformation = new Dictionary <string, string>();
                result.LocalizedPaymentInformation[
                    Logic_Financial_PaymentTransferInfo.PaymentTargetField_ClearingCode] =
                    person.BankName + " " + person.BankClearing;
                result.LocalizedPaymentInformation[
                    Logic_Financial_PaymentTransferInfo.PaymentTargetField_AccountNumber] = person.BankAccount;

                return(result);
            }

            throw new NotImplementedException("Unknown payment information");
        }