Skip to content

Commit 174524c

Browse files
authored
Merge pull request #5516 from woocommerce/issue/5483-update-order
2 parents 46b33c9 + 791755b commit 174524c

File tree

10 files changed

+233
-21
lines changed

10 files changed

+233
-21
lines changed

Networking/Networking/Model/OrderFeeLine.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extension OrderFeeLine {
4141
public func encode(to encoder: Encoder) throws {
4242
var container = encoder.container(keyedBy: CodingKeys.self)
4343

44+
try container.encode(feeID, forKey: .feeID)
4445
try container.encode(name, forKey: .name)
4546
try container.encode(taxClass, forKey: .taxClass)
4647
try container.encode(taxStatus, forKey: .taxStatus)

Networking/Networking/Remote/OrdersRemote.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ public class OrdersRemote: Remote {
183183
case .billingAddress:
184184
let billingAddressEncoded = try order.billingAddress?.toDictionary()
185185
params[Order.CodingKeys.billingAddress.rawValue] = billingAddressEncoded
186+
case .fees:
187+
let feesEncoded = try order.fees.map { try $0.toDictionary() }
188+
params[Order.CodingKeys.feeLines.rawValue] = feesEncoded
186189
}
187190
}
188191
}()
@@ -264,6 +267,7 @@ public extension OrdersRemote {
264267
case customerNote
265268
case shippingAddress
266269
case billingAddress
270+
case fees
267271
}
268272

269273
/// Order fields supported for create

