public void MigrateDependenciesTo(Payout migrationTarget) { if (Identity > 0 && migrationTarget.Identity > 0) { // Persisted payout migration SwarmDb.GetDatabaseForWriting().MovePayoutDependencies(Identity, migrationTarget.Identity); } else { // In-memory migration: this payout isn't in database yet this.DependentCashAdvancesPayback.ForEach(item => migrationTarget.DependentCashAdvancesPayback.Add(item)); this.DependentCashAdvancesPayout.ForEach(item => migrationTarget.DependentCashAdvancesPayout.Add(item)); this.DependentExpenseClaims.ForEach(item => migrationTarget.DependentExpenseClaims.Add(item)); this.DependentInvoices.ForEach(item => migrationTarget.DependentInvoices.Add(item)); this.DependentSalariesNet.ForEach(item => migrationTarget.DependentSalariesNet.Add(item)); this.DependentSalariesTax.ForEach(item => migrationTarget.DependentSalariesTax.Add(item)); this.DependentCashAdvancesPayback = new CashAdvances(); this.DependentCashAdvancesPayout = new CashAdvances(); this.DependentExpenseClaims = new ExpenseClaims(); this.DependentInvoices = new InboundInvoices(); this.DependentSalariesNet = new Salaries(); this.DependentSalariesTax = new Salaries(); } migrationTarget.RecalculateAmount(); RecalculateAmount(); }
private void LoadDependencies() { DependentExpenseClaims = new ExpenseClaims(); DependentInvoices = new InboundInvoices(); DependentSalariesNet = new Salaries(); DependentSalariesTax = new Salaries(); DependentCashAdvancesPayout = new CashAdvances(); DependentCashAdvancesPayback = new CashAdvances(); BasicFinancialDependency[] dependencies = SwarmDb.GetDatabaseForReading().GetPayoutDependencies(this.Identity); foreach (BasicFinancialDependency dependency in dependencies) { switch (dependency.DependencyType) { case FinancialDependencyType.ExpenseClaim: DependentExpenseClaims.Add(ExpenseClaim.FromIdentity(dependency.ForeignId)); break; case FinancialDependencyType.InboundInvoice: DependentInvoices.Add(InboundInvoice.FromIdentity(dependency.ForeignId)); break; case FinancialDependencyType.Salary: Salary salary = Salary.FromIdentity(dependency.ForeignId); if (salary.NetSalaryCents == this.AmountCents) // HACK: Assumes that tax total is not identical { DependentSalariesNet.Add(salary); } else { DependentSalariesTax.Add(salary); } break; case FinancialDependencyType.CashAdvance: DependentCashAdvancesPayout.Add(CashAdvance.FromIdentity(dependency.ForeignId)); break; case FinancialDependencyType.CashAdvancePayback: DependentCashAdvancesPayback.Add(CashAdvance.FromIdentity(dependency.ForeignId)); break; default: throw new NotImplementedException("Unknown financial dependency type in Payout.LoadDependencies(): " + dependency.ToString()); } } }
/// <summary> /// Processes all payroll for all organizations system-wide. Should run on the 1st of every month. /// </summary> public static void ProcessMonthly() { DateTime today = DateTime.UtcNow; string lastRun = Persistence.Key["LastSalaryRun"]; string expectedLastRun = today.ToString("yyyyMM", CultureInfo.InvariantCulture); if (lastRun != null && String.Compare(lastRun, expectedLastRun, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase) >= 0) { // nothing to do, return return; } Persistence.Key["LastSalaryRun"] = expectedLastRun; // Process the payroll for all organizations. Assume payday is 25th. // TODO: Different payday per organization? Payroll payroll = GetAll(); DateTime payday = new DateTime(today.Year, today.Month, 25); foreach (PayrollItem payrollItem in payroll) { Salary salary = Salary.Create(payrollItem, payday); SwarmopsLog.CreateEntry(payrollItem.Person, new SalaryCreatedLogEntry(salary)); // TODO: CREATE SALARY SPECIFICATION, SEND TO PERSON } // If this is January, also send the annual statements for last year if (today.Month == 1) { Salaries.CreateAnnualStatements(today.Year - 1); } }
public static Dictionary <int, Int64> GetBudgetAttestationSpaceAdjustments(Organization organization) { // This function returns a dictionary for the cents that are either accounted for but not attested, // or attested but accounted for, to be used to understand how much is really left in budget // Positive adjustment means more [cost] budget available, negative less [cost] budget available if (_organizationBudgetAttestationSpaceLookup.ContainsKey(organization.Identity)) { return(_organizationBudgetAttestationSpaceLookup [organization.Identity]); } // TODO: This is expensive research, we should cache this result and clear cache on any attestation or create op Dictionary <int, Int64> result = new Dictionary <int, long>(); // Cash advances are accounted for when paid out. Make sure they count toward the budget when attested. CashAdvances advances = CashAdvances.ForOrganization(organization); foreach (CashAdvance advance in advances) { if (!result.ContainsKey(advance.BudgetId)) { result[advance.BudgetId] = 0; } if (advance.Attested) { result[advance.BudgetId] -= advance.AmountCents; } } // Expense claims, Inbound invoices, and Salaries are accounted for when filed. Make sure they DON'T // count toward the budget while they are NOT attested. ExpenseClaims claims = ExpenseClaims.ForOrganization(organization); // gets all open claims foreach (ExpenseClaim claim in claims) { if (!result.ContainsKey(claim.BudgetId)) { result[claim.BudgetId] = 0; } if (!claim.Attested) { result[claim.BudgetId] += claim.AmountCents; } } InboundInvoices invoices = InboundInvoices.ForOrganization(organization); foreach (InboundInvoice invoice in invoices) { if (!result.ContainsKey(invoice.BudgetId)) { result[invoice.BudgetId] = 0; } if (!invoice.Attested) { result[invoice.BudgetId] += invoice.AmountCents; } } Salaries salaries = Salaries.ForOrganization(organization); foreach (Salary salary in salaries) { if (!result.ContainsKey(salary.PayrollItem.BudgetId)) { result[salary.PayrollItem.BudgetId] = 0; } if (!salary.Attested) { result[salary.PayrollItem.BudgetId] += (salary.GrossSalaryCents + salary.AdditiveTaxCents); } } _organizationBudgetAttestationSpaceLookup[organization.Identity] = result; return(result); }
private static void AddUnpaidSalaries(Payouts payoutList, Organization organization) { Int64 taxTotalCents = 0; Salaries salaries = Salaries.ForOrganization(organization); List <int> identityList = new List <int>(); DateTime payDay = Constants.DateTimeHigh; foreach (Salary salary in salaries) { if (!salary.Attested) { continue; } if (!salary.NetPaid) { PayrollItem payrollItem = salary.PayrollItem; Person employee = payrollItem.Person; BasicPayout basicPayout = new BasicPayout(0, organization.Identity, employee.BankName, employee.BankClearing + " / " + employee.BankAccount, "[Loc]Financial_SalarySpecification|[Date]" + salary.PayoutDate.ToString(CultureInfo.InvariantCulture), salary.NetSalaryCents, salary.PayoutDate, false, DateTime.Now, 0); Payout payout = Payout.FromBasic(basicPayout); payout.RecipientPerson = employee; payout.DependentSalariesNet.Add(salary); payoutList.Add(payout); if (payDay > salary.PayoutDate) { payDay = salary.PayoutDate; } } if (!salary.TaxPaid) { taxTotalCents += salary.TaxTotalCents; identityList.Add(salary.Identity); if (payDay > salary.PayoutDate) { payDay = salary.PayoutDate; } } } if (taxTotalCents > 0) { // Add the summarized tax line, too BasicPayout basicPayout = new BasicPayout(0, organization.Identity, "[Loc]Financial_TheTaxMan", "SEBG 5050-1055", // HACK: Get tax account from something organization.TaxPaymentOcr, taxTotalCents, payDay, false, DateTime.Now, 0); Payout payout = Payout.FromBasic(basicPayout); foreach (int salaryId in identityList) { payout.DependentSalariesTax.Add(Salary.FromIdentity(salaryId)); } payoutList.Add(payout); } }
private static void AddUnpaidSalaries(Payouts payoutList, int organizationId) { Int64 taxTotalCents = 0; Salaries salaries = Salaries.ForOrganization(Organization.FromIdentity(organizationId)); List <int> identityList = new List <int>(); DateTime payDay = DateTime.MaxValue; foreach (Salary salary in salaries) { if (!salary.Attested) { continue; } if (!salary.NetPaid) { PayrollItem payrollItem = salary.PayrollItem; Person employee = payrollItem.Person; BasicPayout basicPayout = new BasicPayout(0, organizationId, employee.BankName, employee.BankClearing + " / " + employee.BankAccount, "[Loc]Financial_SalarySpecification|[Date]" + salary.PayoutDate.ToString(CultureInfo.InvariantCulture), salary.NetSalaryCents, salary.PayoutDate, false, DateTime.Now, 0); Payout payout = Payout.FromBasic(basicPayout); payout.DependentSalariesNet.Add(salary); payoutList.Add(payout); if (payDay > salary.PayoutDate) { payDay = salary.PayoutDate; } } if (!salary.TaxPaid) { taxTotalCents += salary.TaxTotalCents; identityList.Add(salary.Identity); if (payDay > salary.PayoutDate) { payDay = salary.PayoutDate; } } } if (taxTotalCents > 0) { // Add the summarized tax line, too string referenceString = string.Empty; if (identityList.Count == 1) { referenceString = "[Loc]Financial_TaxSpecification|" + identityList[0].ToString(); } else { identityList.Sort(); referenceString = "[Loc]Financial_TaxesSpecification|" + Formatting.GenerateRangeString(identityList); } BasicPayout basicPayout = new BasicPayout(0, organizationId, "[Loc]Financial_TheTaxMan", string.Empty, referenceString, taxTotalCents, payDay, false, DateTime.Now, 0); Payout payout = Payout.FromBasic(basicPayout); foreach (int salaryId in identityList) { payout.DependentSalariesTax.Add(Salary.FromIdentity(salaryId)); } payoutList.Add(payout); } }