Skip to content

Commit 664d01e

Browse files
committed
Update order list and order details after sending receipt
1 parent dd60db1 commit 664d01e

13 files changed

+448
-1
lines changed

Modules/Tests/YosemiteTests/Mocks/MockPOSOrdersRemote.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,21 @@ final class MockPOSOrdersRemote: POSOrdersRemoteProtocol {
9191
throw error
9292
}
9393
}
94+
95+
var loadPOSOrderCalled = false
96+
var spyLoadPOSOrderID: Int64?
97+
var loadPOSOrderResult: Result<Order, Error> = .success(Order.fake())
98+
99+
func loadPOSOrder(siteID: Int64, orderID: Int64) async throws -> Order {
100+
loadPOSOrderCalled = true
101+
spySiteID = siteID
102+
spyLoadPOSOrderID = orderID
103+
104+
switch loadPOSOrderResult {
105+
case .success(let order):
106+
return order
107+
case .failure(let error):
108+
throw error
109+
}
110+
}
94111
}

WooCommerce/Classes/POS/Controllers/PointOfSaleOrderListController.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ protocol PointOfSaleOrderListControllerProtocol {
1616
func refreshOrders() async
1717
func loadNextOrders() async
1818
func selectOrder(_ order: POSOrder?)
19+
func updateOrder(orderID: Int64) async throws
1920
}
2021

2122
protocol PointOfSaleSearchingOrderListControllerProtocol: PointOfSaleOrderListControllerProtocol {
@@ -169,4 +170,21 @@ protocol PointOfSaleSearchingOrderListControllerProtocol: PointOfSaleOrderListCo
169170
}
170171
}
171172
}
173+
174+
@MainActor
175+
func updateOrder(orderID: Int64) async throws {
176+
let updatedOrder = try await fetchStrategy.loadOrder(orderID: orderID)
177+
let updatedOrders = ordersViewState.orders.map { order in
178+
order.id == orderID ? updatedOrder : order
179+
}
180+
181+
ordersViewState = ordersViewState.updatingOrders(with: updatedOrders)
182+
cachedOrders = cachedOrders.map { order in
183+
order.id == orderID ? updatedOrder : order
184+
}
185+
186+
if selectedOrder?.id == orderID {
187+
selectedOrder = updatedOrder
188+
}
189+
}
172190
}

WooCommerce/Classes/POS/Models/POSOrdersViewState.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,17 @@ enum POSOrderListState: Equatable {
4545
return []
4646
}
4747
}
48+
49+
func updatingOrders(with updatedOrders: [POSOrder]) -> POSOrderListState {
50+
switch self {
51+
case .loaded(_, let hasMoreItems):
52+
return .loaded(updatedOrders, hasMoreItems: hasMoreItems)
53+
case .loading:
54+
return .loading(updatedOrders)
55+
case .inlineError(_, let error, let context):
56+
return .inlineError(updatedOrders, error: error, context: context)
57+
case .empty, .error:
58+
return self
59+
}
60+
}
4861
}

WooCommerce/Classes/POS/Models/PointOfSaleOrderListModel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ import struct Yosemite.POSOrder
1414

1515
func sendReceipt(order: POSOrder, email: String) async throws {
1616
try await receiptSender.sendReceipt(orderID: order.id, recipientEmail: email)
17+
try await ordersController.updateOrder(orderID: order.id)
1718
}
1819
}