Networking/NetworkingTests/Remote/OrdersRemoteTests.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,17 @@ final class OrdersRemoteTests: XCTestCase {
252252
func test_create_order_properly_encodes_fee_lines() throws {
253253
// Given
254254
let remote = OrdersRemote(network: network)
255-
let fee = OrderFeeLine(feeID: 0, name: "Line", taxClass: "", taxStatus: .none, total: "12.34", totalTax: "", taxes: [], attributes: [])
255+
let fee = OrderFeeLine(feeID: 333, name: "Line", taxClass: "", taxStatus: .none, total: "12.34", totalTax: "", taxes: [], attributes: [])
256256
let order = Order.fake().copy(fees: [fee])
257257

258258
// When
259259
remote.createOrder(siteID: 123, order: order, fields: [.feeLines]) { result in }
260260

261261
// Then
262262
let request = try XCTUnwrap(network.requestsForResponseData.last as? JetpackRequest)
263-
let received = try XCTUnwrap(request.parameters["fee_lines"] as? [[String: String]]).first
264-
let expected = [
263+
let received = try XCTUnwrap(request.parameters["fee_lines"] as? [[String: AnyHashable]]).first
264+
let expected: [String: AnyHashable] = [
265+
"id": fee.feeID,
265266
"name": fee.name,
266267
"tax_status": fee.taxStatus.rawValue,
267268
"tax_class": fee.taxClass,

WooCommerce/Classes/ViewRelated/Orders/Simple Payments/Summary/SimplePaymentsSummary.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,9 @@ private struct TakePaymentSection: View {
228228
Divider()
229229

230230
Button(SimplePaymentsSummary.Localization.takePayment(total: viewModel.total), action: {
231-
print("Take payment pressed")
231+
viewModel.updateOrder()
232232
})
233-
.buttonStyle(PrimaryButtonStyle())
233+
.buttonStyle(PrimaryLoadingButtonStyle(isLoading: viewModel.showLoadingIndicator))
234234
.padding()
235235

236236
}

WooCommerce/Classes/ViewRelated/Orders/Simple Payments/Summary/SimplePaymentsSummaryViewModel.swift

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ final class SimplePaymentsSummaryViewModel: ObservableObject {
2525
///
2626
@Published var enableTaxes: Bool = false
2727

28+
/// Defines if a loading indicator should be shown.
29+
///
30+
@Published private(set) var showLoadingIndicator = false
31+
2832
/// Total to charge. With or without taxes.
2933
///
3034
var total: String {
@@ -45,6 +49,22 @@ final class SimplePaymentsSummaryViewModel: ObservableObject {
4549
///
4650
private let currencyFormatter: CurrencyFormatter
4751

52+
/// Store ID
53+
///
54+
private let siteID: Int64
55+
56+
/// Order ID to update.
57+
///
58+
private let orderID: Int64
59+
60+
/// Fee ID to update.
61+
///
62+
private let feeID: Int64
63+
64+
/// Stores Manager.
65+
///
66+
private let stores: StoresManager
67+
4868
/// ViewModel for the edit order note view.
4969
///
5070
lazy private(set) var noteViewModel = SimplePaymentsNoteViewModel()
@@ -53,8 +73,16 @@ final class SimplePaymentsSummaryViewModel: ObservableObject {
5373
totalWithTaxes: String,
5474
taxAmount: String,
5575
noteContent: String? = nil,
56-
currencyFormatter: CurrencyFormatter = CurrencyFormatter(currencySettings: ServiceLocator.currencySettings)) {
76+
siteID: Int64 = 0,
77+
orderID: Int64 = 0,
78+
feeID: Int64 = 0,
79+
currencyFormatter: CurrencyFormatter = CurrencyFormatter(currencySettings: ServiceLocator.currencySettings),
80+
stores: StoresManager = ServiceLocator.stores) {
81+
self.siteID = siteID
82+
self.orderID = orderID
83+
self.feeID = feeID
5784
self.currencyFormatter = currencyFormatter
85+
self.stores = stores
5886
self.providedAmount = currencyFormatter.formatAmount(providedAmount) ?? providedAmount
5987
self.totalWithTaxes = currencyFormatter.formatAmount(totalWithTaxes) ?? totalWithTaxes
6088
self.taxAmount = currencyFormatter.formatAmount(taxAmount) ?? taxAmount
@@ -81,16 +109,49 @@ final class SimplePaymentsSummaryViewModel: ObservableObject {
81109

82110
convenience init(order: Order,
83111
providedAmount: String,
84-
currencyFormatter: CurrencyFormatter = CurrencyFormatter(currencySettings: ServiceLocator.currencySettings)) {
112+
currencyFormatter: CurrencyFormatter = CurrencyFormatter(currencySettings: ServiceLocator.currencySettings),
113+
stores: StoresManager = ServiceLocator.stores) {
85114
self.init(providedAmount: providedAmount,
86115
totalWithTaxes: order.total,
87116
taxAmount: order.totalTax,
88-
currencyFormatter: currencyFormatter)
117+
siteID: order.siteID,
118+
orderID: order.orderID,
119+
feeID: order.fees.first?.feeID ?? 0,
120+
currencyFormatter: currencyFormatter,
121+
stores: stores)
89122
}
90123

91124
/// Sends a signal to reload the view. Needed when coming back from the `EditNote` view.
92125
///
93126
func reloadContent() {
94127
objectWillChange.send()
95128
}
129+
130+
/// Updates the order remotely with the information entered by the merchant.
131+
///
132+
func updateOrder() {
133+
showLoadingIndicator = true
134+
let action = OrderAction.updateSimplePaymentsOrder(siteID: siteID,
135+
orderID: orderID,
136+
feeID: feeID,
137+
amount: providedAmount,
138+
taxable: enableTaxes,
139+
orderNote: noteContent,
140+
email: email) { [weak self] result in
141+
guard let self = self else { return }
142+
self.showLoadingIndicator = false
143+
144+
switch result {
145+
case .success:
146+
// TODO: Navigate to Payment Method
147+
// TODO: Analytics
148+
break
149+
case .failure:
150+
// TODO: Present notice
151+
// TODO: Analytics
152+
break
153+
}
154+
}
155+
stores.dispatch(action)
156+
}
96157
}

WooCommerce/WooCommerceTests/ViewRelated/Orders/Simple Payments/SimplePaymentsSummaryViewModelTests.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import XCTest
33
import Combine
44

55
@testable import WooCommerce
6+
@testable import Yosemite
67

78
final class SimplePaymentsSummaryViewModelTests: XCTestCase {
89

@@ -77,4 +78,34 @@ final class SimplePaymentsSummaryViewModelTests: XCTestCase {
7778
// When & Then
7879
XCTAssertEqual(viewModel.taxRate, "4.30")
7980
}
81+
82+
func test_when_order_is_updated_loading_indicator_is_toggled() {
83+
// Given
84+
let mockStores = MockStoresManager(sessionManager: .testingInstance)
85+
let viewModel = SimplePaymentsSummaryViewModel(providedAmount: "1.0", totalWithTaxes: "1.0", taxAmount: "0.0", stores: mockStores)
86+
mockStores.whenReceivingAction(ofType: OrderAction.self) { action in
87+
switch action {
88+
case let .updateSimplePaymentsOrder(_, _, _, _, _, _, _, onCompletion):
89+
onCompletion(.success(Order.fake()))
90+
default:
91+
XCTFail("Unexpected action: \(action)")
92+
}
93+
}
94+
95+
// When
96+
let loadingStates: [Bool] = waitFor { promise in
97+
viewModel.$showLoadingIndicator
98+
.dropFirst() // Initial value
99+
.collect(2) // Collect toggle
100+
.first()
101+
.sink { loadingStates in
102+
promise(loadingStates)
103+
}
104+
.store(in: &self.subscriptions)
105+
viewModel.updateOrder()
106+
}
107+
108+
// Then
109+
XCTAssertEqual(loadingStates, [true, false]) // Loading, then not loading.
110+
}
80111
}

Yosemite/Yosemite/Actions/OrderAction.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,22 @@ public enum OrderAction: Action {
6565
///
6666
case updateOrder(siteID: Int64, order: Order, fields: [OrderUpdateField], onCompletion: (Result<Order, Error>) -> Void)
6767

68-
/// Creates a simple payments order with a specific amount value and no tax.
68+
/// Creates a simple payments order with a specific amount value and tax status.
6969
///
7070
case createSimplePaymentsOrder(siteID: Int64, amount: String, taxable: Bool, onCompletion: (Result<Order, Error>) -> Void)
7171

7272
/// Creates a manual order with the provided order details.
7373
///
7474
case createOrder(siteID: Int64, order: Order, onCompletion: (Result<Order, Error>) -> Void)
75+
76+
/// Updates a simple payments order with the specified values.
77+
///
78+
case updateSimplePaymentsOrder(siteID: Int64,
79+
orderID: Int64,
80+
feeID: Int64,
81+
amount: String,
82+
taxable: Bool,
83+
orderNote: String?,
84+
email: String?,
85+
onCompletion: (Result<Order, Error>) -> Void)
7586
}

Yosemite/Yosemite/Stores/Order/OrderFactory.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,19 @@ enum OrderFactory {
3333
shippingLines: [],
3434
coupons: [],
3535
refunds: [],
36-
fees: [.init(feeID: 0,
37-
name: "Simple Payments",
38-
taxClass: "",
39-
taxStatus: taxable ? .taxable : .none,
40-
total: amount,
41-
totalTax: "",
42-
taxes: [],
43-
attributes: [])])
36+
fees: [simplePaymentFee(feeID: 0, amount: amount, taxable: taxable)])
37+
}
38+
39+
/// Creates a fee line suitable to be used within a simple payments order.
40+
///
41+
static func simplePaymentFee(feeID: Int64, amount: String, taxable: Bool) -> OrderFeeLine {
42+
.init(feeID: feeID,
43+
name: "Simple Payments",
44+
taxClass: "",
45+
taxStatus: taxable ? .taxable : .none,
46+
total: amount,
47+
totalTax: "",
48+
taxes: [],
49+
attributes: [])
4450
}
4551
}

Yosemite/Yosemite/Stores/OrderStore.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ public class OrderStore: Store {
6666
createSimplePaymentsOrder(siteID: siteID, amount: amount, taxable: taxable, onCompletion: onCompletion)
6767
case let .createOrder(siteID, order, onCompletion):
6868
createOrder(siteID: siteID, order: order, onCompletion: onCompletion)
69+
70+
case let .updateSimplePaymentsOrder(siteID, orderID, feeID, amount, taxable, orderNote, email, onCompletion):
71+
updateSimplePaymentsOrder(siteID: siteID,
72+
orderID: orderID,
73+
feeID: feeID,
74+
amount: amount,
75+
taxable: taxable,
76+
orderNote: orderNote,
77+
email: email,
78+
onCompletion: onCompletion)
6979
}
7080
}
7181
}
@@ -267,6 +277,41 @@ private extension OrderStore {
267277
}
268278
}
269279

280+
/// Updates a simple payment order with the specified values.
281+
///
282+
func updateSimplePaymentsOrder(siteID: Int64,
283+
orderID: Int64,
284+
feeID: Int64,
285+
amount: String,
286+
taxable: Bool,
287+
orderNote: String?,
288+
email: String?,
289+
onCompletion: @escaping (Result<Order, Error>) -> Void) {
290+
291+
// Recreate the original order
292+
let originalOrder = OrderFactory.simplePaymentsOrder(amount: amount, taxable: taxable)
293+
294+
// Create updated fields
295+
let newFee = OrderFactory.simplePaymentFee(feeID: feeID, amount: amount, taxable: taxable)
296+
let newBillingAddress = Address(firstName: "",
297+
lastName: "",
298+
company: nil,
299+
address1: "",
300+
address2: nil,
301+
city: "",
302+
state: "",
303+
postcode: "",
304+
country: "",
305+
phone: nil,
306+
email: email)
307+
308+
// Set new fields
309+
let updatedOrder = originalOrder.copy(orderID: orderID, customerNote: orderNote, billingAddress: newBillingAddress, fees: [newFee])
310+
let updateFields: [OrderUpdateField] = [.customerNote, .billingAddress, .fees]
311+
312+
updateOrder(siteID: siteID, order: updatedOrder, fields: updateFields, onCompletion: onCompletion)
313+
}
314+
270315
/// Creates a manual order with the provided order details.
271316
///
272317
func createOrder(siteID: Int64, order: Order, onCompletion: @escaping (Result<Order, Error>) -> Void) {

Yosemite/YosemiteTests/Stores/OrderStoreTests.swift

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,9 @@ final class OrderStoreTests: XCTestCase {
644644

645645
// Then
646646
let request = try XCTUnwrap(network.requestsForResponseData.last as? JetpackRequest)
647-
let received = try XCTUnwrap(request.parameters["fee_lines"] as? [[String: String]]).first
648-
let expected = [
647+
let received = try XCTUnwrap(request.parameters["fee_lines"] as? [[String: AnyHashable]]).first
648+
let expected: [String: AnyHashable] = [
649+
"id": 0,
649650
"name": "Simple Payments",
650651
"tax_status": "none",
651652
"tax_class": "",
@@ -665,8 +666,9 @@ final class OrderStoreTests: XCTestCase {
665666

666667
// Then
667668
let request = try XCTUnwrap(network.requestsForResponseData.last as? JetpackRequest)
668-
let received = try XCTUnwrap(request.parameters["fee_lines"] as? [[String: String]]).first
669-
let expected = [
669+
let received = try XCTUnwrap(request.parameters["fee_lines"] as? [[String: AnyHashable]]).first
670+
let expected: [String: AnyHashable] = [
671+
"id": 0,
670672
"name": "Simple Payments",
671673
"tax_status": "taxable",
672674
"tax_class": "",
@@ -710,6 +712,56 @@ final class OrderStoreTests: XCTestCase {
710712
// Then
711713
XCTAssertNotNil(storedOrder)
712714
}
715+
716+
func test_update_simple_payments_order_sends_correct_values() throws {
717+
// Given
718+
let feeID: Int64 = 1234
719+
let amount = "100.00"
720+
let taxable = true
721+
let note = "This is a note"
722+
let email = "[email protected]"
723+
724+
let store = OrderStore(dispatcher: dispatcher, storageManager: storageManager, network: network)
725+
network.simulateResponse(requestUrlSuffix: "orders/963", filename: "order")
726+
727+
// When
728+
let action = OrderAction.updateSimplePaymentsOrder(siteID: sampleSiteID,
729+
orderID: sampleOrderID,
730+
feeID: feeID,
731+
amount: amount,
732+
taxable: taxable,
733+
orderNote: note,
734+
email: email) { _ in }
735+
store.onAction(action)
736+
737+
// Then
738+
let request = try XCTUnwrap(network.requestsForResponseData.last as? JetpackRequest)
739+
let receivedFees = try XCTUnwrap(request.parameters["fee_lines"] as? [[String: AnyHashable]]).first
740+
let expectedFees: [String: AnyHashable] = [
741+
"id": 1234,
742+
"name": "Simple Payments",
743+
"tax_status": "taxable",
744+
"tax_class": "",
745+
"total": "100.00"
746+
]
747+
assertEqual(expectedFees, receivedFees)
748+
749+
let receivedBilling = try XCTUnwrap(request.parameters["billing"] as? [String: AnyHashable])
750+
let expectedBilling: [String: AnyHashable] = [
751+
"first_name": "",
752+
"last_name": "",
753+
"address_1": "",
754+
"city": "",
755+
"state": "",
756+
"postcode": "",
757+
"country": "",
758+
"email": email
759+
]
760+
assertEqual(receivedBilling, expectedBilling)
761+
762+
let receivedNote = try XCTUnwrap(request.parameters["customer_note"] as? String)
763+
assertEqual(receivedNote, note)
764+
}
713765
}
714766

715767

0 commit comments

Comments
 (0)