Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
059bcd9
make PointOfSaleSettingsService
iamgabrielma Aug 25, 2025
433a5d8
update init
iamgabrielma Aug 25, 2025
4713200
move version check to service
iamgabrielma Aug 25, 2025
af3b76e
make settingValueView
iamgabrielma Aug 25, 2025
4201416
early return if version not supported
iamgabrielma Aug 25, 2025
7b66d8f
cleanup
iamgabrielma Aug 25, 2025
46035e4
update NSLocalizedStrings
iamgabrielma Aug 25, 2025
e49dad7
lint and POS fonts
iamgabrielma Aug 25, 2025
4bb9542
move setting retrieval to the parent view
iamgabrielma Aug 25, 2025
03834df
update imports and localization
iamgabrielma Aug 25, 2025
8a02151
make setting view create its own service
iamgabrielma Aug 25, 2025
fe32ce4
Revert "make setting view create its own service"
iamgabrielma Aug 25, 2025
e19018b
split settings service into controller-service
iamgabrielma Aug 26, 2025
c3c2ca0
extract controller init to posmodel
iamgabrielma Aug 26, 2025
8f91b68
make protocol. DI settings controller into POS aggregate
iamgabrielma Aug 26, 2025
a626d12
make test target compile
iamgabrielma Aug 26, 2025
b102193
DI storage into settings service
iamgabrielma Aug 26, 2025
f3de3be
DI default values for store name and address
iamgabrielma Aug 26, 2025
9bedfa1
add PointOfSaleSettingsServiceTests
iamgabrielma Aug 26, 2025
50c3efe
make PointOfSaleSettingsServiceProtocol and add tests
iamgabrielma Aug 26, 2025
e2e84b2
clean up unused storage
iamgabrielma Aug 26, 2025
72bed3b
restore commented out preview
iamgabrielma Aug 26, 2025
ae533c5
Merge branch 'trunk' into task/WOOMOB-1040-POSSettings-store-section
iamgabrielma Aug 27, 2025
3b82a84
remove unnecessary test
iamgabrielma Aug 27, 2025
61728b2
adjust access control
iamgabrielma Aug 27, 2025
0398758
update var name
iamgabrielma Aug 27, 2025
2e5c028
merge tests
iamgabrielma Aug 27, 2025
d036798
make mock and wrap preview in debug flag
iamgabrielma Aug 27, 2025
b7b3bee
update preview
iamgabrielma Aug 27, 2025
cf78712
remove unnecessary imports
iamgabrielma Aug 27, 2025
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
95 changes: 95 additions & 0 deletions WooCommerce/Classes/POS/Models/PointOfSaleSettingsService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import Foundation
import struct Yosemite.SiteSetting
import enum Yosemite.SettingAction
import enum Yosemite.Plugin
import class Yosemite.PluginsService
import Observation

@Observable final class PointOfSaleSettingsService {
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 var siteID: Int64 {
ServiceLocator.stores.sessionManager.defaultSite?.siteID ?? 0
}

var storeName: String {
guard let site = ServiceLocator.stores.sessionManager.defaultSite else {
return Localization.storeNotSet
}
return site.name
}

var storeAddress: String {
SiteAddress().address
}

@MainActor
func retrievePOSReceiptSettings() async {
isLoading = true

shouldShowReceiptInformation = await isPluginSupported(.wooCommerce, minimumVersion: "10.0")

guard shouldShowReceiptInformation else {
isLoading = false
return
}

await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
let action = SettingAction.retrievePointOfSaleSettings(siteID: siteID) { [weak self] result in
guard let self else { return }
switch result {
case .success(let siteSettings):
updateReceiptSettings(from: siteSettings)
case .failure(let error):
DDLogError("Failed to load POS settings: \(error)")
}
isLoading = false
continuation.resume()
}
ServiceLocator.stores.dispatch(action)
}
}

