protected static DateRange GetInvoiceItemDateRange(IPackageItem packageItem, IList <InvoiceDomain> packageInvoices, DateTimeOffset invoiceEndDate) { var startDate = GetActualStartDate(packageItem, packageInvoices); var endDate = Dates.Min(packageItem.EndDate, invoiceEndDate); return(new DateRange(startDate, endDate)); }
/// <summary> /// This function parse frame from data /// </summary> /// <param name="data">Data to parse</param> /// <param name="action">Function to get frame item type</param> private void InternalParseFrame(Byte[] data, Func <UInt16, UInt32, PackageItemTypes> action) { //vriables UInt32 address = 0x00; UInt16 length = 0x00; Byte[] dataItem = null; PackageItemTypes type = PackageItemTypes.Unkown; int count = data.Length; //parse frame for (int i = 0; i < count; i++) { //read address address = (UInt32)(data[i + 3] << 24 | data[i + 2] << 16 | data[i + 1] << 8 | data[i]); length = (UInt16)(data[i + 5] << 8 | data[i + 4]); //read frame item type type = action != null?action(this.Address, address) : PackageItemTypes.Unkown; //read data dataItem = new Byte[length]; Buffer.BlockCopy(data, i + 6, dataItem, 0, length); //parse IPackageItem item = this.InternalParseFrame(type, address, length, dataItem); if (item != null) { this.m_items.Add(item); } //next i += 5 + length; } }
private static Refund TryRefundBeforeFirstPaidDate( IPackageItem packageItem, List <InvoiceItem> paidInvoiceItems, Func <DateRange, decimal, decimal> calculateCurrentCost) { var unpaidRange = new DateRange( Dates.Max(packageItem.StartDate, PayrunConstants.DefaultStartDate), paidInvoiceItems.Min(item => item.FromDate).AddDays(-1)); if (unpaidRange.WeeksInclusive <= 0) { return(null); } // package item start date has been shifted before previously paid invoices - create adjustment for this unpaid period // ............[--inv1--][--inv2--] // ..[--ref0--][--ref1--][--ref2--] var currentCost = calculateCurrentCost(unpaidRange, unpaidRange.WeeksInclusive); return(new Refund { StartDate = unpaidRange.StartDate, EndDate = unpaidRange.EndDate, Quantity = unpaidRange.WeeksInclusive, Amount = currentCost.Round(2) }); }
protected static DateTimeOffset GetActualStartDate(IPackageItem packageItem, IList <InvoiceDomain> packageInvoices) { var latestInvoiceItem = GetLatestInvoiceItem(packageItem, packageInvoices); return(latestInvoiceItem != null ? latestInvoiceItem.ToDate.AddDays(1) : Dates.Max(packageItem.StartDate, PayrunConstants.DefaultStartDate)); }
public static bool IsReferenced(this IPackageItem packageItem, InvoiceItem invoiceItem) { return(packageItem switch { CarePackageDetail _ => packageItem.Id == invoiceItem.CarePackageDetailId, CarePackageReclaim _ => packageItem.Id == invoiceItem.CarePackageReclaimId, _ => throw new InvalidOperationException($"Unsupported package item type {packageItem.GetType()}") });
private static List <InvoiceItem> GetPaidInvoiceItems(IPackageItem packageItem, IList <InvoiceDomain> packageInvoices) { return(packageInvoices .Where(invoice => invoice.Status is InvoiceStatus.Accepted && invoice.PayrunStatus.In(PayrunStatus.Paid, PayrunStatus.PaidWithHold, PayrunStatus.Approved)) .SelectMany(invoice => invoice.Items) .Where(packageItem.IsReferenced) .ToList()); }
public static IEnumerable <Refund> Calculate( IPackageItem packageItem, IList <InvoiceDomain> packageInvoices, Func <DateRange, decimal, decimal> calculateCurrentCost) { var paidInvoiceItems = GetPaidInvoiceItems(packageItem, packageInvoices); if (!paidInvoiceItems.Any()) { yield break; // no payments. no refunds } // package item has some significant updates (cost, dates etc.) -> create a new refund if (packageItem.Version > paidInvoiceItems.Max(item => item.SourceVersion)) { // handle possible shift of package item start date before first paid invoice var refund = TryRefundBeforeFirstPaidDate(packageItem, paidInvoiceItems, calculateCurrentCost); if ((refund != null) && (refund.Amount != 0.0m)) { yield return(refund); } // always create a refund in range of first invoice to avoid // tricky cases with jagged diapasons of refunds / rejected / normal items // [--inv1--][--inv2--][--inv2--] // [--ref1--][--ref2--][--ref2--] var invoiceItemsGroups = paidInvoiceItems.GroupBy(item => item.FromDate); foreach (var paidItems in invoiceItemsGroups) { var currentRange = new DateRange( Dates.Max(packageItem.StartDate, paidItems.First().FromDate), Dates.Min(packageItem.EndDate, paidItems.First().ToDate)); // if package item date range moved into future, its start date can be greater than last invoice date // no payments now needed for new range, but we still need to refund already paid. // So set quantity and current cost to 0 and let the rest of logic flow. var quantity = Math.Max(currentRange.WeeksInclusive, 0); var currentCost = calculateCurrentCost(currentRange, quantity).Round(2); var refundAmount = CalculateRefundAmount(packageItem, currentCost, paidItems.ToList()).Round(2); if (refundAmount != 0.0m) { yield return(new Refund { StartDate = paidItems.First().FromDate, EndDate = paidItems.First().ToDate, Quantity = quantity, Amount = refundAmount }); } } } }
/// <summary> /// Removes the first occurrence of a specific object with address /// </summary> /// <param name="address">Specific address</param> public void Remove(UInt16 address) { for (int i = 0; i < this.m_items.Count; i++) { IPackageItem item = this.m_items[i]; if (item.Address == address) { this.Remove(item); return; } } }
private static string CalculatePackageStatus(CarePackage package, IPackageItem coreCost) { var today = DateTimeOffset.UtcNow.Date; return(package.Status switch { PackageStatus.Approved when coreCost.EndDate != null && coreCost.EndDate.GetValueOrDefault().Date < today => "Ended", PackageStatus.Approved when IsValidDateRange(coreCost.StartDate, coreCost.EndDate) => "Active", PackageStatus.Approved => "Future", _ => package.Status.GetDisplayName() });
/// <summary> /// This function serializes object to frame group /// </summary> /// <typeparam name="T">Data type</typeparam> /// <param name="data">Data object</param> /// <param name="reflection">reflection info for this data type</param> /// <param name="PackageGroupAttribute">Current group attribute</param> /// <returns>Group | null</returns> private static PackageGroupItem InternalSerializeV2ToGroup <T>(T data, ReflectionType reflection, PackageGroupAttribute PackageGroupAttribute) { //variables Object value = null; //create group PackageGroupItem group = new PackageGroupItem(PackageGroupAttribute.Address); //set property foreach (ReflectionProperty item in reflection.PropertyCollection.Values) { //check property type if (item.Property.CanRead) { //get current attribute PackageItemAttribute attribute = item.GetCustomAttribute <PackageItemAttribute>(); if (attribute != null) { try { value = item.Property.GetValue(data, null); if (value != null) { IPackageItem frameItem = Package.CreatePackageItem(attribute.Type, attribute.Address, value); if (frameItem != null) { group.Add(frameItem); } } } catch (Exception ex) { throw new Exception(String.Format("Error type: {0} -> {1} [{2}]", attribute.Type, item.Property.Name, value), ex); } } } } //return current group return(group); }
/// <summary> /// Serializes the specified Object to Frame /// </summary> /// <typeparam name="T">The type of the object to serialize to.</typeparam> /// <param name="obj">The object to serialize.</param> /// <param name="frame">Frame</param> /// <param name="reflection">Reflection information for the Object to deserialize</param> /// <returns>Frame | null</returns> private static Package InternalSerialize <T>(T obj, Package frame, ReflectionType reflection) { //set property foreach (ReflectionProperty item in reflection.PropertyCollection.Values) { //check property type if (item.Property.CanRead) { //get current attribute PackageItemAttribute attribute = item.GetCustomAttribute <PackageItemAttribute>(); if (attribute != null) { Object value = null; try { value = item.Property.GetValue(obj, null); if (value != null) { IPackageItem frameItem = Package.CreatePackageItem(attribute.Type, attribute.Address, value); if (frameItem != null) { frame.Add(frameItem); } } } catch (Exception ex) { throw new Exception(String.Format("Error type: {0} -> {1} [{2}]", attribute.Type, item.Property.Name, value), ex); } } } } //return result return(frame); }
private static decimal CalculateRefundAmount(IPackageItem packageItem, decimal currentCost, IList <InvoiceItem> paidInvoiceItems) { return(packageItem is CarePackageDetail ? CalculatePaymentRefundAmount(currentCost, paidInvoiceItems) : CalculateReclaimRefundAmount((CarePackageReclaim)packageItem, currentCost, paidInvoiceItems)); }
public bool Match(IPackageItem item) { return(true); }
/// <summary> /// Adds an item to the end of the Frame /// </summary> /// <param name="item">The item to be added to the end of the Frame</param> public void Add(IPackageItem item) { this.m_items.Add(item); }
/// <summary> /// Removes the first occurrence of a specific object /// </summary> /// <param name="item">The object to remove</param> public void Remove(IPackageItem item) { this.m_items.Remove(item); }
/// <summary> /// Inserts an item into the Frame at the specified index. /// </summary> /// <param name="item">The object to insert. The value can be null for reference types.</param> /// <param name="index">The zero-based index at which item should be inserted.</param> public void Add(IPackageItem item, int index) { this.m_items.Insert(index, item); }
/// <summary> /// This function parse frame from data /// </summary> /// <param name="data">Data to parse</param> /// <param name="action">Function to get frame item type</param> private void InternalParseFrame(Byte[] data, Func <UInt16, UInt16, UInt32, PackageItemTypes> action) { //vriables UInt32 address = 0x00; UInt16 length = 0x00; Byte[] dataItem = null; PackageItemTypes type = PackageItemTypes.Unkown; int count = data.Length; UInt16 itemCount = 0x00; UInt16 dataAddress = 0x00; int currentIndex = 0x00; //parse data while (currentIndex < count) { //check group value if (data[currentIndex++] != 0x67) { throw new Exception("Data are not valid!"); } //get data address dataAddress = (UInt16)(data[currentIndex + 1] << 8 | data[currentIndex]); //get count+ itemCount = (UInt16)(data[currentIndex + 3] << 8 | data[currentIndex + 2]); currentIndex += 0x04; //create group PackageGroupItem group = new PackageGroupItem(dataAddress); //parse items for (int i = 0; i < itemCount; i++) { //read address address = (UInt32)(data[currentIndex + 3] << 24 | data[currentIndex + 2] << 16 | data[currentIndex + 1] << 8 | data[currentIndex]); length = (UInt16)(data[currentIndex + 5] << 8 | data[currentIndex + 4]); //read frame item type type = action != null?action(this.Address, dataAddress, address) : PackageItemTypes.Unkown; //read data dataItem = new Byte[length]; Buffer.BlockCopy(data, currentIndex + 6, dataItem, 0, length); //parse IPackageItem item = this.InternalParseFrame(type, address, length, dataItem); if (item != null) { group.Add(item); } //next currentIndex += 0x06 + length; } //add group to frame this.m_groups.Add(group); } }
public void OnItemRemoved(BasePackage basePackage, int index, IPackageItem lastItem) { UpdateItem(index, null); }