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
24 changes: 24 additions & 0 deletions Modules/Sources/Yosemite/PointOfSale/POSReceiptInformation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

public struct POSReceiptInformation: Equatable {
public let storeName: String?
public let storeAddress: String?
public let phone: String?
public let email: String?
public let refundReturnsPolicy: String?

public init(storeName: String?, storeAddress: String?, phone: String?, email: String?, refundReturnsPolicy: String?) {
self.storeName = storeName
self.storeAddress = storeAddress
self.phone = phone
self.email = email
self.refundReturnsPolicy = refundReturnsPolicy
}

public static let empty = POSReceiptInformation(
storeName: nil,
storeAddress: nil,
phone: nil,
email: nil,
refundReturnsPolicy: nil
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import Networking
import Storage

public protocol PointOfSaleSettingsServiceProtocol {
var siteID: Int64 { get }
func retrievePointOfSaleSettings() async throws -> [SiteSetting]
func retrievePointOfSaleSettings() async throws -> POSReceiptInformation
}

public final class PointOfSaleSettingsService: PointOfSaleSettingsServiceProtocol {
Expand All @@ -25,7 +24,19 @@ public final class PointOfSaleSettingsService: PointOfSaleSettingsServiceProtoco
network: network))
}

public func retrievePointOfSaleSettings() async throws -> [SiteSetting] {
return try await settingStoreMethods.retrievePointOfSaleSettings(siteID: siteID)
public func retrievePointOfSaleSettings() async throws -> POSReceiptInformation {
let siteSettings = try await settingStoreMethods.retrievePointOfSaleSettings(siteID: siteID)
return POSReceiptInformation(
storeName: settingValue(from: siteSettings, settingID: "woocommerce_pos_store_name"),
storeAddress: settingValue(from: siteSettings, settingID: "woocommerce_pos_store_address"),
phone: settingValue(from: siteSettings, settingID: "woocommerce_pos_store_phone"),
email: settingValue(from: siteSettings, settingID: "woocommerce_pos_store_email"),
refundReturnsPolicy: settingValue(from: siteSettings, settingID: "woocommerce_pos_refund_returns_policy")
)
}

private func settingValue(from siteSettings: [SiteSetting], settingID: String) -> String? {
let value = siteSettings.first { $0.settingID == settingID }?.value
return value?.isEmpty == true ? nil : value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ struct PointOfSaleSettingsServiceTests {
settingStoreMethods: settingStoreMethods)
}

@Test func retrievePointOfSaleSettings_when_empty_settings_then_returns_empty_array() async throws {
@Test func retrievePointOfSaleSettings_when_empty_settings_then_returns_empty_receipt_info() async throws {
// Given
settingStoreMethods.retrievePointOfSaleSettingsResult = .success([])

// When
let settings = try await sut.retrievePointOfSaleSettings()
let receiptInfo = try await sut.retrievePointOfSaleSettings()

// Then
#expect(settingStoreMethods.retrievePointOfSaleSettingsCalled)
#expect(settingStoreMethods.retrievePointOfSaleSettingsSiteID == sampleSiteID)
#expect(settings.isEmpty)
#expect(receiptInfo == POSReceiptInformation.empty)
}

@Test func retrievePointOfSaleSettings_when_network_error_then_throws_error() async throws {
Expand Down Expand Up @@ -64,34 +64,23 @@ struct PointOfSaleSettingsServiceTests {
}
}

@Test func retrievePointOfSaleSettings_with_expected_pos_settings_then_returns_all_settings() async throws {
@Test func retrievePointOfSaleSettings_with_expected_pos_settings_then_returns_mapped_receipt_info() async throws {
// Given
let expectedSettings = makeSiteSettings()
settingStoreMethods.retrievePointOfSaleSettingsResult = .success(expectedSettings)

// When
let settings = try await sut.retrievePointOfSaleSettings()
let receiptInfo = try await sut.retrievePointOfSaleSettings()

// Then
#expect(settingStoreMethods.retrievePointOfSaleSettingsCalled)
#expect(settingStoreMethods.retrievePointOfSaleSettingsSiteID == sampleSiteID)
#expect(settings.count == 5)
#expect(settings == expectedSettings)

let storeNameSetting = settings.first { $0.settingID == "woocommerce_pos_store_name" }
#expect(storeNameSetting?.value == "WooCommerce Store")

let addressSetting = settings.first { $0.settingID == "woocommerce_pos_store_address" }
#expect(addressSetting?.value == "123 Commerce Street\nBusiness District")

let phoneSetting = settings.first { $0.settingID == "woocommerce_pos_store_phone" }
#expect(phoneSetting?.value == "+1 (555) 123-4567")

let emailSetting = settings.first { $0.settingID == "woocommerce_pos_store_email" }
#expect(emailSetting?.value == "[email protected]")

let policySetting = settings.first { $0.settingID == "woocommerce_pos_refund_returns_policy" }
#expect(policySetting?.value == "30-day return policy with receipt")
#expect(receiptInfo.storeName == "WooCommerce Store")
#expect(receiptInfo.storeAddress == "123 Commerce Street\nBusiness District")
#expect(receiptInfo.phone == "+1 (555) 123-4567")
#expect(receiptInfo.email == "[email protected]")
#expect(receiptInfo.refundReturnsPolicy == "30-day return policy with receipt")
}

private func makeSiteSettings() -> [SiteSetting] {
Expand Down
168 changes: 50 additions & 118 deletions WooCommerce/Classes/POS/Models/PointOfSaleSettingsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,34 @@ import Foundation
import Combine
import struct Yosemite.SiteSetting
import enum Yosemite.Plugin
import class Yosemite.PluginsService
import Observation

import struct Yosemite.SystemPlugin
import protocol Yosemite.PluginsServiceProtocol
import protocol Yosemite.PointOfSaleSettingsServiceProtocol
import class Yosemite.PointOfSaleSettingsService
import Storage
import struct Yosemite.POSReceiptInformation
import Observation

protocol PointOfSaleSettingsControllerProtocol {
var receiptStoreName: String? { get }
var receiptStoreAddress: String? { get }
var receiptStorePhone: String? { get }
var receiptStoreEmail: String? { get }
var receiptRefundReturnsPolicy: String? { get }
var isLoading: Bool { get }
var shouldShowReceiptInformation: Bool { get }
var storeName: String { get }
var storeAddress: String { get }

var connectedCardReader: CardPresentPaymentCardReader? { get }

func retrievePOSReceiptSettings() async
var storeViewModel: POSSettingsStoreViewModel { get }
}

@Observable final class PointOfSaleSettingsController: PointOfSaleSettingsControllerProtocol {
private(set) var receiptStoreName: String?
private(set) var receiptStoreAddress: String?
private(set) var receiptStorePhone: String?
private(set) var receiptStoreEmail: String?
private(set) var receiptRefundReturnsPolicy: String?
private(set) var isLoading: Bool = false
private(set) var shouldShowReceiptInformation: Bool = false

private let defaultSiteName: String?
private let settingsService: PointOfSaleSettingsServiceProtocol
private let siteSettings: [SiteSetting]
private(set) var connectedCardReader: CardPresentPaymentCardReader?
private var cancellables: AnyCancellable?

init(settingsService: PointOfSaleSettingsServiceProtocol,
let storeViewModel: POSSettingsStoreViewModel

init(siteID: Int64,
settingsService: PointOfSaleSettingsServiceProtocol,
cardPresentPaymentService: CardPresentPaymentFacade,
pluginsService: PluginsServiceProtocol,
defaultSiteName: String? = ServiceLocator.stores.sessionManager.defaultSite?.name,
siteSettings: [SiteSetting] = ServiceLocator.selectedSiteSettings.siteSettings) {
self.settingsService = settingsService
self.defaultSiteName = defaultSiteName
self.siteSettings = siteSettings
self.storeViewModel = POSSettingsStoreViewModel(siteID: siteID,
settingsService: settingsService,
pluginsService: pluginsService,
defaultSiteName: defaultSiteName,
siteSettings: siteSettings)

observeCardReader(from: cardPresentPaymentService)
}
Expand All @@ -65,104 +48,53 @@ protocol PointOfSaleSettingsControllerProtocol {
connectedCardReader = cardReader
})
}

var storeName: String {
if let defaultSiteName {
return defaultSiteName
} else {
return Localization.storeNameNotSet
}
}

var storeAddress: String {
SiteAddress(siteSettings: siteSettings).address
}

@MainActor
func retrievePOSReceiptSettings() async {
isLoading = true

shouldShowReceiptInformation = await isPluginSupported(.wooCommerce, minimumVersion: Constants.minimumWooCommerceVersion)

guard shouldShowReceiptInformation else {
isLoading = false
return
}

do {
let siteSettings = try await settingsService.retrievePointOfSaleSettings()
updateReceiptSettings(from: siteSettings)
} catch {
DDLogError("Failed to load POS settings: \(error)")
}
isLoading = false
}

@MainActor
private func isPluginSupported(_ plugin: Plugin,
storageManager: StorageManagerType = ServiceLocator.storageManager,
minimumVersion: String) async -> Bool {
let pluginsService = PluginsService(storageManager: storageManager)
guard let systemPlugin = pluginsService.loadPluginInStorage(siteID: settingsService.siteID, plugin: plugin, isActive: true), systemPlugin.active else {
return false
}

let isSupported = VersionHelpers.isVersionSupported(version: systemPlugin.version,
minimumRequired: minimumVersion)
return isSupported
}

private func updateReceiptSettings(from siteSettings: [SiteSetting]) {
receiptStoreName = settingValue(from: siteSettings, settingID: "woocommerce_pos_store_name")
receiptStoreAddress = settingValue(from: siteSettings, settingID: "woocommerce_pos_store_address")
receiptStorePhone = settingValue(from: siteSettings, settingID: "woocommerce_pos_store_phone")
receiptStoreEmail = settingValue(from: siteSettings, settingID: "woocommerce_pos_store_email")
receiptRefundReturnsPolicy = settingValue(from: siteSettings, settingID: "woocommerce_pos_refund_returns_policy")
}

private func settingValue(from siteSettings: [SiteSetting], settingID: String) -> String? {
let value = siteSettings.first { $0.settingID == settingID }?.value
return value?.isEmpty == true ? nil : value
}
}

private extension PointOfSaleSettingsController {
enum Constants {
static let minimumWooCommerceVersion: String = "10.0"
}

enum Localization {
static let storeNameNotSet = NSLocalizedString(
"pointOfSaleSettingsService.storeNameNotSet",
value: "Not set",
comment: "Text displayed on Point of Sale settings when store has not been provided."
)
}
}

#if DEBUG
final class PointOfSaleSettingsPreviewController: PointOfSaleSettingsControllerProtocol {
var receiptStoreName: String? = "Sample Store"
var receiptStoreAddress: String? = "123 Main Street\nAnytown, ST 12345"
var receiptStorePhone: String? = "+1 (555) 123-4567"
var receiptStoreEmail: String? = "[email protected]"
var receiptRefundReturnsPolicy: String? = "30-day return policy"
var isLoading: Bool = false
var shouldShowReceiptInformation: Bool = true
var storeName: String = "Sample Store"

var connectedCardReader: CardPresentPaymentCardReader? = CardPresentPaymentCardReader(
name: "WisePad 3",
batteryLevel: 0.75
)

var storeAddress: String {
"123 Main Street\nAnytown, ST 12345"
var storeViewModel: POSSettingsStoreViewModel = POSSettingsStoreViewModel(siteID: 123,
settingsService: MockPointOfSaleSettingsService(),
pluginsService: PluginsServicePreview(),
defaultSiteName: "Sample Store",
siteSettings: [])
}

final class MockPointOfSaleSettingsService: PointOfSaleSettingsServiceProtocol {
func retrievePointOfSaleSettings() async throws -> POSReceiptInformation {
return .empty
}
}

func retrievePOSReceiptSettings() async {
// no-op
final class PluginsServicePreview: PluginsServiceProtocol {
func waitForPluginInStorage(siteID: Int64, pluginPath: String, isActive: Bool) async -> SystemPlugin {
return SystemPlugin(siteID: 1234,
plugin: "",
name: "",
version: "",
versionLatest: "",
url: "",
authorName: "",
authorUrl: "",
networkActivated: false,
active: true)
}

func loadPluginInStorage(siteID: Int64, plugin: Plugin, isActive: Bool?) -> SystemPlugin? {
return SystemPlugin(siteID: 1234,
plugin: "",
name: "",
version: "",
versionLatest: "",
url: "",
authorName: "",
authorUrl: "",
networkActivated: false,
active: true)
}
}
#endif
Loading