Skip to content

Commit 5feb817

Browse files
authored
Shipping Labels: Display label size from account settings as default (#15873)
2 parents b504c70 + 7593dff commit 5feb817

File tree

5 files changed

+233
-23
lines changed

5 files changed

+233
-23
lines changed

Modules/Sources/Yosemite/Stores/WooShippingStore.swift

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,17 @@ private extension WooShippingStore {
192192

193193
func loadAccountSettings(siteID: Int64,
194194
completion: @escaping (Result<WooShippingAccountSettings, Error>) -> Void) {
195-
remote.loadAccountSettings(siteID: siteID, completion: completion)
195+
remote.loadAccountSettings(siteID: siteID, completion: { [weak self] result in
196+
guard let self else { return }
197+
switch result {
198+
case .success(let settings):
199+
upsertShippingLabelAccountSettingsInBackground(siteID: siteID, accountSettings: settings.accountSettings) {
200+
completion(.success(settings))
201+
}
202+
case .failure(let error):
203+
completion(.failure(error))
204+
}
205+
})
196206
}
197207

198208
func updateAccountSettings(siteID: Int64,
@@ -732,6 +742,29 @@ private extension WooShippingStore {
732742
storageShippingLabel.refund = nil
733743
}
734744
}
745+
746+
/// Updates/inserts the specified readonly shipping label account settings entity *in a background thread*.
747+
/// `onCompletion` will be called on the main thread!
748+
///
749+
func upsertShippingLabelAccountSettingsInBackground(siteID: Int64,
750+
accountSettings: ShippingLabelAccountSettings,
751+
onCompletion: @escaping () -> Void) {
752+
storageManager.performAndSave({ storage in
753+
let storageAccountSettings = storage.loadShippingLabelAccountSettings(siteID: siteID) ??
754+
storage.insertNewObject(ofType: Storage.ShippingLabelAccountSettings.self)
755+
storageAccountSettings.update(with: accountSettings)
756+
757+
// Remove all previous payment methods
758+
storageAccountSettings.paymentMethods?.removeAll()
759+
760+
// Insert the payment methods from the read-only account settings
761+
for paymentMethod in accountSettings.paymentMethods {
762+
let newStoragePaymentMethod = storage.insertNewObject(ofType: Storage.ShippingLabelPaymentMethod.self)
763+
newStoragePaymentMethod.update(with: paymentMethod)
764+
storageAccountSettings.addToPaymentMethods(newStoragePaymentMethod)
765+
}
766+
}, completion: onCompletion, on: .main)
767+
}
735768
}
736769

737770
/// Represents errors that can be returned when purchasing a shipping label

Modules/Tests/YosemiteTests/Stores/WooShippingStoreTests.swift

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import XCTest
22
@testable import Yosemite
33
@testable import Networking
44
import protocol Storage.StorageType
5+
import class Storage.ShippingLabelPaymentMethod
56

67
final class WooShippingStoreTests: XCTestCase {
78

@@ -365,6 +366,90 @@ final class WooShippingStoreTests: XCTestCase {
365366
XCTAssertEqual(receivedValue, [samplePackage])
366367
}
367368

369+
// MARK: `loadAccountSettings`
370+
371+
func test_loadAccountSettings_returns_success_response() throws {
372+
// Given
373+
let remote = MockWooShippingRemote()
374+
let expectedSettings = WooShippingAccountSettings.fake()
375+
remote.whenLoadAccountSettings(siteID: sampleSiteID, thenReturn: .success(expectedSettings))
376+
let store = WooShippingStore(dispatcher: dispatcher, storageManager: storageManager, network: network, remote: remote)
377+
378+
// When
379+
let result: Result<WooShippingAccountSettings, Error> = waitFor { promise in
380+
let action = WooShippingAction.loadAccountSettings(siteID: self.sampleSiteID) { result in
381+
promise(result)
382+
}
383+
store.onAction(action)
384+
}
385+
386+
// Then
387+
XCTAssertTrue(result.isSuccess)
388+
let actualSettings = try result.get()
389+
XCTAssertEqual(actualSettings, expectedSettings)
390+
}
391+
392+
func test_loadAccountSettings_returns_error_on_failure() throws {
393+
// Given
394+
let remote = MockWooShippingRemote()
395+
let expectedError = NetworkError.notFound()
396+
remote.whenLoadAccountSettings(siteID: sampleSiteID, thenReturn: .failure(expectedError))
397+
let store = WooShippingStore(dispatcher: dispatcher, storageManager: storageManager, network: network, remote: remote)
398+
399+
// When
400+
let result: Result<WooShippingAccountSettings, Error> = waitFor { promise in
401+
let action = WooShippingAction.loadAccountSettings(siteID: self.sampleSiteID) { result in
402+
promise(result)
403+
}
404+
store.onAction(action)
405+
}
406+
407+
// Then
408+
XCTAssertTrue(result.isFailure)
409+
let error = try XCTUnwrap(result.failure)
410+
XCTAssertEqual(error as? NetworkError, expectedError)
411+
}
412+
413+
func test_loadAccountSettings_when_successful_then_upserts_settings_into_storage() throws {
414+
// Given
415+
let remote = MockWooShippingRemote()
416+
let paymentMethod = Yosemite.ShippingLabelPaymentMethod(
417+
paymentMethodID: 1434,
418+
name: "James Dean",
419+
cardType: .visa,
420+
cardDigits: "2352",
421+
expiry: DateFormatter.Defaults.yearMonthDayDateFormatter.date(from: "2030-12-31")
422+
)
423+
let accountSettings = ShippingLabelAccountSettings.fake().copy(
424+
siteID: sampleSiteID,
425+
paymentMethods: [paymentMethod],
426+
isEmailReceiptsEnabled: true,
427+
paperSize: .a4
428+
)
429+
let expectedSettings = WooShippingAccountSettings.fake().copy(accountSettings: accountSettings)
430+
remote.whenLoadAccountSettings(siteID: sampleSiteID, thenReturn: .success(expectedSettings))
431+
let store = WooShippingStore(dispatcher: dispatcher, storageManager: storageManager, network: network, remote: remote)
432+
433+
// Confidence check
434+
XCTAssertEqual(storageManager.viewStorage.countObjects(ofType: StorageShippingLabelAccountSettings.self), 0)
435+
436+
// When
437+
let onSuccess: Bool = waitFor { promise in
438+
let action = WooShippingAction.loadAccountSettings(siteID: self.sampleSiteID) { result in
439+
promise(result.isSuccess)
440+
}
441+
store.onAction(action)
442+
}
443+
444+
// Then
445+
XCTAssertTrue(onSuccess)
446+
XCTAssertEqual(storageManager.viewStorage.countObjects(ofType: StorageShippingLabelAccountSettings.self), 1)
447+
let storedSettings = try XCTUnwrap(storageManager.viewStorage.loadShippingLabelAccountSettings(siteID: sampleSiteID))
448+
XCTAssertEqual(storedSettings.siteID, sampleSiteID)
449+
XCTAssertEqual(storedSettings.toReadOnly(), accountSettings)
450+
XCTAssertEqual(storageManager.viewStorage.countObjects(ofType: Storage.ShippingLabelPaymentMethod.self), 1)
451+
}
452+
368453
// MARK: `loadPackages`
369454

370455
func test_loadPackages_returns_success_response_with_rates() throws {

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- [*] Shipping Labels: Enable the confirm button on the payment method sheet even when there are no changes. [https://github.com/woocommerce/woocommerce-ios/pull/15856]
1010
- [*] POS: start a new cart by scanning a barcode on the payment success screen [https://github.com/woocommerce/woocommerce-ios/pull/15870]
1111
- [*] Watch app: Fixed connection issue upon fresh install [https://github.com/woocommerce/woocommerce-ios/pull/15867]
12+
- [*] Shipping Labels: Display label size from account settings as default [https://github.com/woocommerce/woocommerce-ios/pull/15873]
1213

1314
22.7
1415
-----

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Post-Purchase/WooShippingPostPurchaseViewModel.swift

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import Foundation
33
import UIKit
44
import Yosemite
55
import WooFoundation
6+
import protocol Storage.StorageManagerType
67

78
final class WooShippingPostPurchaseViewModel: ObservableObject {
89
private let stores: StoresManager
10+
private let storageManager: StorageManagerType
911
private let siteID: Int64
1012
private let labelID: Int64
1113

@@ -26,14 +28,27 @@ final class WooShippingPostPurchaseViewModel: ObservableObject {
2628
/// Customs form URL for the shipping label
2729
let commercialInvoiceURL: URL?
2830

31+
/// Shipping Label Account Settings ResultsController
32+
///
33+
private lazy var accountSettingsResultsController: ResultsController<StorageShippingLabelAccountSettings> = {
34+
let predicate = NSPredicate(format: "siteID == %lld", siteID)
35+
return ResultsController<StorageShippingLabelAccountSettings>(
36+
storageManager: storageManager,
37+
matching: predicate,
38+
fetchLimit: 1,
39+
sortedBy: []
40+
)
41+
}()
42+
2943
init(siteID: Int64,
3044
labelID: Int64,
3145
labelSizes: [ShippingLabelPaperSize],
3246
isRefundable: Bool,
3347
trackingURL: URL?,
3448
pickupURL: URL?,
3549
commercialInvoiceURL: URL?,
36-
stores: StoresManager = ServiceLocator.stores) {
50+
stores: StoresManager = ServiceLocator.stores,
51+
storageManager: StorageManagerType = ServiceLocator.storageManager) {
3752
self.siteID = siteID
3853
self.labelID = labelID
3954
self.labelSizes = labelSizes
@@ -42,15 +57,20 @@ final class WooShippingPostPurchaseViewModel: ObservableObject {
4257
self.pickupURL = pickupURL
4358
self.commercialInvoiceURL = commercialInvoiceURL
4459
self.stores = stores
60+
self.storageManager = storageManager
61+
62+
configureAccountSettingsResultsController()
4563
}
4664

4765
convenience init(shippingLabel: ShippingLabel,
4866
siteAddress: SiteAddress = SiteAddress(),
49-
stores: StoresManager = ServiceLocator.stores) {
67+
stores: StoresManager = ServiceLocator.stores,
68+
storageManager: StorageManagerType = ServiceLocator.storageManager) {
5069
// Label sizes aren't provided by the API, so we can hard-code them to match the extension behavior:
70+
// Ref: https://github.com/woocommerce/woocommerce-shipping/blob/trunk/client/components/label-purchase/label/utils.ts
5171
let labelSizes = {
5272
var availableLabelSizes: [ShippingLabelPaperSize] = [.label, .letter]
53-
if [.US, .CA, .MX, .DO].contains(siteAddress.countryCode) {
73+
if [.US, .CA, .MX, .DO].contains(siteAddress.countryCode) == false {
5474
availableLabelSizes.append(.a4)
5575
}
5676
return availableLabelSizes
@@ -71,7 +91,8 @@ final class WooShippingPostPurchaseViewModel: ObservableObject {
7191
trackingURL: trackingURL,
7292
pickupURL: pickupURL,
7393
commercialInvoiceURL: commercialInvoiceURL,
74-
stores: stores)
94+
stores: stores,
95+
storageManager: storageManager)
7596
}
7697

7798
/// Fetches the shipping label in the selected paper size and presents the print dialog.
@@ -89,6 +110,26 @@ final class WooShippingPostPurchaseViewModel: ObservableObject {
89110
}
90111

91112
private extension WooShippingPostPurchaseViewModel {
113+
/// Shipping Label Account Settings ResultsController monitoring
114+
///
115+
func configureAccountSettingsResultsController() {
116+
accountSettingsResultsController.onDidChangeContent = { [weak self] in
117+
self?.updateSelectedLabelSize()
118+
}
119+
120+
accountSettingsResultsController.onDidResetContent = { [weak self] in
121+
self?.updateSelectedLabelSize()
122+
}
123+
124+
try? accountSettingsResultsController.performFetch()
125+
updateSelectedLabelSize()
126+
}
127+
128+
func updateSelectedLabelSize() {
129+
guard let fetchedAccountSettings = accountSettingsResultsController.fetchedObjects.first else { return }
130+
selectedLabelSize = fetchedAccountSettings.paperSize
131+
}
132+
92133
/// Requests the shipping label data for printing.
93134
@MainActor
94135
func requestPrintData() async throws -> ShippingLabelPrintData {

WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShippingPostPurchaseViewModelTests.swift

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import Yosemite
44

55
final class WooShippingPostPurchaseViewModelTests: XCTestCase {
66

7+
private var storageManager: MockStorageManager!
8+
9+
override func setUp() {
10+
super.setUp()
11+
storageManager = MockStorageManager()
12+
}
13+
714
func test_inits_with_provided_properties() {
815
// Given
916
let labelSizes: [ShippingLabelPaperSize] = [.label, .letter, .a4]
@@ -27,30 +34,40 @@ final class WooShippingPostPurchaseViewModelTests: XCTestCase {
2734
XCTAssertEqual(viewModel.commercialInvoiceURL, customsFormURL)
2835
}
2936

30-
func test_labelSizes_includes_expected_default_values() {
31-
// Given
32-
let countrySetting = SiteSetting.fake().copy(settingID: "woocommerce_default_country",
33-
value: "GB")
34-
let siteAddress = SiteAddress(siteSettings: [countrySetting])
37+
func test_labelSizes_excludes_a4_for_north_american_countries() {
38+
let northAmericanCountries = ["US", "CA", "MX", "DO"]
3539

36-
// When
37-
let viewModel = WooShippingPostPurchaseViewModel(shippingLabel: ShippingLabel.fake(), siteAddress: siteAddress)
40+
for countryCode in northAmericanCountries {
41+
// Given
42+
let countrySetting = SiteSetting.fake().copy(settingID: "woocommerce_default_country",
43+
value: countryCode)
44+
let siteAddress = SiteAddress(siteSettings: [countrySetting])
3845

39-
// Then
40-
assertEqual([.label, .letter], viewModel.labelSizes)
46+
// When
47+
let viewModel = WooShippingPostPurchaseViewModel(shippingLabel: ShippingLabel.fake(),
48+
siteAddress: siteAddress)
49+
50+
// Then
51+
assertEqual([.label, .letter], viewModel.labelSizes)
52+
}
4153
}
4254

43-
func test_labelSizes_includes_expected_values_for_country_with_a4_label_size() {
44-
// Given
45-
let countrySetting = SiteSetting.fake().copy(settingID: "woocommerce_default_country",
46-
value: "US:NY")
47-
let siteAddress = SiteAddress(siteSettings: [countrySetting])
55+
func test_labelSizes_includes_a4_for_non_north_american_countries() {
56+
let nonNorthAmericanCountries = ["GB", "FR", "DE", "IT", "ES", "NL", "AU", "JP"]
4857

49-
// When
50-
let viewModel = WooShippingPostPurchaseViewModel(shippingLabel: ShippingLabel.fake(), siteAddress: siteAddress)
58+
for countryCode in nonNorthAmericanCountries {
59+
// Given
60+
let countrySetting = SiteSetting.fake().copy(settingID: "woocommerce_default_country",
61+
value: countryCode)
62+
let siteAddress = SiteAddress(siteSettings: [countrySetting])
5163

52-
// Then
53-
assertEqual([.label, .letter, .a4], viewModel.labelSizes)
64+
// When
65+
let viewModel = WooShippingPostPurchaseViewModel(shippingLabel: ShippingLabel.fake(),
66+
siteAddress: siteAddress)
67+
68+
// Then
69+
assertEqual([.label, .letter, .a4], viewModel.labelSizes)
70+
}
5471
}
5572

5673
func test_trackingURL_parsed_from_shipping_label() {
@@ -111,4 +128,37 @@ final class WooShippingPostPurchaseViewModelTests: XCTestCase {
111128
// Then
112129
XCTAssertNotNil(printData)
113130
}
131+
132+
func test_selectedLabelSize_defaults_to_label() {
133+
// Given & When
134+
let viewModel = WooShippingPostPurchaseViewModel(shippingLabel: ShippingLabel.fake(), storageManager: storageManager)
135+
136+
// Then
137+
XCTAssertEqual(viewModel.selectedLabelSize, .label)
138+
}
139+
140+
func test_selectedLabelSize_initialized_from_account_settings() {
141+
// Given
142+
let siteID: Int64 = 123
143+
144+
// Insert account settings with A4 paper size
145+
let settings = ShippingLabelAccountSettings.fake().copy(siteID: siteID, paperSize: .a4)
146+
insertShippingLabelAccountSettings(readonlySettings: settings)
147+
148+
// When
149+
let viewModel = WooShippingPostPurchaseViewModel(
150+
shippingLabel: ShippingLabel.fake().copy(siteID: siteID),
151+
storageManager: storageManager
152+
)
153+
154+
// Then
155+
XCTAssertEqual(viewModel.selectedLabelSize, .a4)
156+
}
157+
}
158+
159+
private extension WooShippingPostPurchaseViewModelTests {
160+
func insertShippingLabelAccountSettings(readonlySettings: ShippingLabelAccountSettings) {
161+
let storageSettings = storageManager.viewStorage.insertNewObject(ofType: StorageShippingLabelAccountSettings.self)
162+
storageSettings.update(with: readonlySettings)
163+
}
114164
}

0 commit comments

Comments
 (0)