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 @@ -715,8 +715,17 @@ private extension PointOfSaleAggregateModel {

private func performIncrementalSync() {
guard let catalogSyncCoordinator else { return }
let popularPurchasableItemsController = popularPurchasableItemsController
let siteID = siteID
Task {
try? await catalogSyncCoordinator.performIncrementalSync(for: siteID)
await withTaskGroup(of: Void.self) { group in
group.addTask {
try? await catalogSyncCoordinator.performIncrementalSync(for: siteID)
}
group.addTask {
await popularPurchasableItemsController.refreshItems(base: .root)
}
}
}
}

Expand Down
9 changes: 8 additions & 1 deletion Modules/Sources/PointOfSale/Presentation/ItemListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,14 @@ struct ItemListView: View {
)
.refreshable {
analyticsTracker.trackRefresh()
await itemsController(itemListType).refreshItems(base: .root)
await withTaskGroup(of: Void.self) { group in
group.addTask {
await itemsController(itemListType).refreshItems(base: .root)
}
group.addTask {
await posModel.popularPurchasableItemsController.refreshItems(base: .root)
}
}
}
}
.task {
Expand Down
32 changes: 18 additions & 14 deletions Modules/Sources/Storage/GRDB/Model/PersistedProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,8 @@ public extension PersistedProduct {
/// Returns a request for POS-supported products (simple and variable, non-downloadable) for a given site, ordered by name
/// Filters out products with trash, draft, pending, or private status to ensure only published and 3rd party custom status products are shown
static func posProductsRequest(siteID: Int64) -> QueryInterfaceRequest<PersistedProduct> {
let excludedStatuses = [
"trash",
"draft",
"pending"
]

return PersistedProduct
.filter(Columns.siteID == siteID)
.filter([ProductType.simple.rawValue, ProductType.variable.rawValue].contains(Columns.productTypeKey))
.filter(Columns.downloadable == false)
.filter(!excludedStatuses.contains(Columns.statusKey))
PersistedProduct
.baseQuery(siteID: siteID)
.order(Columns.name.collating(.localizedCaseInsensitiveCompare))
}

Expand All @@ -124,6 +115,7 @@ public extension PersistedProduct {
/// - siteID: The site ID
/// - globalUniqueID: The global unique ID (barcode) to search for
/// - Returns: A query request that matches products with the given global unique ID
/// Note that this may return unsupported products, so they can be shown as errors in the UI
static func posProductByGlobalUniqueID(siteID: Int64, globalUniqueID: String) -> QueryInterfaceRequest<PersistedProduct> {
return PersistedProduct
.filter(Columns.siteID == siteID)
Expand All @@ -140,9 +132,7 @@ public extension PersistedProduct {
let likePattern = "%\(escapedTerm)%"

return PersistedProduct
.filter(Columns.siteID == siteID)
.filter([ProductType.simple.rawValue, ProductType.variable.rawValue].contains(Columns.productTypeKey))
.filter(Columns.downloadable == false)
.baseQuery(siteID: siteID)
.filter(
Columns.name.like(likePattern, escape: "\\") ||
Columns.sku.like(likePattern, escape: "\\") ||
Expand All @@ -160,6 +150,20 @@ public extension PersistedProduct {
.replacingOccurrences(of: "%", with: "\\%")
.replacingOccurrences(of: "_", with: "\\_")
}

private static func baseQuery(siteID: Int64) -> QueryInterfaceRequest<PersistedProduct> {
let excludedStatuses = [
"trash",
"draft",
"pending"
]

return PersistedProduct
.filter(Columns.siteID == siteID)
.filter([ProductType.simple.rawValue, ProductType.variable.rawValue].contains(Columns.productTypeKey))
.filter(Columns.downloadable == false)
.filter(!excludedStatuses.contains(Columns.statusKey))
}
}

// periphery:ignore - TODO: remove ignore when populating database
Expand Down
108 changes: 108 additions & 0 deletions Modules/Tests/StorageTests/GRDB/PersistedProductSearchQueryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,114 @@ struct PersistedProductSearchQueryTests {
#expect(!results.contains(where: { $0.productTypeKey == "grouped" }))
}

@Test("posProductSearch filters out unsupported product statuses")
func test_search_only_returns_pos_supported_product_statuses() async throws {
// Given
let trashedProduct = PersistedProduct(
id: 10,
siteID: siteID,
name: "Search Test Trash",
productTypeKey: "simple",
fullDescription: nil,
shortDescription: nil,
sku: nil,
globalUniqueID: nil,
price: "10.00",
downloadable: false,
parentID: 0,
manageStock: false,
stockQuantity: nil,
stockStatusKey: "instock",
statusKey: "trash"
)
let draftProduct = PersistedProduct(
id: 11,
siteID: siteID,
name: "Search Test Draft",
productTypeKey: "variable",
fullDescription: nil,
shortDescription: nil,
sku: nil,
globalUniqueID: nil,
price: "20.00",
downloadable: false,
parentID: 0,
manageStock: false,
stockQuantity: nil,
stockStatusKey: "instock",
statusKey: "draft"
)
let pendingProduct = PersistedProduct(
id: 12,
siteID: siteID,
name: "Search Test Pending",
productTypeKey: "simple",
fullDescription: nil,
shortDescription: nil,
sku: nil,
globalUniqueID: nil,
price: "0.00",
downloadable: false,
parentID: 0,
manageStock: false,
stockQuantity: nil,
stockStatusKey: "instock",
statusKey: "pending"
)
let publishedProduct = PersistedProduct(
id: 13,
siteID: siteID,
name: "Search Test Published",
productTypeKey: "simple",
fullDescription: nil,
shortDescription: nil,
sku: nil,
globalUniqueID: nil,
price: "0.00",
downloadable: false,
parentID: 0,
manageStock: false,
stockQuantity: nil,
stockStatusKey: "instock",
statusKey: "publish"
)
let privateProduct = PersistedProduct(
id: 14,
siteID: siteID,
name: "Search Test Private",
productTypeKey: "simple",
fullDescription: nil,
shortDescription: nil,
sku: nil,
globalUniqueID: nil,
price: "0.00",
downloadable: false,
parentID: 0,
manageStock: false,
stockQuantity: nil,
stockStatusKey: "instock",
statusKey: "private"
)
try await insertProduct(trashedProduct)
try await insertProduct(draftProduct)
try await insertProduct(pendingProduct)
try await insertProduct(publishedProduct)
try await insertProduct(privateProduct)

// When
let results = try await grdbManager.databaseConnection.read { db in
try PersistedProduct.posProductSearch(siteID: siteID, searchTerm: "Search Test").fetchAll(db)
}

// Then
#expect(results.count == 2)
#expect(results.contains(where: { $0.statusKey == "publish" }))
#expect(results.contains(where: { $0.statusKey == "private" }))
#expect(!results.contains(where: { $0.statusKey == "trash" }))
#expect(!results.contains(where: { $0.statusKey == "draft" }))
#expect(!results.contains(where: { $0.statusKey == "pending" }))
}

// MARK: - Site Isolation Tests

@Test("posProductSearch only returns products from specified site")
Expand Down