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); } } }
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); }
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); } } }
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)); }
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)); }
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"); }