Skip to content

Commit aa4d818

Browse files
authored
Merge pull request #6207 from woocommerce/issue/6129-auto-draft-support
Order Creation: Defines base status for order sync
2 parents fa5da3a + 316bb7e commit aa4d818

File tree

8 files changed

+200
-2
lines changed

8 files changed

+200
-2
lines changed

Networking/Networking/Model/OrderStatusEnum.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Codegen
77
/// and it is used to determine the user facing display order
88
///
99
public enum OrderStatusEnum: Codable, Hashable, Comparable, GeneratedFakeable {
10+
case autoDraft
1011
case pending
1112
case processing
1213
case onHold
@@ -25,6 +26,8 @@ extension OrderStatusEnum: RawRepresentable {
2526
///
2627
public init(rawValue: String) {
2728
switch rawValue {
29+
case Keys.autoDraft:
30+
self = .autoDraft
2831
case Keys.pending:
2932
self = .pending
3033
case Keys.processing:
@@ -48,6 +51,7 @@ extension OrderStatusEnum: RawRepresentable {
4851
///
4952
public var rawValue: String {
5053
switch self {
54+
case .autoDraft: return Keys.autoDraft
5155
case .pending: return Keys.pending
5256
case .processing: return Keys.processing
5357
case .onHold: return Keys.onHold
@@ -64,6 +68,7 @@ extension OrderStatusEnum: RawRepresentable {
6468
/// Enum containing the 'Known' OrderStatus Keys
6569
///
6670
private enum Keys {
71+
static let autoDraft = "auto-draft"
6772
static let pending = "pending"
6873
static let processing = "processing"
6974
static let onHold = "on-hold"

WooCommerce/Classes/Extensions/UILabel+OrderStatus.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension UILabel {
2727
///
2828
private func applyBackground(for statusEnum: OrderStatusEnum) {
2929
switch statusEnum {
30-
case .pending, .completed, .cancelled, .refunded, .custom:
30+
case .autoDraft, .pending, .completed, .cancelled, .refunded, .custom:
3131
backgroundColor = .gray(.shade5)
3232
case .onHold:
3333
backgroundColor = .withColorStudio(.orange, shade: .shade5)

WooCommerce/Classes/ViewRelated/Orders/Order Creation/NewOrderViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ extension NewOrderViewModel {
289289
title = orderStatus.name ?? orderStatus.slug
290290
color = {
291291
switch orderStatus.status {
292-
case .pending, .completed, .cancelled, .refunded, .custom:
292+
case .autoDraft, .pending, .completed, .cancelled, .refunded, .custom:
293293
return .gray(.shade5)
294294
case .onHold:
295295
return .withColorStudio(.orange, shade: .shade5)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import Yosemite
2+
3+
/// Helper that defines which `status` a new order should initially have.
4+
///
5+
struct NewOrderInitialStatusResolver {
6+
/// Current site ID
7+
///
8+
private let siteID: Int64
9+
10+
/// Stores.
11+
///
12+
private let stores: StoresManager
13+
14+
/// Defines the WC version where `auto-draft` should be available.
15+
///
16+
private let draftMinSupportedVersion = "6.3.0"
17+
18+
/// WooCommerce plugin name.
19+
///
20+
private let wcPluginName = "WooCommerce"
21+
22+
init(siteID: Int64, stores: StoresManager = ServiceLocator.stores) {
23+
self.siteID = siteID
24+
self.stores = stores
25+
}
26+
27+
/// Decides the initial `status` for a new order based on the current store version.
28+
///
29+
func resolve(onCompletion: @escaping (OrderStatusEnum) -> ()) {
30+
let action = SystemStatusAction.fetchSystemPlugin(siteID: siteID, systemPluginName: wcPluginName) { wooPlugin in
31+
guard let wooPlugin = wooPlugin else {
32+
return onCompletion(.pending)
33+
}
34+
35+
// auto-draft should exists in versions greater than `6.3.0`
36+
switch draftMinSupportedVersion.compare(wooPlugin.version, options: .numeric) {
37+
case .orderedAscending, .orderedSame:
38+
onCompletion(.autoDraft)
39+
case .orderedDescending:
40+
onCompletion(.pending)
41+
}
42+
}
43+
stores.dispatch(action)
44+
}
45+
}

WooCommerce/Classes/ViewRelated/Orders/Order Creation/Synchronizer/RemoteOrderSynchronizer.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,17 @@ final class RemoteOrderSynchronizer: OrderSynchronizer {
3838

3939
private let stores: StoresManager
4040

41+
/// This is the order status that we will use to keep the order in sync with the remote source.
42+
///
43+
private var baseSyncStatus: OrderStatusEnum = .pending
44+
4145
// MARK: Initializers
4246

4347
init(siteID: Int64, stores: StoresManager = ServiceLocator.stores) {
4448
self.siteID = siteID
4549
self.stores = stores
50+
51+
updateBaseSyncOrderStatus()
4652
}
4753

4854
// MARK: Methods
@@ -56,3 +62,14 @@ final class RemoteOrderSynchronizer: OrderSynchronizer {
5662
// TODO: Implement
5763
}
5864
}
65+
66+
// MARK: Helpers
67+
private extension RemoteOrderSynchronizer {
68+
/// Updates the base sync order status.
69+
///
70+
func updateBaseSyncOrderStatus() {
71+
NewOrderInitialStatusResolver(siteID: siteID, stores: stores).resolve { [weak self] baseStatus in
72+
self?.baseSyncStatus = baseStatus
73+
}
74+
}
75+
}

WooCommerce/Classes/ViewRelated/Orders/Order Filters/FilterOrderListViewModel.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ extension OrderStatusEnum: FilterType {
117117
///
118118
var description: String {
119119
switch self {
120+
case .autoDraft:
121+
return NSLocalizedString("Draft", comment: "Display label for auto-draft order status.")
120122
case .pending:
121123
return NSLocalizedString("Pending", comment: "Display label for pending order status.")
122124
case .processing:

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@
420420
24C5AC7625A53021008FD769 /* Embassy in Frameworks */ = {isa = PBXBuildFile; productRef = 247CE89B2583402A00F9D9D1 /* Embassy */; };
421421
24F98C502502AEE200F49B68 /* EventLogging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F98C4F2502AEE200F49B68 /* EventLogging.swift */; };
422422
2602A63D27BD3C8C00B347F1 /* RemoteOrderSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2602A63C27BD3C8C00B347F1 /* RemoteOrderSynchronizer.swift */; };
423+
2602A63F27BD880A00B347F1 /* NewOrderInitialStatusResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2602A63E27BD880A00B347F1 /* NewOrderInitialStatusResolver.swift */; };
424+
2602A64227BD89CE00B347F1 /* NewOrderInitialStatusResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2602A64127BD89CE00B347F1 /* NewOrderInitialStatusResolverTests.swift */; };
423425
260C315E2523CC4000157BC2 /* RefundProductsTotalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260C315D2523CC4000157BC2 /* RefundProductsTotalViewModel.swift */; };
424426
260C31602524ECA900157BC2 /* IssueRefundViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260C315F2524ECA900157BC2 /* IssueRefundViewController.swift */; };
425427
260C31622524EEB200157BC2 /* IssueRefundViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 260C31612524EEB200157BC2 /* IssueRefundViewController.xib */; };
@@ -2064,6 +2066,8 @@
20642066
24F98C4F2502AEE200F49B68 /* EventLogging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLogging.swift; sourceTree = "<group>"; };
20652067
25D00C97936D2C6589F8ECE9 /* Pods-WooCommerce.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WooCommerce.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-WooCommerce/Pods-WooCommerce.release-alpha.xcconfig"; sourceTree = "<group>"; };
20662068
2602A63C27BD3C8C00B347F1 /* RemoteOrderSynchronizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteOrderSynchronizer.swift; sourceTree = "<group>"; };
2069+
2602A63E27BD880A00B347F1 /* NewOrderInitialStatusResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewOrderInitialStatusResolver.swift; sourceTree = "<group>"; };
2070+
2602A64127BD89CE00B347F1 /* NewOrderInitialStatusResolverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewOrderInitialStatusResolverTests.swift; sourceTree = "<group>"; };
20672071
260C315D2523CC4000157BC2 /* RefundProductsTotalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefundProductsTotalViewModel.swift; sourceTree = "<group>"; };
20682072
260C315F2524ECA900157BC2 /* IssueRefundViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueRefundViewController.swift; sourceTree = "<group>"; };
20692073
260C31612524EEB200157BC2 /* IssueRefundViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IssueRefundViewController.xib; sourceTree = "<group>"; };
@@ -4305,6 +4309,14 @@
43054309
path = "Edit Order Status";
43064310
sourceTree = "<group>";
43074311
};
4312+
2602A64027BD89B300B347F1 /* Synchronizer */ = {
4313+
isa = PBXGroup;
4314+
children = (
4315+
2602A64127BD89CE00B347F1 /* NewOrderInitialStatusResolverTests.swift */,
4316+
);
4317+
path = Synchronizer;
4318+
sourceTree = "<group>";
4319+
};
43084320
2611EE57243A46C500A74490 /* Categories */ = {
43094321
isa = PBXGroup;
43104322
children = (
@@ -4540,6 +4552,7 @@
45404552
26C6439227B5DBE900DD00D1 /* OrderSynchronizer.swift */,
45414553
26C6439427B9A1B300DD00D1 /* LocalOrderSynchronizer.swift */,
45424554
2602A63C27BD3C8C00B347F1 /* RemoteOrderSynchronizer.swift */,
4555+
2602A63E27BD880A00B347F1 /* NewOrderInitialStatusResolver.swift */,
45434556
);
45444557
path = Synchronizer;
45454558
sourceTree = "<group>";
@@ -6320,6 +6333,7 @@
63206333
CC53FB3F2759042600C4CA4F /* AddProductToOrderViewModelTests.swift */,
63216334
CC13C0CC278E086D00C0B5B5 /* AddProductVariationToOrderViewModelTests.swift */,
63226335
AE90475B27A99D6000073E1D /* CreateOrderAddressFormViewModelTests.swift */,
6336+
2602A64027BD89B300B347F1 /* Synchronizer */,
63236337
);
63246338
path = "Order Creation";
63256339
sourceTree = "<group>";
@@ -8439,6 +8453,7 @@
84398453
02817B39242B34560050AD8B /* ToolbarView.swift in Sources */,
84408454
D817586222BB64C300289CFE /* OrderDetailsNotices.swift in Sources */,
84418455
022F7A0324A05F6400012601 /* LinkedProductsListSelectorViewController.swift in Sources */,
8456+
2602A63F27BD880A00B347F1 /* NewOrderInitialStatusResolver.swift in Sources */,
84428457
E120F63826C26B550005A029 /* InPersonPaymentsLoadingView.swift in Sources */,
84438458
456417F4247D5434001203F6 /* UITableView+Helpers.swift in Sources */,
84448459
E15FC74326BC1D2700CF83E6 /* SafariSheet.swift in Sources */,
@@ -9331,6 +9346,7 @@
93319346
31F635DC273AF0B100E14F10 /* VersionHelpersTests.swift in Sources */,
93329347
FE3E427726A8545B00C596CE /* MockRoleEligibilityUseCase.swift in Sources */,
93339348
02F67FF525806E0100C3BAD2 /* ShippingLabelTrackingURLGeneratorTests.swift in Sources */,
9349+
2602A64227BD89CE00B347F1 /* NewOrderInitialStatusResolverTests.swift in Sources */,
93349350
570AAB052472FACB00516C0C /* OrderDetailsDataSourceTests.swift in Sources */,
93359351
CC77488E2719A07D0043CDD7 /* ShippingLabelAddressTopBannerFactoryTests.swift in Sources */,
93369352
B517EA1A218B2D2600730EC4 /* StringFormatterTests.swift in Sources */,
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import XCTest
2+
import TestKit
3+
import Fakes
4+
5+
@testable import WooCommerce
6+
@testable import Yosemite
7+
8+
class NewOrderInitialStatusResolverTests: XCTestCase {
9+
10+
private let sampleSiteID: Int64 = 1234
11+
12+
func test_no_store_version_use_pending_status() {
13+
// Given
14+
let stores = createStoreWithVersion(nil)
15+
16+
// When
17+
let resolver = NewOrderInitialStatusResolver(siteID: sampleSiteID, stores: stores)
18+
let initialStatus: OrderStatusEnum = waitFor { promise in
19+
resolver.resolve { status in
20+
promise(status)
21+
}
22+
}
23+
24+
// Then
25+
XCTAssertEqual(initialStatus, .pending)
26+
}
27+
28+
func test_older_store_version_use_pending_status() {
29+
// Given
30+
let stores = createStoreWithVersion("6.2.5")
31+
32+
// When
33+
let resolver = NewOrderInitialStatusResolver(siteID: sampleSiteID, stores: stores)
34+
let initialStatus: OrderStatusEnum = waitFor { promise in
35+
resolver.resolve { status in
36+
promise(status)
37+
}
38+
}
39+
40+
// Then
41+
XCTAssertEqual(initialStatus, .pending)
42+
}
43+
44+
func test_same_store_version_use_draft_status() {
45+
// Given
46+
let stores = createStoreWithVersion("6.3.0")
47+
48+
// When
49+
let resolver = NewOrderInitialStatusResolver(siteID: sampleSiteID, stores: stores)
50+
let initialStatus: OrderStatusEnum = waitFor { promise in
51+
resolver.resolve { status in
52+
promise(status)
53+
}
54+
}
55+
56+
// Then
57+
XCTAssertEqual(initialStatus, .autoDraft)
58+
}
59+
60+
func test_newer_store_version_use_draft_status() {
61+
// Given
62+
let stores = createStoreWithVersion("6.4.0")
63+
64+
// When
65+
let resolver = NewOrderInitialStatusResolver(siteID: sampleSiteID, stores: stores)
66+
let initialStatus: OrderStatusEnum = waitFor { promise in
67+
resolver.resolve { status in
68+
promise(status)
69+
}
70+
}
71+
72+
// Then
73+
XCTAssertEqual(initialStatus, .autoDraft)
74+
}
75+
76+
func test_beta_store_version_use_draft_status() {
77+
// Given
78+
let stores = createStoreWithVersion("6.3.0-beta.1")
79+
80+
// When
81+
let resolver = NewOrderInitialStatusResolver(siteID: sampleSiteID, stores: stores)
82+
let initialStatus: OrderStatusEnum = waitFor { promise in
83+
resolver.resolve { status in
84+
promise(status)
85+
}
86+
}
87+
88+
// Then
89+
XCTAssertEqual(initialStatus, .autoDraft)
90+
}
91+
}
92+
93+
private extension NewOrderInitialStatusResolverTests {
94+
95+
/// Creates a mock store manager that returns the provided version as part of the `SystemStatusAction.fetchSystemPlugin` action.
96+
///
97+
func createStoreWithVersion(_ version: String?) -> StoresManager {
98+
let stores = MockStoresManager(sessionManager: .testingInstance)
99+
stores.whenReceivingAction(ofType: SystemStatusAction.self) { action in
100+
switch action {
101+
case let .fetchSystemPlugin(_, _, onCompletion):
102+
guard let version = version else {
103+
return onCompletion(nil)
104+
}
105+
let plugin = SystemPlugin.fake().copy(version: version)
106+
onCompletion(plugin)
107+
default:
108+
XCTFail("Unexpected action received: \(action)")
109+
}
110+
}
111+
return stores
112+
}
113+
}

0 commit comments

Comments
 (0)