WooCommerce/Classes/POS/Utils/PreviewHelpers.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ final class PointOfSalePreviewOrderListController: PointOfSaleSearchingOrderList
381381
func loadNextOrders() async {}
382382
func refreshOrders() async {}
383383
func selectOrder(_ order: POSOrder?) {}
384+
func updateOrder(orderID: Int64) async throws {}
384385
func searchOrders(searchTerm: String) async {}
385386
func clearSearchOrders() {}
386387
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@
9898
01ADC1382C9AB6050036F7D2 /* PointOfSaleCardPresentPaymentIntentCreationErrorMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01ADC1372C9AB6050036F7D2 /* PointOfSaleCardPresentPaymentIntentCreationErrorMessageView.swift */; };
9999
01B3A1F22DB6D48800286B7F /* ItemListType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B3A1F12DB6D48800286B7F /* ItemListType.swift */; };
100100
01B744E22D2FCA1400AEB3F4 /* PushNotificationBackgroundSynchronizerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B744E12D2FCA1300AEB3F4 /* PushNotificationBackgroundSynchronizerFactory.swift */; };
101+
01B7AFBD2E707FB30004BE9D /* PointOfSaleOrderListModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7AFBB2E707FB30004BE9D /* PointOfSaleOrderListModelTests.swift */; };
102+
01B7AFBE2E707FB30004BE9D /* POSOrderListStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7AFBC2E707FB30004BE9D /* POSOrderListStateTests.swift */; };
103+
01B7AFC02E70801A0004BE9D /* MockPointOfSaleOrderListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B7AFBF2E7080180004BE9D /* MockPointOfSaleOrderListController.swift */; };
101104
01BB6C072D09DC560094D55B /* CardPresentModalLocationPreAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BB6C062D09DC470094D55B /* CardPresentModalLocationPreAlert.swift */; };
102105
01BB6C0A2D09E9630094D55B /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BB6C092D09E9630094D55B /* LocationService.swift */; };
103106
01BD77442C58CED400147191 /* PointOfSaleCardPresentPaymentProcessingMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BD77432C58CED400147191 /* PointOfSaleCardPresentPaymentProcessingMessageView.swift */; };
@@ -3319,6 +3322,9 @@
33193322
01ADC1372C9AB6050036F7D2 /* PointOfSaleCardPresentPaymentIntentCreationErrorMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentIntentCreationErrorMessageView.swift; sourceTree = "<group>"; };
33203323
01B3A1F12DB6D48800286B7F /* ItemListType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListType.swift; sourceTree = "<group>"; };
33213324
01B744E12D2FCA1300AEB3F4 /* PushNotificationBackgroundSynchronizerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationBackgroundSynchronizerFactory.swift; sourceTree = "<group>"; };
3325+
01B7AFBB2E707FB30004BE9D /* PointOfSaleOrderListModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleOrderListModelTests.swift; sourceTree = "<group>"; };
3326+
01B7AFBC2E707FB30004BE9D /* POSOrderListStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSOrderListStateTests.swift; sourceTree = "<group>"; };
3327+
01B7AFBF2E7080180004BE9D /* MockPointOfSaleOrderListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPointOfSaleOrderListController.swift; sourceTree = "<group>"; };
33223328
01BB6C062D09DC470094D55B /* CardPresentModalLocationPreAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalLocationPreAlert.swift; sourceTree = "<group>"; };
33233329
01BB6C092D09E9630094D55B /* LocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = "<group>"; };
33243330
01BD77432C58CED400147191 /* PointOfSaleCardPresentPaymentProcessingMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentProcessingMessageView.swift; sourceTree = "<group>"; };
@@ -7808,6 +7814,7 @@
78087814
02CD3BFC2C35D01600E575C4 /* Mocks */ = {
78097815
isa = PBXGroup;
78107816
children = (
7817+
01B7AFBF2E7080180004BE9D /* MockPointOfSaleOrderListController.swift */,
78117818
019460E12E70121A00FCB9AB /* MockPOSReceiptController.swift */,
78127819
012ACB812E5D8DCD00A49458 /* MockPointOfSaleOrderListFetchStrategyFactory.swift */,
78137820
012ACB792E5C84D200A49458 /* MockPointOfSaleOrderListService.swift */,
@@ -13193,6 +13200,8 @@
1319313200
DAD988C72C4A9D49009DE9E3 /* Models */ = {
1319413201
isa = PBXGroup;
1319513202
children = (
13203+
01B7AFBB2E707FB30004BE9D /* PointOfSaleOrderListModelTests.swift */,
13204+
01B7AFBC2E707FB30004BE9D /* POSOrderListStateTests.swift */,
1319613205
685A305E2E608F29001E667B /* POSSettingsStoreViewModelTests.swift */,
1319713206
20FCBCDE2CE241810082DCA3 /* PointOfSaleAggregateModelTests.swift */,
1319813207
);
@@ -17177,6 +17186,7 @@
1717717186
269098B627D2C09D001FEB07 /* ShippingInputTransformerTests.swift in Sources */,
1717817187
02BA128B24616B48008D8325 /* ProductFormActionsFactory+VisibilityTests.swift in Sources */,
1717917188
DEA88F522AAAC1180037273B /* AddEditProductCategoryViewModelTests.swift in Sources */,
17189+
01B7AFC02E70801A0004BE9D /* MockPointOfSaleOrderListController.swift in Sources */,
1718017190
DE2BF4FD2846192B00FBE68A /* CouponAllowedEmailsViewModelTests.swift in Sources */,
1718117191
FEEB2F6E268A2F7B0075A6E0 /* RoleEligibilityUseCaseTests.swift in Sources */,
1718217192
31E906A326CC91A70099A985 /* CardReaderConnectionControllerTests.swift in Sources */,
@@ -17406,6 +17416,8 @@
1740617416
45EF798624509B4C00B22BA2 /* ArrayIndexPathTests.swift in Sources */,
1740717417
D8610BDD256F5ABF00A5DF27 /* JetpackErrorViewModelTests.swift in Sources */,
1740817418
746791632108D7C0007CF1DC /* WooAnalyticsTests.swift in Sources */,
17419+
01B7AFBD2E707FB30004BE9D /* PointOfSaleOrderListModelTests.swift in Sources */,
17420+
01B7AFBE2E707FB30004BE9D /* POSOrderListStateTests.swift in Sources */,
1740917421
200BA15E2CF0A9EB0006DC5B /* PointOfSaleItemsControllerTests.swift in Sources */,
1741017422
2667BFDD252F61C5008099D4 /* RefundShippingDetailsViewModelTests.swift in Sources */,
1741117423
DE7B479727A3C4980018742E /* CouponDetailsViewModelTests.swift in Sources */,

WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleOrderListControllerTests.swift

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Foundation
44
import enum Yosemite.PointOfSaleOrderListServiceError
55
import struct NetworkingCore.Order
66
import Observation
7+
import struct Yosemite.POSOrder
78

89
final class PointOfSaleOrderListControllerTests {
910
private let orderListService = MockPointOfSaleOrderListService()
@@ -315,4 +316,84 @@ final class PointOfSaleOrderListControllerTests {
315316
#expect(orders == searchOrders)
316317
#expect(orderListService.lastSearchTerm == "test")
317318
}
319+
320+
@Test func updateOrder_when_order_loaded_from_API_then_order_list_updates() async throws {
321+
// Given - load initial orders
322+
let initialOrders = MockPointOfSaleOrderListService.makeInitialOrders()
323+
orderListService.orderPages = [initialOrders]
324+
await sut.loadOrders()
325+
326+
// Setup updated order
327+
let orderToUpdate = initialOrders[0]
328+
let updatedOrder = POSOrder(
329+
id: orderToUpdate.id,
330+
number: orderToUpdate.number,
331+
dateCreated: orderToUpdate.dateCreated,
332+
status: orderToUpdate.status,
333+
formattedTotal: orderToUpdate.formattedTotal,
334+
formattedSubtotal: orderToUpdate.formattedSubtotal,
335+
customerEmail: "[email protected]", // Updated email
336+
paymentMethodID: orderToUpdate.paymentMethodID,
337+
paymentMethodTitle: orderToUpdate.paymentMethodTitle,
338+
lineItems: orderToUpdate.lineItems,
339+
refunds: orderToUpdate.refunds,
340+
formattedTotalTax: orderToUpdate.formattedTotalTax,
341+
formattedDiscountTotal: orderToUpdate.formattedDiscountTotal,
342+
formattedPaymentTotal: orderToUpdate.formattedPaymentTotal,
343+
formattedNetAmount: orderToUpdate.formattedNetAmount
344+
)
345+
orderListService.loadOrderResult = updatedOrder
346+
347+
// When
348+
try await sut.updateOrder(orderID: orderToUpdate.id)
349+
350+
// Then
351+
guard case .loaded(let orders, _) = sut.ordersViewState else {
352+
Issue.record("Expected loaded state after update, but got \(sut.ordersViewState)")
353+
return
354+
}
355+
356+
let foundOrder = orders.first { $0.id == orderToUpdate.id }
357+
#expect(foundOrder != nil)
358+
#expect(foundOrder?.customerEmail == "[email protected]")
359+
#expect(orderListService.loadOrderWasCalled)
360+
#expect(orderListService.lastLoadOrderID == orderToUpdate.id)
361+
}
362+
363+
@Test func updateOrder_when_order_loaded_from_API_then_selected_order_updates() async throws {
364+
// Given
365+
let initialOrders = MockPointOfSaleOrderListService.makeInitialOrders()
366+
orderListService.orderPages = [initialOrders]
367+
await sut.loadOrders()
368+
369+
let orderToUpdate = initialOrders[0]
370+
await sut.selectOrder(orderToUpdate)
371+
#expect(sut.selectedOrder?.id == orderToUpdate.id)
372+
373+
// Setup updated order
374+
let updatedOrder = POSOrder(
375+
id: orderToUpdate.id,
376+
number: orderToUpdate.number,
377+
dateCreated: orderToUpdate.dateCreated,
378+
status: orderToUpdate.status,
379+
formattedTotal: orderToUpdate.formattedTotal,
380+
formattedSubtotal: orderToUpdate.formattedSubtotal,
381+
customerEmail: "[email protected]",
382+
paymentMethodID: orderToUpdate.paymentMethodID,
383+
paymentMethodTitle: orderToUpdate.paymentMethodTitle,
384+
lineItems: orderToUpdate.lineItems,
385+
refunds: orderToUpdate.refunds,
386+
formattedTotalTax: orderToUpdate.formattedTotalTax,
387+
formattedDiscountTotal: orderToUpdate.formattedDiscountTotal,
388+
formattedPaymentTotal: orderToUpdate.formattedPaymentTotal,
389+
formattedNetAmount: orderToUpdate.formattedNetAmount
390+
)
391+
orderListService.loadOrderResult = updatedOrder
392+
393+
// When
394+
try await sut.updateOrder(orderID: orderToUpdate.id)
395+
396+
// Then
397+
#expect(sut.selectedOrder?.customerEmail == "[email protected]")
398+
}
318399
}

WooCommerce/WooCommerceTests/POS/Mocks/MockPOSReceiptController.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ final class MockPOSReceiptSender: POSReceiptSending {
88
var sendReceiptCalledWithOrderID: Int64?
99
var sendReceiptCalledWithEmail: String?
1010

11+
enum TestError: Error {
12+
case sendReceiptFailed
13+
}
14+
1115
func sendReceipt(orderID: Int64, recipientEmail: String) async throws {
1216
sendReceiptWasCalled = true
1317
sendReceiptCalledWithOrderID = orderID
1418
sendReceiptCalledWithEmail = recipientEmail
15-
19+
1620
if let sendReceiptErrorToThrow {
1721
throw sendReceiptErrorToThrow
1822
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Foundation
2+
@testable import WooCommerce
3+
import struct Yosemite.POSOrder
4+
5+
final class MockPointOfSaleOrderListController: PointOfSaleSearchingOrderListControllerProtocol {
6+
var ordersViewState: POSOrderListState = .empty
7+
var selectedOrder: POSOrder?
8+
var updateOrderCalled = false
9+
var spyUpdateOrderID: Int64?
10+
var shouldThrowError = false
11+
12+
enum TestError: Error {
13+
case updateOrderFailed
14+
}
15+
16+
func loadOrders() async {}
17+
18+
func refreshOrders() async {}
19+
20+
func loadNextOrders() async {}
21+
22+
func selectOrder(_ order: POSOrder?) {
23+
selectedOrder = order
24+
}
25+
26+
func updateOrder(orderID: Int64) async throws {
27+
updateOrderCalled = true
28+
spyUpdateOrderID = orderID
29+
30+
if shouldThrowError {
31+
throw TestError.updateOrderFailed
32+
}
33+
}
34+
35+
func searchOrders(searchTerm: String) async {}
36+
37+
func clearSearchOrders() {}
38+
}

WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleOrderListFetchStrategyFactory.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ private struct MockPointOfSaleOrderListFetchStrategy: PointOfSaleOrderListFetchS
3232
func fetchOrders(pageNumber: Int) async throws -> PagedItems<POSOrder> {
3333
try await orderService.providePointOfSaleOrders(pageNumber: pageNumber)
3434
}
35+
36+
func loadOrder(orderID: Int64) async throws -> POSOrder {
37+
try await orderService.loadOrder(orderID: orderID)
38+
}
3539
}
3640

3741
private struct MockPointOfSaleOrderListSearchFetchStrategy: PointOfSaleOrderListFetchStrategy {
@@ -45,4 +49,8 @@ private struct MockPointOfSaleOrderListSearchFetchStrategy: PointOfSaleOrderList
4549
func fetchOrders(pageNumber: Int) async throws -> PagedItems<POSOrder> {
4650
try await orderService.searchPointOfSaleOrders(searchTerm: searchTerm, pageNumber: pageNumber)
4751
}
52+
53+
func loadOrder(orderID: Int64) async throws -> POSOrder {
54+
try await orderService.loadOrder(orderID: orderID)
55+
}
4856
}

0 commit comments

Comments
 (0)