Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public struct WooShippingShipment: Equatable, GeneratedFakeable, GeneratedCopiab
/// Represents a shipment item from the WooCommerce Shipping extension.
///
public struct WooShippingShipmentItem: Codable, Equatable, GeneratedFakeable, GeneratedCopiable {
/// ID of the shipment
/// ID of the order item
public let id: Int64

/// Items of the shipment
Expand Down
7 changes: 7 additions & 0 deletions Modules/Sources/Storage/Model/MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

This file documents changes in the WCiOS Storage data model. Please explain any changes to the data model as well as any custom migrations.

## Model 124 (Release 22.9.0.0)
- @itsmeichigo 2025-07-11
- Added `WooShippingShipment` entity.
- Added `WooShippingShipmentItem` entity.
- Added `shipment` relationship to `ShippingLabel` entity.
- Added `shipments` relationship to `Order` entity.

## Model 123 (Release 22.8.0.0)
- @iamgabrielma 2025-06-30
- Added `createdVia` attribute to `Order` entity.
Expand Down
17 changes: 17 additions & 0 deletions Modules/Sources/Storage/Model/Order+CoreDataProperties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ extension Order {
@NSManaged public var shippingLabelSettings: ShippingLabelSettings?
@NSManaged public var taxes: Set<OrderTaxLine>?
@NSManaged public var attributionInfo: OrderAttributionInfo?
@NSManaged public var shipments: Set<WooShippingShipment>?

}

Expand Down Expand Up @@ -277,3 +278,19 @@ extension Order {
@NSManaged public func removeFromAppliedGiftCards(_ values: NSSet)

}

// MARK: Generated accessors for shipments
extension Order {

@objc(addShipmentsObject:)
@NSManaged public func addToShipments(_ value: WooShippingShipment)

@objc(removeShipmentsObject:)
@NSManaged public func removeFromShipments(_ value: WooShippingShipment)

@objc(addShipments:)
@NSManaged public func addToShipments(_ values: NSSet)

@objc(removeShipments:)
@NSManaged public func removeFromShipments(_ values: NSSet)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extension ShippingLabel {
@NSManaged public var destinationAddress: ShippingLabelAddress?
@NSManaged public var refund: ShippingLabelRefund?
@NSManaged public var order: Order?
@NSManaged public var shipment: WooShippingShipment?
@NSManaged public var commercialInvoiceURL: String?
@NSManaged public var usedDate: Date?
@NSManaged public var expiryDate: Date?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation
import CoreData

@objc(WooShippingShipment)
public class WooShippingShipment: NSManagedObject {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Foundation
import CoreData

extension WooShippingShipment {
@nonobjc public class func fetchRequest() -> NSFetchRequest<WooShippingShipment> {
return NSFetchRequest<WooShippingShipment>(entityName: "WooShippingShipment")
}

@NSManaged public var siteID: Int64
@NSManaged public var orderID: Int64
Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to also store siteID and orderID here since those are accessible through order relationship?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order relationship is optional, so I'd prefer having non-optional IDs here instead.

@NSManaged public var index: String
@NSManaged public var shippingLabel: ShippingLabel?
@NSManaged public var items: Set<WooShippingShipmentItem>?
@NSManaged public var order: Order?

}

// MARK: Generated accessors for items
extension WooShippingShipment {

@objc(addItemsObject:)
@NSManaged public func addToItems(_ value: WooShippingShipmentItem)

@objc(removeItemsObject:)
@NSManaged public func removeFromItems(_ value: WooShippingShipmentItem)

@objc(addItems:)
@NSManaged public func addToItems(_ values: NSSet)

@objc(removeItems:)
@NSManaged public func removeFromItems(_ values: NSSet)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation
import CoreData

@objc(WooShippingShipmentItem)
public class WooShippingShipmentItem: NSManagedObject {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import CoreData

extension WooShippingShipmentItem {

@nonobjc public class func fetchRequest() -> NSFetchRequest<WooShippingShipmentItem> {
return NSFetchRequest<WooShippingShipmentItem>(entityName: "WooShippingShipmentItem")
}

@NSManaged public var id: Int64
@NSManaged public var subItems: NSArray?
@NSManaged public var shipment: WooShippingShipment?

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Model 123.xcdatamodel</string>
<string>Model 124.xcdatamodel</string>
</dict>
</plist>

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Modules/Sources/Yosemite/Model/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ public typealias StorageWooShippingPredefinedOption = Storage.WooShippingPredefi
public typealias StorageWooShippingPredefinedPackage = Storage.WooShippingPredefinedPackage
public typealias StorageWooShippingCustomPackage = Storage.WooShippingCustomPackage
public typealias StorageWooShippingSavedPredefinedPackage = Storage.WooShippingSavedPredefinedPackage
public typealias StorageWooShippingShipment = Storage.WooShippingShipment

// MARK: - Internal ReadOnly Models

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ extension Storage.Order: ReadOnlyConvertible {
let orderCustomFields = customFields?.map { $0.toReadOnly() } ?? [Yosemite.MetaData]()
let orderGiftCards = appliedGiftCards?.map { $0.toReadOnly() } ?? [Yosemite.OrderGiftCard]()
let orderShippingLabels = shippingLabels?.map { $0.toReadOnly() } ?? [Yosemite.ShippingLabel]()
let orderShipments = shipments?.map { $0.toReadOnly() } ?? [Yosemite.WooShippingShipment]()

return Order(siteID: siteID,
orderID: orderID,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation
import Storage

// Storage.WooShippingShipment: ReadOnlyConvertible Conformance.
//
extension Storage.WooShippingShipment: ReadOnlyConvertible {
/// Updates the Storage.WooShippingShipment with the a ReadOnly WooShippingShipment.
///
public func update(with savedShipment: Yosemite.WooShippingShipment) {
self.siteID = savedShipment.siteID
self.orderID = savedShipment.orderID
self.index = savedShipment.index
}

/// Returns a ReadOnly version of the receiver.
///
public func toReadOnly() -> Yosemite.WooShippingShipment {
let shipmentShippingLabel = shippingLabel?.toReadOnly()
let shipmentItems = items?.map { $0.toReadOnly() } ?? [Yosemite.WooShippingShipmentItem]()

return WooShippingShipment(siteID: siteID,
orderID: orderID,
index: index,
items: shipmentItems,
shippingLabel: shipmentShippingLabel)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation
import Storage

// Storage.WooShippingShipmentItem: ReadOnlyConvertible Conformance.
//
extension Storage.WooShippingShipmentItem: ReadOnlyConvertible {
/// Updates the Storage.WooShippingShipmentItem with the a ReadOnly WooShippingShipmentItem.
///
public func update(with savedItem: Yosemite.WooShippingShipmentItem) {
self.id = savedItem.id
self.subItems = savedItem.subItems as? NSArray
}

/// Returns a ReadOnly version of the receiver.
///
public func toReadOnly() -> Yosemite.WooShippingShipmentItem {
WooShippingShipmentItem(id: id, subItems: subItems as? [String])
}
}
112 changes: 112 additions & 0 deletions Modules/Tests/StorageTests/CoreData/MigrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3398,6 +3398,101 @@ final class MigrationTests: XCTestCase {
let updatedValue = migratedObject.value(forKey: "createdVia") as? String
XCTAssertEqual(updatedValue, "pos-rest-api")
}

func test_migrating_from_123_to_124_enables_creating_new_WooShippingShipment_and_WooShippingShipmentItem_entities() throws {
// Given
let sourceContainer = try startPersistentContainer("Model 123")
let sourceContext = sourceContainer.viewContext

try sourceContext.save()

// Confidence Check. These entities should not exist in Model 123
XCTAssertNil(NSEntityDescription.entity(forEntityName: "WooShippingShipment", in: sourceContext))
XCTAssertNil(NSEntityDescription.entity(forEntityName: "WooShippingShipmentItem", in: sourceContext))

// When
let targetContainer = try migrate(sourceContainer, to: "Model 124")
let targetContext = targetContainer.viewContext

// Then
XCTAssertEqual(try targetContext.count(entityName: "WooShippingShipment"), 0)
XCTAssertEqual(try targetContext.count(entityName: "WooShippingShipmentItem"), 0)

let shipment = insertWooShippingShipment(to: targetContext)
let shipmentItem = insertWooShippingShipmentItem(to: targetContext)
shipmentItem.setValue(shipment, forKey: "shipment")
XCTAssertNoThrow(try targetContext.save())

XCTAssertEqual(try targetContext.count(entityName: "WooShippingShipment"), 1)
let insertedShipment = try XCTUnwrap(targetContext.firstObject(ofType: WooShippingShipment.self))
XCTAssertEqual(insertedShipment, shipment)
XCTAssertEqual(insertedShipment.items?.count, 1)

XCTAssertEqual(try targetContext.count(entityName: "WooShippingShipmentItem"), 1)
let insertedShipmentItem = try XCTUnwrap(targetContext.firstObject(ofType: WooShippingShipmentItem.self))
XCTAssertEqual(insertedShipmentItem, shipmentItem)
}

func test_migrating_from_123_to_124_adds_new_relationship_shipment_to_shippingLabel() throws {
// Given
let sourceContainer = try startPersistentContainer("Model 123")
let sourceContext = sourceContainer.viewContext

let label = insertShippingLabel(to: sourceContext)
try sourceContext.save()

XCTAssertNil(label.entity.relationshipsByName["shipment"], "Precondition. Relationship does not exist.")

// When
let targetContainer = try migrate(sourceContainer, to: "Model 124")

// Then
let targetContext = targetContainer.viewContext
let migratedLabel = try XCTUnwrap(targetContext.first(entityName: "ShippingLabel"))

// `shipment` should be present in `migratedLabel`
XCTAssertNotNil(migratedLabel.entity.relationshipsByName["shipment"])

let savedShipment = migratedLabel.value(forKey: "shipment") as? WooShippingShipment
XCTAssertNil(savedShipment) // default value

let shipment = insertWooShippingShipment(to: targetContext)
migratedLabel.setValue(shipment, forKey: "shipment")
try targetContext.save()

let updatedShipment = migratedLabel.value(forKey: "shipment") as? WooShippingShipment
XCTAssertEqual(updatedShipment, shipment)
}

func test_migrating_from_123_to_124_adds_new_relationship_shipments_to_order() throws {
// Given
let sourceContainer = try startPersistentContainer("Model 123")
let sourceContext = sourceContainer.viewContext

let order = insertOrder(to: sourceContext)
try sourceContext.save()

XCTAssertNil(order.entity.relationshipsByName["shipments"], "Precondition. Relationship does not exist.")

// When
let targetContainer = try migrate(sourceContainer, to: "Model 124")

// Then
let targetContext = targetContainer.viewContext
let migratedOrder = try XCTUnwrap(targetContext.first(entityName: "Order"))

// `shipments` should be present in `migratedOrder`
XCTAssertNotNil(migratedOrder.entity.relationshipsByName["shipments"])

let savedShipments = migratedOrder.value(forKey: "shipments") as? Set<WooShippingShipment>
XCTAssertEqual(savedShipments?.count, 0) // default value

let shipment = insertWooShippingShipment(to: targetContext)
migratedOrder.mutableSetValue(forKey: "shipments").add(shipment)
try targetContext.save()

XCTAssertEqual(migratedOrder.value(forKey: "shipments") as? Set<NSManagedObject>, [shipment])
}
}

// MARK: - Persistent Store Setup and Migrations
Expand Down Expand Up @@ -4297,4 +4392,21 @@ private extension MigrationTests {
"groupTitle": "USPS Priority Mail Flat Rate Boxes",
])
}

@discardableResult
func insertWooShippingShipment(to context: NSManagedObjectContext) -> NSManagedObject {
context.insert(entityName: "WooShippingShipment", properties: [
"siteID": 1,
"orderID": 2,
"index": "3"
])
}

@discardableResult
func insertWooShippingShipmentItem(to context: NSManagedObjectContext) -> NSManagedObject {
context.insert(entityName: "WooShippingShipmentItem", properties: [
"id": 4,
"subItems": ["sub_1", "sub_2"]
])
}
}