@MainActor
private func isPluginSupported(_ plugin: Plugin, minimumVersion: String) async -> Bool {
let storageManager = ServiceLocator.storageManager
let pluginsService = PluginsService(storageManager: storageManager)
guard let systemPlugin = pluginsService.loadPluginInStorage(siteID: 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 PointOfSaleSettingsService {
enum Localization {
static let storeNotSet = NSLocalizedString(
"pointOfSaleSettingsService.storeNotSet",
value: "Not set",
comment: "Text displayed on Point of Sale settings when store has not been provided."
)
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,129 @@
import SwiftUI

struct PointOfSaleSettingsStoreDetailView: View {
let posSettingsService: PointOfSaleSettingsService

var body: some View {
NavigationStack {
VStack(alignment: .leading) {
Text("Store Settings")
.font(.title2)
Text("Store-related configuration")
.font(.caption)
.foregroundStyle(.secondary)
Group {
Text(Localization.storeInformation)
.font(.posBodyLargeRegular())

Text(Localization.storeName)
Text(posSettingsService.storeName)
.font(.posBodyMediumRegular())
.foregroundStyle(.secondary)

Text(Localization.address)
Text(posSettingsService.storeAddress)
.font(.posBodyMediumRegular())
.foregroundStyle(.secondary)
}

Group {
Spacer()
Text(Localization.receiptInformation)
.font(.posBodyLargeRegular())
Text(Localization.receiptStoreName)
settingValueView(for: posSettingsService.receiptStoreName)

Text(Localization.physicalAddress)
settingValueView(for: posSettingsService.receiptStoreAddress)

Text(Localization.phoneNumber)
settingValueView(for: posSettingsService.receiptStorePhone)

Text(Localization.email)
settingValueView(for: posSettingsService.receiptStoreEmail)

Text(Localization.refundReturnsPolicy)
settingValueView(for: posSettingsService.receiptRefundReturnsPolicy)

}
.renderedIf(posSettingsService.shouldShowReceiptInformation)
}
.padding()
}
}

@ViewBuilder
private func settingValueView(for value: String?) -> some View {
if posSettingsService.isLoading {
ProgressView()
.font(.posBodyLargeRegular())
} else {
Text(value ?? Localization.notSet)
.font(.posBodyMediumRegular())
.foregroundStyle(.secondary)
}
}
}

private extension PointOfSaleSettingsStoreDetailView {
enum Localization {
static let notSet = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.notSet",
value: "Not set",
comment: "Text displayed on Point of Sale settings when any setting has not been provided."
)

static let storeInformation = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.storeInformation",
value: "Store Information",
comment: "Section title for store information in Point of Sale settings."
)

static let storeName = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.storeName",
value: "Store name",
comment: "Label for store name field in Point of Sale settings."
)

static let address = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.address",
value: "Address",
comment: "Label for address field in Point of Sale settings."
)

static let receiptInformation = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.receiptInformation",
value: "Receipt Information",
comment: "Section title for receipt information in Point of Sale settings."
)

static let receiptStoreName = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.receiptStoreName",
value: "Store name",
comment: "Label for receipt store name field in Point of Sale settings."
)

static let physicalAddress = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.physicalAddress",
value: "Physical address",
comment: "Label for physical address field in Point of Sale settings."
)

static let phoneNumber = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.phoneNumber",
value: "Phone number",
comment: "Label for phone number field in Point of Sale settings."
)

static let email = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.email",
value: "Email",
comment: "Label for email field in Point of Sale settings."
)

static let refundReturnsPolicy = NSLocalizedString(
"pointOfSaleSettingsStoreDetailView.refundReturnsPolicy",
value: "Refund & Returns Policy",
comment: "Label for refund and returns policy field in Point of Sale settings."
)
}
}

