diff --git a/Modules/Sources/Fakes/Networking.generated.swift b/Modules/Sources/Fakes/Networking.generated.swift index 221bf6d81a5..931e388a8d9 100644 --- a/Modules/Sources/Fakes/Networking.generated.swift +++ b/Modules/Sources/Fakes/Networking.generated.swift @@ -338,6 +338,7 @@ extension Networking.Booking { resourceID: .fake(), startDate: .fake(), statusKey: .fake(), + attendanceStatusKey: .fake(), localTimezone: .fake(), currency: .fake(), orderInfo: .fake() diff --git a/Modules/Sources/Networking/Model/Bookings/Booking.swift b/Modules/Sources/Networking/Model/Bookings/Booking.swift index 21a6d331e47..4cdb6ff4bb8 100644 --- a/Modules/Sources/Networking/Model/Bookings/Booking.swift +++ b/Modules/Sources/Networking/Model/Bookings/Booking.swift @@ -20,6 +20,7 @@ public struct Booking: Codable, GeneratedCopiable, Hashable, GeneratedFakeable { public let resourceID: Int64 public let startDate: Date public let statusKey: String + public let attendanceStatusKey: String public let localTimezone: String public let currency: String public let orderInfo: BookingOrderInfo? @@ -28,6 +29,11 @@ public struct Booking: Codable, GeneratedCopiable, Hashable, GeneratedFakeable { return BookingStatus(rawValue: statusKey) ?? .unknown } + /// periphery: ignore - will be used in UI in upcoming PRs + public var attendanceStatus: BookingAttendanceStatus { + return BookingAttendanceStatus(rawValue: attendanceStatusKey) ?? .unknown + } + /// Booking struct initializer. /// public init(siteID: Int64, @@ -46,6 +52,7 @@ public struct Booking: Codable, GeneratedCopiable, Hashable, GeneratedFakeable { resourceID: Int64, startDate: Date, statusKey: String, + attendanceStatusKey: String, localTimezone: String, currency: String, orderInfo: BookingOrderInfo?) { @@ -65,6 +72,7 @@ public struct Booking: Codable, GeneratedCopiable, Hashable, GeneratedFakeable { self.resourceID = resourceID self.startDate = startDate self.statusKey = statusKey + self.attendanceStatusKey = attendanceStatusKey self.localTimezone = localTimezone self.currency = currency self.orderInfo = orderInfo @@ -99,6 +107,7 @@ public struct Booking: Codable, GeneratedCopiable, Hashable, GeneratedFakeable { let resourceID = try container.decode(Int64.self, forKey: .resourceID) let startDate = Date(timeIntervalSince1970: try container.decode(Double.self, forKey: .startDate)) let statusKey = try container.decode(String.self, forKey: .statusKey) + let attendanceStatusKey = container.failsafeDecodeIfPresent(String.self, forKey: .attendanceStatusKey) ?? "" let localTimezone = try container.decode(String.self, forKey: .localTimezone) let currency = try container.decode(String.self, forKey: .currency) let orderInfo: BookingOrderInfo? = nil // to be prefilled when synced @@ -119,6 +128,7 @@ public struct Booking: Codable, GeneratedCopiable, Hashable, GeneratedFakeable { resourceID: resourceID, startDate: startDate, statusKey: statusKey, + attendanceStatusKey: attendanceStatusKey, localTimezone: localTimezone, currency: currency, orderInfo: orderInfo) @@ -142,6 +152,7 @@ public struct Booking: Codable, GeneratedCopiable, Hashable, GeneratedFakeable { try container.encode(resourceID, forKey: .resourceID) try container.encode(startDate, forKey: .startDate) try container.encode(statusKey, forKey: .statusKey) + try container.encode(attendanceStatusKey, forKey: .attendanceStatusKey) try container.encode(localTimezone, forKey: .localTimezone) } } @@ -171,6 +182,7 @@ private extension Booking { case resourceID = "resource_id" case startDate = "start" case statusKey = "status" + case attendanceStatusKey = "attendance_status" case localTimezone = "local_timezone" case currency } @@ -185,7 +197,6 @@ enum BookingDecodingError: Error { // MARK: - Supporting Types // -// periphery: ignore /// Represents a Booking Status. public enum BookingStatus: String, CaseIterable { case complete @@ -196,3 +207,12 @@ public enum BookingStatus: String, CaseIterable { case confirmed case unknown } + +/// periphery: ignore - will be used in UI in upcoming PRs +public enum BookingAttendanceStatus: String, CaseIterable { + case booked + case checkedIn = "checked-in" + case cancelled + case noShow = "no-show" + case unknown +} diff --git a/Modules/Sources/Networking/Model/Copiable/Models+Copiable.generated.swift b/Modules/Sources/Networking/Model/Copiable/Models+Copiable.generated.swift index 91f7c131fe2..ff51dabe27f 100644 --- a/Modules/Sources/Networking/Model/Copiable/Models+Copiable.generated.swift +++ b/Modules/Sources/Networking/Model/Copiable/Models+Copiable.generated.swift @@ -446,6 +446,7 @@ extension Networking.Booking { resourceID: CopiableProp = .copy, startDate: CopiableProp = .copy, statusKey: CopiableProp = .copy, + attendanceStatusKey: CopiableProp = .copy, localTimezone: CopiableProp = .copy, currency: CopiableProp = .copy, orderInfo: NullableCopiableProp = .copy @@ -466,6 +467,7 @@ extension Networking.Booking { let resourceID = resourceID ?? self.resourceID let startDate = startDate ?? self.startDate let statusKey = statusKey ?? self.statusKey + let attendanceStatusKey = attendanceStatusKey ?? self.attendanceStatusKey let localTimezone = localTimezone ?? self.localTimezone let currency = currency ?? self.currency let orderInfo = orderInfo ?? self.orderInfo @@ -487,6 +489,7 @@ extension Networking.Booking { resourceID: resourceID, startDate: startDate, statusKey: statusKey, + attendanceStatusKey: attendanceStatusKey, localTimezone: localTimezone, currency: currency, orderInfo: orderInfo diff --git a/Modules/Sources/Storage/Model/Booking/Booking+CoreDataProperties.swift b/Modules/Sources/Storage/Model/Booking/Booking+CoreDataProperties.swift index f4176800f76..23e8e8eb9b7 100644 --- a/Modules/Sources/Storage/Model/Booking/Booking+CoreDataProperties.swift +++ b/Modules/Sources/Storage/Model/Booking/Booking+CoreDataProperties.swift @@ -19,6 +19,7 @@ extension Booking { @NSManaged public var googleCalendarEventID: String? @NSManaged public var orderItemID: Int64 @NSManaged public var statusKey: String? + @NSManaged public var attendanceStatusKey: String? @NSManaged public var localTimezone: String? @NSManaged public var currency: String? @NSManaged public var orderInfo: BookingOrderInfo? diff --git a/Modules/Sources/Storage/Model/MIGRATIONS.md b/Modules/Sources/Storage/Model/MIGRATIONS.md index 58b314d4d5c..dd85daf3e41 100644 --- a/Modules/Sources/Storage/Model/MIGRATIONS.md +++ b/Modules/Sources/Storage/Model/MIGRATIONS.md @@ -2,6 +2,10 @@ 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 129 (Release X.X.X.X) +- @rafaelkayumov 2025-10-17 + - Added `attendanceStatusKey` attribute to `Booking` entity. + ## Model 128 (Release 23.5.0.0) - @itsmeichigo 2025-10-14 - Added `BookingOrderInfo` entity. diff --git a/Modules/Sources/Storage/Resources/WooCommerce.xcdatamodeld/.xccurrentversion b/Modules/Sources/Storage/Resources/WooCommerce.xcdatamodeld/.xccurrentversion index e0df1183b72..051471ed320 100644 --- a/Modules/Sources/Storage/Resources/WooCommerce.xcdatamodeld/.xccurrentversion +++ b/Modules/Sources/Storage/Resources/WooCommerce.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - Model 128.xcdatamodel + Model 129.xcdatamodel diff --git a/Modules/Sources/Storage/Resources/WooCommerce.xcdatamodeld/Model 129.xcdatamodel/contents b/Modules/Sources/Storage/Resources/WooCommerce.xcdatamodeld/Model 129.xcdatamodel/contents new file mode 100644 index 00000000000..d030970308a --- /dev/null +++ b/Modules/Sources/Storage/Resources/WooCommerce.xcdatamodeld/Model 129.xcdatamodel/contents @@ -0,0 +1,1176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Modules/Sources/Yosemite/Model/Booking/Booking+ReadOnlyConvertible.swift b/Modules/Sources/Yosemite/Model/Booking/Booking+ReadOnlyConvertible.swift index 7e8b5385375..55a62cb3a6f 100644 --- a/Modules/Sources/Yosemite/Model/Booking/Booking+ReadOnlyConvertible.swift +++ b/Modules/Sources/Yosemite/Model/Booking/Booking+ReadOnlyConvertible.swift @@ -24,6 +24,7 @@ extension Storage.Booking: ReadOnlyConvertible { resourceID = booking.resourceID startDate = booking.startDate statusKey = booking.statusKey + attendanceStatusKey = booking.attendanceStatusKey localTimezone = booking.localTimezone currency = booking.currency } @@ -47,6 +48,7 @@ extension Storage.Booking: ReadOnlyConvertible { resourceID: resourceID, startDate: startDate ?? Date(), statusKey: statusKey ?? "", + attendanceStatusKey: attendanceStatusKey ?? "", localTimezone: localTimezone ?? "", currency: currency ?? "USD", orderInfo: orderInfo?.toReadOnly()) diff --git a/Modules/Tests/StorageTests/CoreData/MigrationTests.swift b/Modules/Tests/StorageTests/CoreData/MigrationTests.swift index 515828fc98f..a634d620938 100644 --- a/Modules/Tests/StorageTests/CoreData/MigrationTests.swift +++ b/Modules/Tests/StorageTests/CoreData/MigrationTests.swift @@ -2281,6 +2281,37 @@ final class MigrationTests: XCTestCase { let insertedResource = try XCTUnwrap(targetContext.first(entityName: "BookingResource")) XCTAssertEqual(insertedResource, resource) } + + func test_migrating_from_128_to_129_adds_new_attendanceStatusKey_attribute_to_booking() throws { + // Given + let sourceContainer = try startPersistentContainer("Model 128") + let sourceContext = sourceContainer.viewContext + + let booking = insertBooking(to: sourceContext) + try sourceContext.save() + + XCTAssertNil(booking.entity.attributesByName["attendanceStatusKey"], "Precondition. Attribute does not exist.") + + // When + let targetContainer = try migrate(sourceContainer, to: "Model 129") + + // Then + let targetContext = targetContainer.viewContext + let migratedBooking = try XCTUnwrap(targetContext.first(entityName: "Booking")) + + // `attendanceStatusKey` should be present in `migratedBooking` + XCTAssertNotNil(migratedBooking.entity.attributesByName["attendanceStatusKey"]) + + // `attendanceStatusKey` value should default as "" in model 129 + let value = migratedBooking.value(forKey: "attendanceStatusKey") as? String + XCTAssertEqual(value, "") + + // `attendanceStatusKey` must be settable + migratedBooking.setValue("checked_in", forKey: "attendanceStatusKey") + try targetContext.save() + let updatedValue = migratedBooking.value(forKey: "attendanceStatusKey") as? String + XCTAssertEqual(updatedValue, "checked_in") + } } // MARK: - Persistent Store Setup and Migrations diff --git a/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift b/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift index ebfd276fec3..6b985811f6a 100644 --- a/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift +++ b/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift @@ -335,6 +335,7 @@ struct BookingDetailsView_Previews: PreviewProvider { resourceID: 113, startDate: now, statusKey: "paid", + attendanceStatusKey: "booked", localTimezone: "America/New_York", currency: "USD", orderInfo: nil