diff --git a/WooCommerce/Classes/Copiable/Models+Copiable.generated.swift b/WooCommerce/Classes/Copiable/Models+Copiable.generated.swift index 42fa1711d5b..68f9850829f 100644 --- a/WooCommerce/Classes/Copiable/Models+Copiable.generated.swift +++ b/WooCommerce/Classes/Copiable/Models+Copiable.generated.swift @@ -16,7 +16,8 @@ extension WooCommerce.AggregateOrderItem { sku: NullableCopiableProp = .copy, total: NullableCopiableProp = .copy, imageURL: NullableCopiableProp = .copy, - attributes: CopiableProp<[OrderItemAttribute]> = .copy + attributes: CopiableProp<[OrderItemAttribute]> = .copy, + parent: NullableCopiableProp = .copy ) -> WooCommerce.AggregateOrderItem { let itemID = itemID ?? self.itemID let productID = productID ?? self.productID @@ -28,6 +29,7 @@ extension WooCommerce.AggregateOrderItem { let total = total ?? self.total let imageURL = imageURL ?? self.imageURL let attributes = attributes ?? self.attributes + let parent = parent ?? self.parent return WooCommerce.AggregateOrderItem( itemID: itemID, @@ -39,7 +41,8 @@ extension WooCommerce.AggregateOrderItem { sku: sku, total: total, imageURL: imageURL, - attributes: attributes + attributes: attributes, + parent: parent ) } } diff --git a/WooCommerce/Classes/Tools/AggregateData/AggregateDataHelper.swift b/WooCommerce/Classes/Tools/AggregateData/AggregateDataHelper.swift index e118d1dc35f..d1143a621c7 100644 --- a/WooCommerce/Classes/Tools/AggregateData/AggregateDataHelper.swift +++ b/WooCommerce/Classes/Tools/AggregateData/AggregateDataHelper.swift @@ -48,13 +48,16 @@ final class AggregateDataHelper { .compactMap { currency.convertToDecimal($0.total) } .reduce(NSDecimalNumber(value: 0), { $0.adding($1) }) - let attributes = orderItems.first(where: { + // Find the order item matching this refund, to get its properties + let matchingOrderItem = orderItems.first(where: { guard let refundedItemID = item.refundedItemID else { return false } return $0.itemID == Int64(refundedItemID) - })?.attributes ?? [] + }) + let attributes = matchingOrderItem?.attributes ?? [] + let parent = matchingOrderItem?.parent return AggregateOrderItem( itemID: key, @@ -65,7 +68,8 @@ final class AggregateDataHelper { quantity: totalQuantity, sku: item.sku, total: total, - attributes: attributes + attributes: attributes, + parent: parent ) } @@ -93,7 +97,8 @@ final class AggregateDataHelper { quantity: item.quantity, sku: item.sku, total: total, - attributes: item.attributes + attributes: item.attributes, + parent: item.parent ) } @@ -128,7 +133,8 @@ final class AggregateDataHelper { quantity: totalQuantity, sku: item.sku, total: total, - attributes: item.attributes + attributes: item.attributes, + parent: item.parent ) } diff --git a/WooCommerce/Classes/ViewModels/Order Details/Aggregate Order Items/AggregateOrderItem.swift b/WooCommerce/Classes/ViewModels/Order Details/Aggregate Order Items/AggregateOrderItem.swift index 6696ad16bc0..ee5be4ad70c 100644 --- a/WooCommerce/Classes/ViewModels/Order Details/Aggregate Order Items/AggregateOrderItem.swift +++ b/WooCommerce/Classes/ViewModels/Order Details/Aggregate Order Items/AggregateOrderItem.swift @@ -26,6 +26,10 @@ struct AggregateOrderItem: Equatable, GeneratedCopiable { let attributes: [OrderItemAttribute] + /// Item ID of the parent order item, if any. + /// + let parent: Int64? + /// Designated initializer. /// init(itemID: String, @@ -37,7 +41,8 @@ struct AggregateOrderItem: Equatable, GeneratedCopiable { sku: String?, total: NSDecimalNumber?, imageURL: URL? = nil, - attributes: [OrderItemAttribute]) { + attributes: [OrderItemAttribute], + parent: Int64?) { self.itemID = itemID self.productID = productID self.variationID = variationID @@ -48,6 +53,7 @@ struct AggregateOrderItem: Equatable, GeneratedCopiable { self.total = total self.imageURL = imageURL self.attributes = attributes + self.parent = parent } } diff --git a/WooCommerce/Classes/ViewModels/Order Details/Shipping Labels/AggregatedShippingLabelOrderItems.swift b/WooCommerce/Classes/ViewModels/Order Details/Shipping Labels/AggregatedShippingLabelOrderItems.swift index c2843a1a6b2..a0cbd17a4a0 100644 --- a/WooCommerce/Classes/ViewModels/Order Details/Shipping Labels/AggregatedShippingLabelOrderItems.swift +++ b/WooCommerce/Classes/ViewModels/Order Details/Shipping Labels/AggregatedShippingLabelOrderItems.swift @@ -109,7 +109,7 @@ private extension AggregatedShippingLabelOrderItems { func orderItem(from model: OrderItemModel, quantity: Int) -> AggregateOrderItem { switch model { case .productName(let name): - return .init(itemID: "0", productID: 0, variationID: 0, name: name, price: nil, quantity: 0, sku: nil, total: nil, attributes: []) + return .init(itemID: "0", productID: 0, variationID: 0, name: name, price: nil, quantity: 0, sku: nil, total: nil, attributes: [], parent: nil) case .product(let product, let orderItem, let name): let itemID = orderItem?.itemID.description ?? "0" let productName = orderItem?.name ?? name @@ -131,7 +131,8 @@ private extension AggregatedShippingLabelOrderItems { sku: orderItem?.sku ?? product.sku, total: totalPrice, imageURL: imageURL, - attributes: orderItem?.attributes ?? []) + attributes: orderItem?.attributes ?? [], + parent: orderItem?.parent) case .productVariation(let variation, let orderItem, let name): let itemID = orderItem?.itemID.description ?? "0" let productName = orderItem?.name ?? name @@ -153,7 +154,8 @@ private extension AggregatedShippingLabelOrderItems { sku: orderItem?.sku ?? variation.sku, total: totalPrice, imageURL: imageURL, - attributes: orderItem?.attributes ?? []) + attributes: orderItem?.attributes ?? [], + parent: orderItem?.parent) } } diff --git a/WooCommerce/WooCommerceTests/Tools/AggregateDataHelperTests.swift b/WooCommerce/WooCommerceTests/Tools/AggregateDataHelperTests.swift index 613f474c16c..1436c07fabe 100644 --- a/WooCommerce/WooCommerceTests/Tools/AggregateDataHelperTests.swift +++ b/WooCommerce/WooCommerceTests/Tools/AggregateDataHelperTests.swift @@ -98,6 +98,23 @@ final class AggregateDataHelperTests: XCTestCase { XCTAssertEqual(aggregatedOrderItems[0].attributes, testOrderItemAttributes) } + func test_refunded_AggregateOrderItem_has_parent_from_OrderItem() throws { + // Given + let productID: Int64 = 1 + let parentID: Int64 = 61 + let orderItems = [MockOrderItem.sampleItem(itemID: parentID, productID: productID, quantity: 1), + MockOrderItem.sampleItem(itemID: 62, productID: productID, quantity: 1, parent: parentID)] + let refundItems = [MockRefunds.sampleRefundItem(productID: productID, refundedItemID: "62", quantity: 1)] + let refunds = [MockRefunds.sampleRefund(items: refundItems)] + + // When + let aggregatedRefundItems = AggregateDataHelper.combineRefundedProducts(from: refunds, orderItems: orderItems) + + // Then + let actualParentID = try XCTUnwrap(aggregatedRefundItems?.first).parent + XCTAssertEqual(actualParentID, parentID) + } + func test_two_order_items_with_same_productID_and_different_itemIDs_create_two_AggregateOrderItems() { // Given let productID: Int64 = 1 @@ -183,7 +200,8 @@ private extension AggregateDataHelperTests { quantity: -1, sku: "HOODIE-HAPPY-NINJA", total: currencyFormatter.convertToDecimal("-31.50") ?? NSDecimalNumber.zero, - attributes: [] + attributes: [], + parent: nil ) let item1 = AggregateOrderItem( @@ -195,7 +213,8 @@ private extension AggregateDataHelperTests { quantity: -1, sku: "T-SHIRT-NINJA-SILHOUETTE", total: currencyFormatter.convertToDecimal("-18.00") ?? NSDecimalNumber.zero, - attributes: [] + attributes: [], + parent: nil ) let item2 = AggregateOrderItem( @@ -207,7 +226,8 @@ private extension AggregateDataHelperTests { quantity: -1, sku: "HOODIE-SHIP-YOUR-IDEA-BLACK-L", total: currencyFormatter.convertToDecimal("-31.50") ?? NSDecimalNumber.zero, - attributes: testOrderItemAttributes + attributes: testOrderItemAttributes, + parent: nil ) let item3 = AggregateOrderItem( @@ -219,7 +239,8 @@ private extension AggregateDataHelperTests { quantity: -2, sku: "HOODIE-WOO-LOGO", total: currencyFormatter.convertToDecimal("-63.00") ?? NSDecimalNumber.zero, - attributes: [] + attributes: [], + parent: nil ) let item4 = AggregateOrderItem( @@ -231,7 +252,8 @@ private extension AggregateDataHelperTests { quantity: -3, sku: "HOODIE-SHIP-YOUR-IDEA-BLUE-XL", total: currencyFormatter.convertToDecimal("-81.00") ?? NSDecimalNumber.zero, - attributes: [] + attributes: [], + parent: nil ) return [item0, item1, item2, item3, item4] diff --git a/WooCommerce/WooCommerceTests/Tools/MockAggregateOrderItem.swift b/WooCommerce/WooCommerceTests/Tools/MockAggregateOrderItem.swift index 2acc8a15c62..c1fdd6ed49d 100644 --- a/WooCommerce/WooCommerceTests/Tools/MockAggregateOrderItem.swift +++ b/WooCommerce/WooCommerceTests/Tools/MockAggregateOrderItem.swift @@ -14,6 +14,7 @@ public struct MockAggregateOrderItem { quantity: 0, sku: nil, total: nil, - attributes: []) + attributes: [], + parent: nil) } } diff --git a/WooCommerce/WooCommerceTests/Tools/MockOrderItem.swift b/WooCommerce/WooCommerceTests/Tools/MockOrderItem.swift index 197c0781d87..7d59877638d 100644 --- a/WooCommerce/WooCommerceTests/Tools/MockOrderItem.swift +++ b/WooCommerce/WooCommerceTests/Tools/MockOrderItem.swift @@ -33,6 +33,6 @@ public struct MockOrderItem { total: total, totalTax: totalTax, attributes: attributes, - parent: nil) + parent: parent) } } diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/ProductDetailsCellViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/ProductDetailsCellViewModelTests.swift index 3bbbee347dd..83926749223 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/ProductDetailsCellViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/ProductDetailsCellViewModelTests.swift @@ -225,7 +225,8 @@ private extension ProductDetailsCellViewModelTests { total: NSDecimalNumber?, sku: String = "", imageURL: URL? = nil, - attributes: [OrderItemAttribute] = []) -> AggregateOrderItem { + attributes: [OrderItemAttribute] = [], + parent: Int64? = nil) -> AggregateOrderItem { AggregateOrderItem(itemID: "2", productID: 1, variationID: 6, @@ -235,7 +236,8 @@ private extension ProductDetailsCellViewModelTests { sku: sku, total: total, imageURL: imageURL, - attributes: attributes) + attributes: attributes, + parent: parent) } func makeOrderItemRefund(quantity: Decimal, diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/Shipping Labels/AggregatedShippingLabelOrderItemsTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/Shipping Labels/AggregatedShippingLabelOrderItemsTests.swift index 1bb84cb25a1..7e69a40bfe0 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/Shipping Labels/AggregatedShippingLabelOrderItemsTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Details/Shipping Labels/AggregatedShippingLabelOrderItemsTests.swift @@ -17,7 +17,16 @@ final class AggregatedShippingLabelOrderItemsTests: XCTestCase { // Then XCTAssertEqual(shippingLabelOrderItems, [ - .init(itemID: "0", productID: 0, variationID: 0, name: "Password protected!", price: nil, quantity: 0, sku: nil, total: nil, attributes: []) + .init(itemID: "0", + productID: 0, + variationID: 0, + name: "Password protected!", + price: nil, + quantity: 0, + sku: nil, + total: nil, + attributes: [], + parent: nil) ]) XCTAssertEqual(shippingLabelOrderItems[0], aggregatedOrderItems.orderItem(of: shippingLabel, at: 0)) } @@ -50,10 +59,20 @@ final class AggregatedShippingLabelOrderItemsTests: XCTestCase { sku: orderItem1.sku, total: 59.2, imageURL: imageURL1, - attributes: orderItem1.attributes), + attributes: orderItem1.attributes, + parent: nil), // Product with ID 3013 does not have a matching OrderItem so the price and SKU come from the Product. // Since a Product's name could change, the name falls back to the name in shipping label's `productNames`. - .init(itemID: "0", productID: 3013, variationID: 0, name: "PW", price: 25.9, quantity: 3, sku: product2.sku, total: 77.7, attributes: []) + .init(itemID: "0", + productID: 3013, + variationID: 0, + name: "PW", + price: 25.9, + quantity: 3, + sku: product2.sku, + total: 77.7, + attributes: [], + parent: nil) ]) XCTAssertEqual(shippingLabelOrderItems[0], aggregatedOrderItems.orderItem(of: shippingLabel, at: 0)) XCTAssertEqual(shippingLabelOrderItems[1], aggregatedOrderItems.orderItem(of: shippingLabel, at: 1)) @@ -97,7 +116,8 @@ final class AggregatedShippingLabelOrderItemsTests: XCTestCase { sku: orderItem.sku, total: 25.9, imageURL: imageURL, - attributes: orderItem.attributes) + attributes: orderItem.attributes, + parent: nil) ]) XCTAssertEqual(shippingLabelOrderItems[0], aggregatedOrderItems.orderItem(of: shippingLabel, at: 0)) } @@ -131,7 +151,8 @@ final class AggregatedShippingLabelOrderItemsTests: XCTestCase { sku: variation.sku, total: 62, imageURL: imageURL, - attributes: []) + attributes: [], + parent: nil) ]) XCTAssertEqual(shippingLabelOrderItems[0], aggregatedOrderItems.orderItem(of: shippingLabel, at: 0)) } diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Products/Product Loader/ProductLoaderViewControllerModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Products/Product Loader/ProductLoaderViewControllerModelTests.swift index cd569cbdcae..8233731d78c 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Products/Product Loader/ProductLoaderViewControllerModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Products/Product Loader/ProductLoaderViewControllerModelTests.swift @@ -134,7 +134,8 @@ private extension ProductLoaderViewControllerModelTests { quantity: 3, sku: nil, total: 3.6, - attributes: []) + attributes: [], + parent: nil) } func makeTopEarnerStatsItem(productID: Int64) -> TopEarnerStatsItem {