#Preview {
PointOfSaleSettingsStoreDetailView(posSettingsService: PointOfSaleSettingsService())
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SwiftUI
struct PointOfSaleSettingsView: View {
@Environment(\.dismiss) private var dismiss
@State private var selection: SidebarNavigation? = .store
@State private var settingsService = PointOfSaleSettingsService()

var body: some View {
POSPageHeaderView(
Expand Down Expand Up @@ -66,7 +67,7 @@ struct PointOfSaleSettingsView: View {
Group {
switch selection {
case .store:
PointOfSaleSettingsStoreDetailView()
PointOfSaleSettingsStoreDetailView(posSettingsService: settingsService)
case .hardware:
PointOfSaleSettingsHardwareDetailView()
case .help:
Expand All @@ -77,6 +78,9 @@ struct PointOfSaleSettingsView: View {
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.task {
await settingsService.retrievePOSReceiptSettings()
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions WooCommerce/WooCommerce.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1648,6 +1648,7 @@
68E952CC287536010095A23D /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952CB287536010095A23D /* SafariView.swift */; };
68E952D0287587BF0095A23D /* CardReaderManualRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952CF287587BF0095A23D /* CardReaderManualRowView.swift */; };
68E952D22875A44B0095A23D /* CardReaderType+Manual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952D12875A44B0095A23D /* CardReaderType+Manual.swift */; };
68E9F7012E5C499200D45747 /* PointOfSaleSettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E9F7002E5C499000D45747 /* PointOfSaleSettingsService.swift */; };
68ED2BD62ADD2C8C00ECA88D /* LineDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68ED2BD52ADD2C8C00ECA88D /* LineDetailView.swift */; };
68F151E12C0DA7910082AEC8 /* Cart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68F151E02C0DA7910082AEC8 /* Cart.swift */; };
68F68A502D6730E200BB9568 /* POSCollectOrderPaymentAnalyticsTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68F68A4F2D6730DF00BB9568 /* POSCollectOrderPaymentAnalyticsTracking.swift */; };
Expand Down Expand Up @@ -4824,6 +4825,7 @@
68E952CB287536010095A23D /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
68E952CF287587BF0095A23D /* CardReaderManualRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardReaderManualRowView.swift; sourceTree = "<group>"; };
68E952D12875A44B0095A23D /* CardReaderType+Manual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CardReaderType+Manual.swift"; sourceTree = "<group>"; };
68E9F7002E5C499000D45747 /* PointOfSaleSettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleSettingsService.swift; sourceTree = "<group>"; };
68ED2BD52ADD2C8C00ECA88D /* LineDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineDetailView.swift; sourceTree = "<group>"; };
68F151E02C0DA7910082AEC8 /* Cart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cart.swift; sourceTree = "<group>"; };
68F68A4F2D6730DF00BB9568 /* POSCollectOrderPaymentAnalyticsTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSCollectOrderPaymentAnalyticsTracking.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -9966,6 +9968,7 @@
children = (
01B3A1F12DB6D48800286B7F /* ItemListType.swift */,
20FCBCDC2CE223340082DCA3 /* PointOfSaleAggregateModel.swift */,
68E9F7002E5C499000D45747 /* PointOfSaleSettingsService.swift */,
209ECA802DB8FC280089F3D2 /* PointOfSaleViewStateCoordinator.swift */,
20C6E7502CDE4AEA00CD124C /* ItemListState.swift */,
20F7B12C2D12C7B900C08193 /* ItemsContainerState.swift */,
Expand Down Expand Up @@ -15576,6 +15579,7 @@
CE21B3D720FE669A00A259D5 /* BasicTableViewCell.swift in Sources */,
451A04EA2386D28300E368C9 /* ProductImagesHeaderViewModel.swift in Sources */,
02307924258731B2008EADEE /* PrintShippingLabelViewModel.swift in Sources */,
68E9F7012E5C499200D45747 /* PointOfSaleSettingsService.swift in Sources */,
D843D5D92248EE91001BFA55 /* ManualTrackingViewModel.swift in Sources */,
20886D3D2D96E0F900F7AE03 /* PointOfSaleCardPresentPaymentConnectingFailedLocationRequiredAlertViewModel.swift in Sources */,
204CB8102C10BB88000C9773 /* CardPresentPaymentPreviewService.swift in Sources */,
Expand Down