diff --git a/Modules/Sources/Storage/GRDB/Migrations/V001InitialSchema.swift b/Modules/Sources/Storage/GRDB/Migrations/V001InitialSchema.swift index 46e179696ea..dc02ccff017 100644 --- a/Modules/Sources/Storage/GRDB/Migrations/V001InitialSchema.swift +++ b/Modules/Sources/Storage/GRDB/Migrations/V001InitialSchema.swift @@ -29,7 +29,8 @@ struct V001InitialSchema { // not derive them from a Swift class. // https://swiftpackageindex.com/groue/grdb.swift/v7.6.1/documentation/grdb/migrations#Good-Practices-for-Defining-Migrations try db.create(table: "product") { productTable in - productTable.primaryKey("id", .integer).notNull() + productTable.column("id", .integer).notNull() + productTable.primaryKey(["siteID", "id"]) // SiteID column created by belongsTo relationship productTable.belongsTo("site", onDelete: .cascade).notNull() productTable.column("name", .text).notNull() @@ -56,7 +57,12 @@ struct V001InitialSchema { try db.create(table: "productAttribute") { productAttributeTable in // This table holds local product attributes only. Global attributes belong to a site. productAttributeTable.autoIncrementedPrimaryKey("id").notNull() - productAttributeTable.belongsTo("product", onDelete: .cascade).notNull() + productAttributeTable.column("siteID", .integer).notNull() + productAttributeTable.column("productID", .integer).notNull() + productAttributeTable.foreignKey(["siteID", "productID"], + references: "product", + columns: ["siteID", "id"], + onDelete: .cascade) productAttributeTable.column("name", .text).notNull() productAttributeTable.column("position", .integer).notNull() @@ -68,8 +74,14 @@ struct V001InitialSchema { private static func createProductImageTable(_ db: Database) throws { try db.create(table: "productImage") { productImageTable in - productImageTable.primaryKey("id", .integer).notNull() - productImageTable.belongsTo("product", onDelete: .cascade).notNull() + productImageTable.column("siteID", .integer).notNull() + productImageTable.column("id", .integer).notNull() + productImageTable.primaryKey(["siteID", "id"]) + productImageTable.column("productID", .integer).notNull() + productImageTable.foreignKey(["siteID", "productID"], + references: "product", + columns: ["siteID", "id"], + onDelete: .cascade) productImageTable.column("dateCreated", .datetime).notNull() productImageTable.column("dateModified", .datetime) @@ -82,9 +94,14 @@ struct V001InitialSchema { private static func createProductVariationTable(_ db: Database) throws { try db.create(table: "productVariation") { productVariationTable in - productVariationTable.primaryKey("id", .integer).notNull() + productVariationTable.column("id", .integer).notNull() + productVariationTable.primaryKey(["siteID", "id"]) // SiteID column created by belongsTo relationship + productVariationTable.column("productID", .integer).notNull() productVariationTable.belongsTo("site", onDelete: .cascade).notNull() - productVariationTable.belongsTo("product", onDelete: .cascade).notNull() + productVariationTable.foreignKey(["siteID", "productID"], + references: "product", + columns: ["siteID", "id"], + onDelete: .cascade) productVariationTable.column("sku", .text) productVariationTable.column("globalUniqueID", .text) @@ -104,7 +121,12 @@ struct V001InitialSchema { try db.create(table: "productVariationAttribute") { productVariationAttributeTable in // This table holds local variation attributes only. Global attributes belong to a site. productVariationAttributeTable.autoIncrementedPrimaryKey("id").notNull() - productVariationAttributeTable.belongsTo("productVariation", onDelete: .cascade).notNull() + productVariationAttributeTable.column("siteID", .integer).notNull() + productVariationAttributeTable.column("productVariationID", .integer).notNull() + productVariationAttributeTable.foreignKey(["siteID", "productVariationID"], + references: "productVariation", + columns: ["siteID", "id"], + onDelete: .cascade) productVariationAttributeTable.column("name", .text).notNull() productVariationAttributeTable.column("option", .text).notNull() @@ -113,8 +135,14 @@ struct V001InitialSchema { private static func createProductVariationImageTable(_ db: Database) throws { try db.create(table: "productVariationImage") { productVariationImageTable in - productVariationImageTable.primaryKey("id", .integer).notNull() - productVariationImageTable.belongsTo("productVariation", onDelete: .cascade).notNull() + productVariationImageTable.column("siteID", .integer).notNull() + productVariationImageTable.column("id", .integer).notNull() + productVariationImageTable.primaryKey(["siteID", "id"]) + productVariationImageTable.column("productVariationID", .integer).notNull() + productVariationImageTable.foreignKey(["siteID", "productVariationID"], + references: "productVariation", + columns: ["siteID", "id"], + onDelete: .cascade) productVariationImageTable.column("dateCreated", .datetime).notNull() productVariationImageTable.column("dateModified", .datetime) diff --git a/Modules/Sources/Storage/GRDB/Model/PersistedProduct.swift b/Modules/Sources/Storage/GRDB/Model/PersistedProduct.swift index 6b3dad86fc0..e7102605fc9 100644 --- a/Modules/Sources/Storage/GRDB/Model/PersistedProduct.swift +++ b/Modules/Sources/Storage/GRDB/Model/PersistedProduct.swift @@ -53,25 +53,33 @@ public struct PersistedProduct: Codable { extension PersistedProduct: FetchableRecord, PersistableRecord { public static var databaseTableName: String { "product" } + public static var primaryKey: [String] { [CodingKeys.siteID.stringValue, CodingKeys.id.stringValue] } + public enum Columns { - static let id = Column(CodingKeys.id) - static let siteID = Column(CodingKeys.siteID) - static let name = Column(CodingKeys.name) - static let productTypeKey = Column(CodingKeys.productTypeKey) - static let fullDescription = Column(CodingKeys.fullDescription) - static let shortDescription = Column(CodingKeys.shortDescription) - static let sku = Column(CodingKeys.sku) - static let globalUniqueID = Column(CodingKeys.globalUniqueID) - static let price = Column(CodingKeys.price) - static let downloadable = Column(CodingKeys.downloadable) - static let parentID = Column(CodingKeys.parentID) - static let manageStock = Column(CodingKeys.manageStock) - static let stockQuantity = Column(CodingKeys.stockQuantity) - static let stockStatusKey = Column(CodingKeys.stockStatusKey) + public static let id = Column(CodingKeys.id) + public static let siteID = Column(CodingKeys.siteID) + public static let name = Column(CodingKeys.name) + public static let productTypeKey = Column(CodingKeys.productTypeKey) + public static let fullDescription = Column(CodingKeys.fullDescription) + public static let shortDescription = Column(CodingKeys.shortDescription) + public static let sku = Column(CodingKeys.sku) + public static let globalUniqueID = Column(CodingKeys.globalUniqueID) + public static let price = Column(CodingKeys.price) + public static let downloadable = Column(CodingKeys.downloadable) + public static let parentID = Column(CodingKeys.parentID) + public static let manageStock = Column(CodingKeys.manageStock) + public static let stockQuantity = Column(CodingKeys.stockQuantity) + public static let stockStatusKey = Column(CodingKeys.stockStatusKey) } - public static let images = hasMany(PersistedProductImage.self) - public static let attributes = hasMany(PersistedProductAttribute.self) + public static let images = hasMany(PersistedProductImage.self, + using: ForeignKey([PersistedProductImage.CodingKeys.siteID.stringValue, + PersistedProductImage.CodingKeys.productID.stringValue], + to: primaryKey)) + public static let attributes = hasMany(PersistedProductAttribute.self, + using: ForeignKey([PersistedProductAttribute.CodingKeys.siteID.stringValue, + PersistedProductAttribute.CodingKeys.productID.stringValue], + to: primaryKey)) } // periphery:ignore - TODO: remove ignore when populating database diff --git a/Modules/Sources/Storage/GRDB/Model/PersistedProductAttribute.swift b/Modules/Sources/Storage/GRDB/Model/PersistedProductAttribute.swift index fc85b5eab34..3b52f7f97d1 100644 --- a/Modules/Sources/Storage/GRDB/Model/PersistedProductAttribute.swift +++ b/Modules/Sources/Storage/GRDB/Model/PersistedProductAttribute.swift @@ -4,6 +4,7 @@ import GRDB // periphery:ignore - TODO: remove ignore when populating database public struct PersistedProductAttribute: Codable { public private(set) var id: Int64? + public let siteID: Int64 public let productID: Int64 public let name: String public let position: Int64 @@ -12,6 +13,7 @@ public struct PersistedProductAttribute: Codable { public let options: [String] public init(id: Int64? = nil, + siteID: Int64, productID: Int64, name: String, position: Int64, @@ -19,6 +21,7 @@ public struct PersistedProductAttribute: Codable { variation: Bool, options: [String]) { self.id = id + self.siteID = siteID self.productID = productID self.name = name self.position = position @@ -32,26 +35,32 @@ public struct PersistedProductAttribute: Codable { extension PersistedProductAttribute: FetchableRecord, MutablePersistableRecord { public static var databaseTableName: String { "productAttribute" } + public static var primaryKey: [String] { [CodingKeys.id.stringValue] } + public enum Columns { - static let id = Column(CodingKeys.id) + public static let id = Column(CodingKeys.id) + public static let siteID = Column(CodingKeys.siteID) public static let productID = Column(CodingKeys.productID) - static let name = Column(CodingKeys.name) - static let position = Column(CodingKeys.position) - static let visible = Column(CodingKeys.visible) - static let variation = Column(CodingKeys.variation) - static let options = Column(CodingKeys.options) + public static let name = Column(CodingKeys.name) + public static let position = Column(CodingKeys.position) + public static let visible = Column(CodingKeys.visible) + public static let variation = Column(CodingKeys.variation) + public static let options = Column(CodingKeys.options) } public mutating func didInsert(_ inserted: InsertionSuccess) { id = inserted.rowID } + + public static let product = belongsTo(PersistedProduct.self) } // periphery:ignore - TODO: remove ignore when populating database -private extension PersistedProductAttribute { +extension PersistedProductAttribute { enum CodingKeys: String, CodingKey { case id + case siteID case productID case name case position diff --git a/Modules/Sources/Storage/GRDB/Model/PersistedProductImage.swift b/Modules/Sources/Storage/GRDB/Model/PersistedProductImage.swift index 522cd1c4969..2938bf66062 100644 --- a/Modules/Sources/Storage/GRDB/Model/PersistedProductImage.swift +++ b/Modules/Sources/Storage/GRDB/Model/PersistedProductImage.swift @@ -3,6 +3,7 @@ import GRDB // periphery:ignore - TODO: remove ignore when populating database public struct PersistedProductImage: Codable { + public let siteID: Int64 public let id: Int64 public let productID: Int64 public let dateCreated: Date @@ -11,13 +12,15 @@ public struct PersistedProductImage: Codable { public let name: String? public let alt: String? - public init(id: Int64, + public init(siteID: Int64, + id: Int64, productID: Int64, dateCreated: Date, dateModified: Date?, src: String, name: String?, alt: String?) { + self.siteID = siteID self.id = id self.productID = productID self.dateCreated = dateCreated @@ -32,21 +35,25 @@ public struct PersistedProductImage: Codable { extension PersistedProductImage: FetchableRecord, PersistableRecord { public static var databaseTableName: String { "productImage" } + public static var primaryKey: [String] { [CodingKeys.siteID.stringValue, CodingKeys.id.stringValue] } + public enum Columns { - static let id = Column(CodingKeys.id) + public static let siteID = Column(CodingKeys.siteID) + public static let id = Column(CodingKeys.id) public static let productID = Column(CodingKeys.productID) - static let dateCreated = Column(CodingKeys.dateCreated) - static let dateModified = Column(CodingKeys.dateModified) - static let src = Column(CodingKeys.src) - static let name = Column(CodingKeys.name) - static let alt = Column(CodingKeys.alt) + public static let dateCreated = Column(CodingKeys.dateCreated) + public static let dateModified = Column(CodingKeys.dateModified) + public static let src = Column(CodingKeys.src) + public static let name = Column(CodingKeys.name) + public static let alt = Column(CodingKeys.alt) } } // periphery:ignore - TODO: remove ignore when populating database -private extension PersistedProductImage { +extension PersistedProductImage { enum CodingKeys: String, CodingKey { + case siteID case id case productID case dateCreated diff --git a/Modules/Sources/Storage/GRDB/Model/PersistedProductVariation.swift b/Modules/Sources/Storage/GRDB/Model/PersistedProductVariation.swift index 495fed18e16..b37be1909d9 100644 --- a/Modules/Sources/Storage/GRDB/Model/PersistedProductVariation.swift +++ b/Modules/Sources/Storage/GRDB/Model/PersistedProductVariation.swift @@ -44,22 +44,32 @@ public struct PersistedProductVariation: Codable { extension PersistedProductVariation: FetchableRecord, PersistableRecord { public static var databaseTableName: String { "productVariation" } + public static var primaryKey: [String] { [CodingKeys.siteID.stringValue, CodingKeys.id.stringValue] } + public enum Columns { - static let id = Column(CodingKeys.id) - static let siteID = Column(CodingKeys.siteID) - static let productID = Column(CodingKeys.productID) - static let sku = Column(CodingKeys.sku) - static let globalUniqueID = Column(CodingKeys.globalUniqueID) - static let price = Column(CodingKeys.price) - static let downloadable = Column(CodingKeys.downloadable) - static let fullDescription = Column(CodingKeys.fullDescription) - static let manageStock = Column(CodingKeys.manageStock) - static let stockQuantity = Column(CodingKeys.stockQuantity) - static let stockStatusKey = Column(CodingKeys.stockStatusKey) + public static let id = Column(CodingKeys.id) + public static let siteID = Column(CodingKeys.siteID) + public static let productID = Column(CodingKeys.productID) + public static let sku = Column(CodingKeys.sku) + public static let globalUniqueID = Column(CodingKeys.globalUniqueID) + public static let price = Column(CodingKeys.price) + public static let downloadable = Column(CodingKeys.downloadable) + public static let fullDescription = Column(CodingKeys.fullDescription) + public static let manageStock = Column(CodingKeys.manageStock) + public static let stockQuantity = Column(CodingKeys.stockQuantity) + public static let stockStatusKey = Column(CodingKeys.stockStatusKey) } - public static let attributes = hasMany(PersistedProductVariationAttribute.self).forKey("attributes") - public static let image = hasOne(PersistedProductVariationImage.self).forKey("image") + public static let attributes = hasMany(PersistedProductVariationAttribute.self, + key: "attributes", + using: ForeignKey([PersistedProductVariationAttribute.CodingKeys.siteID.stringValue, + PersistedProductVariationAttribute.CodingKeys.productVariationID.stringValue], + to: primaryKey)) + public static let image = hasOne(PersistedProductVariationImage.self, + key: "image", + using: ForeignKey([PersistedProductVariationImage.CodingKeys.siteID.stringValue, + PersistedProductVariationImage.CodingKeys.productVariationID.stringValue], + to: primaryKey)) } diff --git a/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationAttribute.swift b/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationAttribute.swift index 9fbf496a7ae..09d50fd2b7e 100644 --- a/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationAttribute.swift +++ b/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationAttribute.swift @@ -4,15 +4,18 @@ import GRDB // periphery:ignore - TODO: remove ignore when populating database public struct PersistedProductVariationAttribute: Codable { public private(set) var id: Int64? + public let siteID: Int64 public let productVariationID: Int64 public let name: String public let option: String public init(id: Int64? = nil, + siteID: Int64, productVariationID: Int64, name: String, option: String) { self.id = id + self.siteID = siteID self.productVariationID = productVariationID self.name = name self.option = option @@ -24,23 +27,29 @@ public struct PersistedProductVariationAttribute: Codable { extension PersistedProductVariationAttribute: FetchableRecord, MutablePersistableRecord { public static var databaseTableName: String { "productVariationAttribute" } + public static var primaryKey: [String] { [CodingKeys.id.stringValue] } + public enum Columns { - static let id = Column(CodingKeys.id) + public static let id = Column(CodingKeys.id) + public static let siteID = Column(CodingKeys.siteID) public static let productVariationID = Column(CodingKeys.productVariationID) - static let name = Column(CodingKeys.name) - static let option = Column(CodingKeys.option) + public static let name = Column(CodingKeys.name) + public static let option = Column(CodingKeys.option) } public mutating func didInsert(_ inserted: InsertionSuccess) { id = inserted.rowID } + + public static let variation = belongsTo(PersistedProductVariation.self) } // periphery:ignore - TODO: remove ignore when populating database -private extension PersistedProductVariationAttribute { +extension PersistedProductVariationAttribute { enum CodingKeys: String, CodingKey { case id + case siteID case productVariationID case name case option diff --git a/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationImage.swift b/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationImage.swift index d941594b1ee..3bedeb9cb7e 100644 --- a/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationImage.swift +++ b/Modules/Sources/Storage/GRDB/Model/PersistedProductVariationImage.swift @@ -3,6 +3,7 @@ import GRDB // periphery:ignore - TODO: remove ignore when populating database public struct PersistedProductVariationImage: Codable { + public let siteID: Int64 public let id: Int64 public let productVariationID: Int64 public let dateCreated: Date @@ -11,13 +12,15 @@ public struct PersistedProductVariationImage: Codable { public let name: String? public let alt: String? - public init(id: Int64, + public init(siteID: Int64, + id: Int64, productVariationID: Int64, dateCreated: Date, dateModified: Date?, src: String, name: String?, alt: String?) { + self.siteID = siteID self.id = id self.productVariationID = productVariationID self.dateCreated = dateCreated @@ -32,21 +35,25 @@ public struct PersistedProductVariationImage: Codable { extension PersistedProductVariationImage: FetchableRecord, PersistableRecord { public static var databaseTableName: String { "productVariationImage" } + public static var primaryKey: [String] { [CodingKeys.siteID.stringValue, CodingKeys.id.stringValue] } + public enum Columns { - static let id = Column(CodingKeys.id) + public static let siteID = Column(CodingKeys.siteID) + public static let id = Column(CodingKeys.id) public static let productVariationID = Column(CodingKeys.productVariationID) - static let dateCreated = Column(CodingKeys.dateCreated) - static let dateModified = Column(CodingKeys.dateModified) - static let src = Column(CodingKeys.src) - static let name = Column(CodingKeys.name) - static let alt = Column(CodingKeys.alt) + public static let dateCreated = Column(CodingKeys.dateCreated) + public static let dateModified = Column(CodingKeys.dateModified) + public static let src = Column(CodingKeys.src) + public static let name = Column(CodingKeys.name) + public static let alt = Column(CodingKeys.alt) } } // periphery:ignore - TODO: remove ignore when populating database -private extension PersistedProductVariationImage { +extension PersistedProductVariationImage { enum CodingKeys: String, CodingKey { + case siteID case id case productVariationID case dateCreated diff --git a/Modules/Sources/Yosemite/Model/Storage/PersistedProduct+Conversions.swift b/Modules/Sources/Yosemite/Model/Storage/PersistedProduct+Conversions.swift index bda7f7c17d2..d8bc677094e 100644 --- a/Modules/Sources/Yosemite/Model/Storage/PersistedProduct+Conversions.swift +++ b/Modules/Sources/Yosemite/Model/Storage/PersistedProduct+Conversions.swift @@ -69,13 +69,13 @@ extension POSProduct { // Save related images for image in self.images { - let persistedImage = PersistedProductImage(from: image, productID: self.productID) + let persistedImage = PersistedProductImage(from: image, siteID: self.siteID, productID: self.productID) try persistedImage.insert(db) } // Save related attributes for attribute in self.attributes { - var persistedAttribute = PersistedProductAttribute(from: attribute, productID: self.productID) + var persistedAttribute = PersistedProductAttribute(from: attribute, siteID: self.siteID, productID: self.productID) try persistedAttribute.insert(db) } } @@ -85,8 +85,9 @@ extension POSProduct { // MARK: - PersistedProductAttribute Conversions // periphery:ignore - TODO: remove ignore when populating database extension PersistedProductAttribute { - init(from productAttribute: ProductAttribute, productID: Int64) { + init(from productAttribute: ProductAttribute, siteID: Int64, productID: Int64) { self.init( + siteID: siteID, productID: productID, name: productAttribute.name, position: Int64(productAttribute.position), @@ -112,8 +113,9 @@ extension PersistedProductAttribute { // MARK: - PersistedProductImage Conversions // periphery:ignore - TODO: remove ignore when populating database extension PersistedProductImage { - init(from productImage: ProductImage, productID: Int64) { + init(from productImage: ProductImage, siteID: Int64, productID: Int64) { self.init( + siteID: siteID, id: productImage.imageID, productID: productID, dateCreated: productImage.dateCreated, diff --git a/Modules/Sources/Yosemite/Model/Storage/PersistedProductVariation+Conversions.swift b/Modules/Sources/Yosemite/Model/Storage/PersistedProductVariation+Conversions.swift index 8dfb451a348..739ab87eb6c 100644 --- a/Modules/Sources/Yosemite/Model/Storage/PersistedProductVariation+Conversions.swift +++ b/Modules/Sources/Yosemite/Model/Storage/PersistedProductVariation+Conversions.swift @@ -63,13 +63,13 @@ extension POSProductVariation { // Save related image if present if let image = self.image { - let persistedImage = PersistedProductVariationImage(from: image, productVariationID: self.productVariationID) + let persistedImage = PersistedProductVariationImage(from: image, siteID: self.siteID, productVariationID: self.productVariationID) try persistedImage.insert(db) } // Save related attributes for attribute in self.attributes { - var persistedAttribute = PersistedProductVariationAttribute(from: attribute, productVariationID: self.productVariationID) + var persistedAttribute = PersistedProductVariationAttribute(from: attribute, siteID: self.siteID, productVariationID: self.productVariationID) try persistedAttribute.insert(db) } } @@ -79,8 +79,9 @@ extension POSProductVariation { // MARK: - PersistedProductVariationAttribute Conversions // periphery:ignore - TODO: remove ignore when populating database extension PersistedProductVariationAttribute { - init(from productVariationAttribute: ProductVariationAttribute, productVariationID: Int64) { + init(from productVariationAttribute: ProductVariationAttribute, siteID: Int64, productVariationID: Int64) { self.init( + siteID: siteID, productVariationID: productVariationID, name: productVariationAttribute.name, option: productVariationAttribute.option @@ -99,8 +100,9 @@ extension PersistedProductVariationAttribute { // MARK: - PersistedProductVariationImage Conversions // periphery:ignore - TODO: remove ignore when populating database extension PersistedProductVariationImage { - public init(from productImage: ProductImage, productVariationID: Int64) { + public init(from productImage: ProductImage, siteID: Int64, productVariationID: Int64) { self.init( + siteID: siteID, id: productImage.imageID, productVariationID: productVariationID, dateCreated: productImage.dateCreated, diff --git a/Modules/Sources/Yosemite/Tools/POS/POSCatalogPersistenceService.swift b/Modules/Sources/Yosemite/Tools/POS/POSCatalogPersistenceService.swift index ad1ee334a58..716feb6d5d4 100644 --- a/Modules/Sources/Yosemite/Tools/POS/POSCatalogPersistenceService.swift +++ b/Modules/Sources/Yosemite/Tools/POS/POSCatalogPersistenceService.swift @@ -29,8 +29,7 @@ final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol { try await grdbManager.databaseConnection.write { db in DDLogInfo("🗑️ Clearing catalog data for site \(siteID)") - // currently, we can't save for more than one site as entity IDs are not namespaced. - try PersistedSite.deleteAll(db) + try PersistedSite.deleteOne(db, key: siteID) let site = PersistedSite(id: siteID, lastCatalogFullSyncDate: catalog.syncDate) try site.insert(db) @@ -84,11 +83,11 @@ final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol { try product.insert(db, onConflict: .replace) try PersistedProductImage - .filter { $0.productID == product.id } + .filter { $0.siteID == siteID && $0.productID == product.id } .deleteAll(db) try PersistedProductAttribute - .filter { $0.productID == product.id } + .filter { $0.siteID == siteID && $0.productID == product.id } .deleteAll(db) } @@ -104,11 +103,11 @@ final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol { try variation.insert(db, onConflict: .replace) try PersistedProductVariationImage - .filter { $0.productVariationID == variation.id } + .filter { $0.siteID == siteID && $0.productVariationID == variation.id } .deleteAll(db) try PersistedProductVariationAttribute - .filter { $0.productVariationID == variation.id } + .filter { $0.siteID == siteID && $0.productVariationID == variation.id } .deleteAll(db) } @@ -148,13 +147,17 @@ private extension POSCatalog { var productImagesToPersist: [PersistedProductImage] { products.flatMap { product in - product.images.map { PersistedProductImage(from: $0, productID: product.productID) } + product.images.map { PersistedProductImage(from: $0, + siteID: product.siteID, + productID: product.productID) } } } var productAttributesToPersist: [PersistedProductAttribute] { products.flatMap { product in - product.attributes.map { PersistedProductAttribute(from: $0, productID: product.productID) } + product.attributes.map { PersistedProductAttribute(from: $0, + siteID: product.siteID, + productID: product.productID) } } } @@ -164,13 +167,17 @@ private extension POSCatalog { var variationImagesToPersist: [PersistedProductVariationImage] { variations.compactMap { variation in - variation.image.map { PersistedProductVariationImage(from: $0, productVariationID: variation.productVariationID) } + variation.image.map { PersistedProductVariationImage(from: $0, + siteID: variation.siteID, + productVariationID: variation.productVariationID) } } } var variationAttributesToPersist: [PersistedProductVariationAttribute] { variations.flatMap { variation in - variation.attributes.map { PersistedProductVariationAttribute(from: $0, productVariationID: variation.productVariationID) } + variation.attributes.map { PersistedProductVariationAttribute(from: $0, + siteID: variation.siteID, + productVariationID: variation.productVariationID) } } } } diff --git a/Modules/Tests/StorageTests/GRDB/GRDBManagerTests.swift b/Modules/Tests/StorageTests/GRDB/GRDBManagerTests.swift index 4a65a0ae150..d156ef5b7ab 100644 --- a/Modules/Tests/StorageTests/GRDB/GRDBManagerTests.swift +++ b/Modules/Tests/StorageTests/GRDB/GRDBManagerTests.swift @@ -191,6 +191,7 @@ struct GRDBManagerTests { // When try manager.databaseConnection.write { db in let attribute = TestProductAttribute( + siteID: 1, productID: 100, name: "Color", position: 0, @@ -244,6 +245,7 @@ struct GRDBManagerTests { // When try manager.databaseConnection.write { db in let variationAttribute = TestProductVariationAttribute( + siteID: 1, productVariationID: 200, name: "Color", option: "Red" @@ -296,6 +298,7 @@ struct GRDBManagerTests { try variation.insert(db) let productAttribute = TestProductAttribute( + siteID: testSiteId, productID: 100, name: "Color", position: 0, @@ -306,6 +309,7 @@ struct GRDBManagerTests { try productAttribute.insert(db) let variationAttribute = TestProductVariationAttribute( + siteID: testSiteId, productVariationID: 200, name: "Size", option: "Large" @@ -318,8 +322,10 @@ struct GRDBManagerTests { return ( products: try TestProduct.filter(Column("siteID") == testSiteId).fetchCount(db), variations: try TestProductVariation.filter(Column("siteID") == testSiteId).fetchCount(db), - productAttributes: try TestProductAttribute.filter(Column("productID") == 100).fetchCount(db), - variationAttributes: try TestProductVariationAttribute.filter(Column("productVariationID") == 200).fetchCount(db) + productAttributes: try TestProductAttribute + .filter(Column("siteID") == testSiteId && Column("productID") == 100).fetchCount(db), + variationAttributes: try TestProductVariationAttribute + .filter(Column("siteID") == testSiteId && Column("productVariationID") == 200).fetchCount(db) ) } @@ -339,8 +345,10 @@ struct GRDBManagerTests { sites: try TestSite.filter(Column("id") == testSiteId).fetchCount(db), products: try TestProduct.filter(Column("siteID") == testSiteId).fetchCount(db), variations: try TestProductVariation.filter(Column("siteID") == testSiteId).fetchCount(db), - productAttributes: try TestProductAttribute.filter(Column("productID") == 100).fetchCount(db), - variationAttributes: try TestProductVariationAttribute.filter(Column("productVariationID") == 200).fetchCount(db) + productAttributes: try TestProductAttribute + .filter(Column("siteID") == testSiteId && Column("productID") == 100).fetchCount(db), + variationAttributes: try TestProductVariationAttribute + .filter(Column("siteID") == testSiteId && Column("productVariationID") == 200).fetchCount(db) ) } @@ -553,6 +561,7 @@ struct GRDBManagerTests { // Insert product attributes let productAttribute = TestProductAttribute( + siteID: siteID, productID: product.id, name: "Color \(i)", position: i, @@ -564,6 +573,7 @@ struct GRDBManagerTests { // Insert variation attributes let variationAttribute = TestProductVariationAttribute( + siteID: siteID, productVariationID: variation.id, name: "Size \(i)", option: "Large" @@ -732,6 +742,7 @@ extension TestProductVariation: FetchableRecord, PersistableRecord { } struct TestProductAttribute: Codable { + let siteID: Int64 let productID: Int64 let name: String let position: Int @@ -745,6 +756,7 @@ extension TestProductAttribute: FetchableRecord, PersistableRecord { } struct TestProductVariationAttribute: Codable { + let siteID: Int64 let productVariationID: Int64 let name: String let option: String diff --git a/Modules/Tests/YosemiteTests/Storage/PersistedProductTests.swift b/Modules/Tests/YosemiteTests/Storage/PersistedProductTests.swift index 1959e38f79c..7e822fad176 100644 --- a/Modules/Tests/YosemiteTests/Storage/PersistedProductTests.swift +++ b/Modules/Tests/YosemiteTests/Storage/PersistedProductTests.swift @@ -73,14 +73,16 @@ struct PersistedProductTests { ) let productImages = [ - PersistedProductImage(id: 200, + PersistedProductImage(siteID: siteID, + id: 200, productID: productID, dateCreated: Date(timeIntervalSince1970: 10), dateModified: nil, src: "https://example.com/p1.png", name: "p1", alt: "a1"), - PersistedProductImage(id: 201, + PersistedProductImage(siteID: siteID, + id: 201, productID: productID, dateCreated: Date(timeIntervalSince1970: 11), dateModified: Date(timeIntervalSince1970: 12), @@ -90,8 +92,8 @@ struct PersistedProductTests { ] let persistedAttributes = [ - PersistedProductAttribute(productID: productID, name: "Material", position: 0, visible: true, variation: false, options: ["Cotton"]), - PersistedProductAttribute(productID: productID, name: "Fit", position: 1, visible: true, variation: true, options: ["Slim", "Regular"]) + PersistedProductAttribute(siteID: siteID, productID: productID, name: "Material", position: 0, visible: true, variation: false, options: ["Cotton"]), + PersistedProductAttribute(siteID: siteID, productID: productID, name: "Fit", position: 1, visible: true, variation: true, options: ["Slim", "Regular"]) ] // When @@ -151,6 +153,7 @@ struct PersistedProductTests { try product.insert(db) let image1 = PersistedProductImage( + siteID: 1, id: 200, productID: 100, dateCreated: Date(timeIntervalSince1970: 1000), @@ -160,6 +163,7 @@ struct PersistedProductTests { alt: "Alt text 1" ) let image2 = PersistedProductImage( + siteID: 1, id: 201, productID: 100, dateCreated: Date(timeIntervalSince1970: 2000), @@ -172,6 +176,7 @@ struct PersistedProductTests { try image2.insert(db) var attribute1 = PersistedProductAttribute( + siteID: 1, productID: 100, name: "Color", position: 0, @@ -180,6 +185,7 @@ struct PersistedProductTests { options: ["Red", "Blue", "Green"] ) var attribute2 = PersistedProductAttribute( + siteID: 1, productID: 100, name: "Size", position: 1, @@ -287,7 +293,7 @@ struct PersistedProductTests { options: ["Vanilla", "Chocolate"]) // When - let persisted = PersistedProductAttribute(from: attribute, productID: productID) + let persisted = PersistedProductAttribute(from: attribute, siteID: siteID, productID: productID) let back = persisted.toProductAttribute(siteID: siteID) // Then @@ -318,7 +324,8 @@ struct PersistedProductTests { alt: "y") // When - let persisted = PersistedProductImage(from: image, productID: productID) + let siteID: Int64 = 4 + let persisted = PersistedProductImage(from: image, siteID: siteID, productID: productID) let back = persisted.toProductImage() // Then @@ -379,7 +386,7 @@ struct PersistedProductTests { try posProduct.save(to: db) let fetchedProduct = try db.read { db in - try PersistedProduct.filter(PersistedProduct.Columns.id == 123).fetchOne(db) + try PersistedProduct.filter { $0.siteID == 1 && $0.id == 123 }.fetchOne(db) } let product = try #require(fetchedProduct) let loadedPOSProduct = try product.toPOSProduct(db: db) diff --git a/Modules/Tests/YosemiteTests/Storage/PersistedProductVariationTests.swift b/Modules/Tests/YosemiteTests/Storage/PersistedProductVariationTests.swift index 7c6910b7da4..188370af5dc 100644 --- a/Modules/Tests/YosemiteTests/Storage/PersistedProductVariationTests.swift +++ b/Modules/Tests/YosemiteTests/Storage/PersistedProductVariationTests.swift @@ -76,10 +76,11 @@ struct PersistedProductVariationTests { ) let varAttrs = [ - PersistedProductVariationAttribute(productVariationID: variationID, name: "Material", option: "Wool"), - PersistedProductVariationAttribute(productVariationID: variationID, name: "Fit", option: "Slim") + PersistedProductVariationAttribute(siteID: siteID, productVariationID: variationID, name: "Material", option: "Wool"), + PersistedProductVariationAttribute(siteID: siteID, productVariationID: variationID, name: "Fit", option: "Slim") ] let varImage = PersistedProductVariationImage( + siteID: siteID, id: 601, productVariationID: variationID, dateCreated: Date(timeIntervalSince1970: 2000), @@ -156,6 +157,7 @@ struct PersistedProductVariationTests { try variation.insert(db) let variationImage = PersistedProductVariationImage( + siteID: 2, id: 600, productVariationID: 500, dateCreated: Date(timeIntervalSince1970: 3000), @@ -167,11 +169,13 @@ struct PersistedProductVariationTests { try variationImage.insert(db) var attr1 = PersistedProductVariationAttribute( + siteID: 2, productVariationID: 500, name: "Color", option: "Purple" ) var attr2 = PersistedProductVariationAttribute( + siteID: 2, productVariationID: 500, name: "Material", option: "Cotton" @@ -276,6 +280,7 @@ struct PersistedProductVariationTests { // Add attributes only, no image var attr = PersistedProductVariationAttribute( + siteID: 4, productVariationID: 700, name: "Style", option: "Modern" @@ -306,7 +311,8 @@ struct PersistedProductVariationTests { let attr = ProductVariationAttribute(id: 0, name: "Style", option: "Modern") // When - let persisted = PersistedProductVariationAttribute(from: attr, productVariationID: variationID) + let siteID: Int64 = 7 + let persisted = PersistedProductVariationAttribute(from: attr, siteID: siteID, productVariationID: variationID) let back = persisted.toProductVariationAttribute() // Then @@ -330,7 +336,8 @@ struct PersistedProductVariationTests { alt: nil) // When - let persisted = PersistedProductVariationImage(from: image, productVariationID: variationID) + let siteID: Int64 = 8 + let persisted = PersistedProductVariationImage(from: image, siteID: siteID, productVariationID: variationID) let back = persisted.toProductImage() // Then