/// <summary> /// 获取本次摊销日期 /// </summary> /// <param name="interval">间隔类型</param> /// <param name="the">日期</param> /// <returns>这次摊销日期</returns> private static DateTime ThisAmortizationDate(AmortizeInterval interval, DateTime the) { switch (interval) { case AmortizeInterval.EveryDay: case AmortizeInterval.SameDayOfWeek: return(the); case AmortizeInterval.SameDayOfYear: if (the.Month == 2 && the.Day == 29) { return(the.AddDays(1)); } return(the); case AmortizeInterval.SameDayOfMonth: return(the.Day > 28 ? the.AddDays(1 - the.Day).AddMonths(1) : the); case AmortizeInterval.LastDayOfWeek: return(the.DayOfWeek == DayOfWeek.Sunday ? the : the.AddDays(7 - (int)the.DayOfWeek)); case AmortizeInterval.LastDayOfMonth: return(AccountantHelper.LastDayOfMonth(the.Year, the.Month)); case AmortizeInterval.LastDayOfYear: return(new DateTime(the.Year + 1, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(-1)); default: throw new ArgumentException("间隔类型未知", nameof(interval)); } }
/// <summary> /// 获取下一个摊销日期 /// </summary> /// <param name="interval">间隔类型</param> /// <param name="last">上一次摊销日期</param> /// <returns>下一个摊销日期</returns> private static DateTime NextAmortizationDate(AmortizeInterval interval, DateTime last) { switch (interval) { case AmortizeInterval.EveryDay: return(last.AddDays(1)); case AmortizeInterval.SameDayOfWeek: return(last.AddDays(7)); case AmortizeInterval.LastDayOfWeek: return(last.DayOfWeek == DayOfWeek.Sunday ? last.AddDays(7) : last.AddDays(14 - (int)last.DayOfWeek)); case AmortizeInterval.SameDayOfMonth: return(last.AddMonths(1)); case AmortizeInterval.LastDayOfMonth: return(AccountantHelper.LastDayOfMonth(last.Year, last.Month + 1)); case AmortizeInterval.SameDayOfYear: return(last.AddYears(1)); case AmortizeInterval.LastDayOfYear: return(new DateTime(last.Year + 2, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(-1)); default: throw new ArgumentException("间隔类型未知", nameof(interval)); } }
/// <summary> /// 调整资产计算表 /// </summary> /// <param name="asset">资产</param> /// <returns>资产</returns> public static Asset InternalRegular(Asset asset) { if (asset.Remark == Asset.IgnoranceMark) { return(asset); } if (!asset.Date.HasValue || !asset.Value.HasValue) { return(asset); } var lst = asset.Schedule?.ToList() ?? new(); foreach (var assetItem in lst) { if (assetItem is DepreciateItem || assetItem is DevalueItem) { if (assetItem.Date.HasValue) { assetItem.Date = AccountantHelper.LastDayOfMonth( assetItem.Date.Value.Year, assetItem.Date.Value.Month); } } } lst.Sort(new AssetItemComparer()); if (lst.Count == 0 || lst[0] is not AcquisitionItem acq0) { lst.Insert( 0, new AcquisitionItem { Date = asset.Date, OrigValue = asset.Value.Value }); }
/// <inheritdoc /> public override IQueryResult Execute(string expr, IEntitiesSerializer serializer) { var content = Parsing.Token(ref expr); var avg = Parsing.DoubleF(ref expr); Parsing.Eof(expr); var tdy = ClientDateTime.Today; var ldom = AccountantHelper.LastDayOfMonth(tdy.Year, tdy.Month); var srng = new DateFilter(new DateTime(tdy.Year, tdy.Month, 1, 0, 0, 0, DateTimeKind.Utc), tdy); var balance = Accountant.RunGroupedQuery( $"T1002 {content.Quotation('\'')} [~{tdy.AsDate()}]`vD{srng.AsDateRange()}"); var bal = 0D; var btd = 0D; foreach (var b in balance.Items.Cast <ISubtotalDate>()) { if (b.Date == tdy) { btd += b.Fund; } else { bal += b.Fund; } } var targ = ldom.Day * avg; var sb = new StringBuilder(); sb.AppendLine($"Target: {targ.AsCurrency()}"); sb.AppendLine($"Balance until yesterday: {bal.AsCurrency()}"); if ((bal - targ).IsNonNegative()) { sb.AppendLine("Achieved."); sb.AppendLine(); sb.AppendLine( (btd - avg).IsNonNegative() ? $"Plan A: Credit {(btd - avg).AsCurrency()}, Balance {avg.AsCurrency()}" : $"Plan A: Debit {(avg - btd).AsCurrency()}, Balance {avg.AsCurrency()}"); sb.AppendLine("Plan B: No Action"); } else { var res = targ - bal; var rsd = ldom.Day - tdy.Day + 1; sb.AppendLine($"Deficiency: {res.AsCurrency()}"); var avx = res / rsd; if ((rsd * avg - res).IsNonNegative()) { sb.AppendLine($"Average deficiency: {avx.AsCurrency()} <= {avg.AsCurrency()}"); sb.AppendLine(); sb.AppendLine( (btd - avx).IsNonNegative() ? $"Plan A: Credit {(btd - avx).AsCurrency()}, Balance {avx.AsCurrency()}" : $"Plan A: Debit {(avx - btd).AsCurrency()}, Balance {avx.AsCurrency()}"); sb.AppendLine( (btd - avg).IsNonNegative() ? $"Plan B: Credit {(btd - avg).AsCurrency()}, Balance {avg.AsCurrency()}" : $"Plan B: Debit {(avg - btd).AsCurrency()}, Balance {avg.AsCurrency()}"); } else { sb.AppendLine($"Average deficiency: {avx.AsCurrency()} > {avg.AsCurrency()}"); sb.AppendLine(); sb.AppendLine( (btd - avx).IsNonNegative() ? $"Plan: Credit {(btd - avx).AsCurrency()}, Balance {avx.AsCurrency()}" : $"Plan: Debit {(avx - btd).AsCurrency()}, Balance {avx.AsCurrency()}"); } } return(new PlainText(sb.ToString())); }
public void LastDayOfMonthTest(string expectedS, int year, int month) { var expected = expectedS.ToDateTime(); Assert.Equal(expected, AccountantHelper.LastDayOfMonth(year, month)); }
/// <summary> /// 折旧 /// </summary> public static void Depreciate(Asset asset) { if (!asset.Date.HasValue || !asset.Value.HasValue || !asset.Salvge.HasValue || !asset.Life.HasValue) { return; } var items = asset.Schedule.Where( assetItem => !(assetItem is DepreciateItem) || assetItem.Remark == AssetItem.IgnoranceMark) .ToList(); switch (asset.Method) { case DepreciationMethod.None: break; case DepreciationMethod.StraightLine: { var lastYear = asset.Date.Value.Year + asset.Life.Value; var lastMonth = asset.Date.Value.Month; var dt = asset.Date.Value; var flag = false; for (var i = 0;; i++) { if (i < items.Count) { if (items[i].Date > dt) { dt = items[i].Date ?? dt; flag = false; continue; } if (flag) { continue; } if (items[i] is AcquisationItem || items[i] is DispositionItem) { continue; } if (items[i] is DepreciateItem) // With IgnoranceMark { flag = true; continue; } } flag = true; if (dt.Year == asset.Date.Value.Year && dt.Month == asset.Date.Value.Month) { dt = AccountantHelper.LastDayOfMonth(dt.Year, dt.Month + 1); if (i == items.Count) { i--; } continue; } var amount = items[i - 1].Value - asset.Salvge.Value; var monthes = 12 * (lastYear - dt.Year) + lastMonth - dt.Month; if (amount.IsZero() || monthes < 0) // Ended, Over-depreciated or Dispositoned { if (i < items.Count) { continue; // If another AcquisationItem exists } break; } items.Insert( i, new DepreciateItem { Date = dt, Amount = amount / (monthes + 1), Value = items[i - 1].Value - amount / (monthes + 1) }); dt = AccountantHelper.LastDayOfMonth(dt.Year, dt.Month + 1); } } //if (mo < 12) // for (var mon = mo + 1; mon <= 12; mon++) // items.Add( // new DepreciateItem // { // Date = LastDayOfMonth(yr, mon), // Amount = amount / n / 12 // }); //for (var year = 1; year < n; year++) // for (var mon = 1; mon <= 12; mon++) // items.Add( // new DepreciateItem // { // Date = LastDayOfMonth(yr + year, mon), // Amount = amount / n / 12 // }); //// if (mo > 0) //{ // for (var mon = 1; mon <= mo; mon++) // items.Add( // new DepreciateItem // { // Date = LastDayOfMonth(yr + n, mon), // Amount = amount / n / 12 // }); //} break; case DepreciationMethod.SumOfTheYear: if (items.Any(a => a is DevalueItem || a.Remark == AssetItem.IgnoranceMark) || items.Count(a => a is AcquisationItem) != 1) { throw new NotImplementedException(); } { var n = asset.Life.Value; var mo = asset.Date.Value.Month; var yr = asset.Date.Value.Year; var amount = asset.Value.Value - asset.Salvge.Value; var z = n * (n + 1) / 2; var nstar = n - mo / 12D; var zstar = (Math.Floor(nstar) + 1) * (Math.Floor(nstar) + 2 * (nstar - Math.Floor(nstar))) / 2; if (mo < 12) { var a = amount * n / z * (12 - mo) / z; amount -= a; for (var mon = mo + 1; mon <= 12; mon++) { items.Add( new DepreciateItem { Date = AccountantHelper.LastDayOfMonth(yr, mon), Amount = a / (12 - mo) }); } } for (var year = 1; year < n; year++) { for (var mon = 1; mon <= 12; mon++) { items.Add( new DepreciateItem { Date = AccountantHelper.LastDayOfMonth(yr + year, mon), Amount = amount * (nstar - year + 1) / zstar / 12 }); } } // if (mo > 0) { for (var mon = 1; mon <= mo; mon++) { items.Add( new DepreciateItem { Date = AccountantHelper.LastDayOfMonth(yr + n, mon), Amount = amount * (nstar - (n + 1) + 2) / zstar / 12 }); } } } break; case DepreciationMethod.DoubleDeclineMethod: throw new NotImplementedException(); } asset.Schedule = items; }
/// <summary> /// 调整资产计算表 /// </summary> /// <param name="asset">资产</param> /// <returns>资产</returns> public static Asset InternalRegular(Asset asset) { if (asset.Remark == Asset.IgnoranceMark) { return(asset); } if (!asset.Date.HasValue || !asset.Value.HasValue) { return(asset); } var lst = asset.Schedule?.ToList() ?? new List <AssetItem>(); foreach (var assetItem in lst) { if (assetItem is DepreciateItem || assetItem is DevalueItem) { if (assetItem.Date.HasValue) { assetItem.Date = AccountantHelper.LastDayOfMonth( assetItem.Date.Value.Year, assetItem.Date.Value.Month); } } } lst.Sort(new AssetItemComparer()); if (lst.Count == 0 || !(lst[0] is AcquisationItem acq0)) { lst.Insert( 0, new AcquisationItem { Date = asset.Date, OrigValue = asset.Value.Value }); } else if (lst[0].Remark != AssetItem.IgnoranceMark) { acq0.Date = asset.Date; acq0.OrigValue = asset.Value.Value; } var bookValue = 0D; for (var i = 0; i < lst.Count; i++) { var item = lst[i]; if (item is AcquisationItem acq) { bookValue += acq.OrigValue; item.Value = bookValue; } else if (item is DepreciateItem dep) { bookValue -= dep.Amount; item.Value = bookValue; } else if (item is DevalueItem dev) { if (bookValue <= dev.FairValue && item.Remark != AssetItem.IgnoranceMark) { lst.RemoveAt(i--); continue; } dev.Amount = bookValue - dev.FairValue; bookValue = dev.FairValue; item.Value = dev.FairValue; } else if (item is DispositionItem) { bookValue = 0; } } asset.Schedule = lst; return(asset); }