Skip to content

Commit 7643f26

Browse files
committed
Update coupon lines on POSOrderService
1 parent 5e66974 commit 7643f26

File tree

2 files changed

+123
-6
lines changed

2 files changed

+123
-6
lines changed

Yosemite/Yosemite/Tools/POS/POSOrderService.swift

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ public final class POSOrderService: POSOrderServiceProtocol {
4141
let initialOrder: Order = posOrder ?? OrderFactory.newOrder(currency: currency)
4242
.copy(siteID: siteID,
4343
status: .autoDraft)
44-
let order = updateOrder(initialOrder, cartItems: cart.items).sanitizingLocalItems()
44+
let order = updateOrder(initialOrder, cart: cart).sanitizingLocalItems()
4545
let syncedOrder: Order
4646
if posOrder != nil {
47-
syncedOrder = try await ordersRemote.updatePOSOrder(siteID: siteID, order: order, fields: [.items])
47+
syncedOrder = try await ordersRemote.updatePOSOrder(siteID: siteID, order: order, fields: [.items, .couponLines])
4848
} else {
49-
syncedOrder = try await ordersRemote.createPOSOrder(siteID: siteID, order: order, fields: [.items, .status, .currency])
49+
syncedOrder = try await ordersRemote.createPOSOrder(siteID: siteID, order: order, fields: [.items, .status, .currency, .couponLines])
5050
}
5151
return syncedOrder
5252
}
@@ -82,17 +82,39 @@ private struct POSOrderSyncProductType: OrderSyncProductTypeProtocol, Hashable {
8282
}
8383

8484
private extension POSOrderService {
85-
func updateOrder(_ order: Order, cartItems: [POSCartItem]) -> Order {
85+
func updateOrder(_ order: Order, cart: POSCart) -> Order {
8686
// Removes all existing items by setting quantity to 0.
8787
let itemsToRemove = order.items.compactMap {
8888
Self.removalProductInput(item: $0)
8989
}
9090

9191
// Adds items from the latest cart grouping by item.
92-
let itemsToAdd = cartItems.createGroupedOrderSyncProductInputs().values
92+
let itemsToAdd = cart.items.createGroupedOrderSyncProductInputs().values
9393
let itemsToSync = itemsToRemove + itemsToAdd
9494

95-
return ProductInputTransformer.updateMultipleItems(with: itemsToSync, on: order, shouldUpdateOrDeleteZeroQuantities: .update)
95+
var order = ProductInputTransformer.updateMultipleItems(with: itemsToSync, on: order, shouldUpdateOrDeleteZeroQuantities: .update)
96+
order = updateCoupons(cart.coupons, on: order)
97+
98+
return order
99+
}
100+
101+
func updateCoupons(_ coupons: [POSCartCouponItem], on order: Order) -> Order {
102+
// Get coupon codes from cart
103+
let cartCouponCodes = Set(coupons.map { $0.code })
104+
105+
// Keep existing coupons that are still in the cart
106+
let remainingCoupons = order.coupons.filter { orderCoupon in
107+
cartCouponCodes.contains(orderCoupon.code)
108+
}
109+
110+
// Find new coupons that need to be added (in cart but not in order)
111+
let existingCouponCodes = Set(order.coupons.map { $0.code })
112+
let newCoupons = coupons
113+
.filter { !existingCouponCodes.contains($0.code) }
114+
.map { OrderFactory.newOrderCouponLine(code: $0.code) }
115+
116+
// Update order with remaining + new coupons
117+
return order.copy(coupons: remainingCoupons + newCoupons)
96118
}
97119

98120
/// Creates a new `OrderSyncProductInput` type meant to remove an existing item from `OrderSynchronizer`

Yosemite/YosemiteTests/Tools/POS/POSOrderServiceTests.swift

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,101 @@ struct POSOrderServiceTests {
135135
item.productID == 102 && item.quantity == 5
136136
}), "Item for product 102 should be added")
137137
}
138+
139+
@Test func syncOrder_after_adding_coupon_to_cart_adds_it_to_the_order() async throws {
140+
// Given
141+
let orderItems = [OrderItem.fake().copy(itemID: 1, name: "Item 1", productID: 100, quantity: 1)]
142+
let order = Order.fake().copy(siteID: 123, orderID: 456, items: orderItems, coupons: [])
143+
144+
// When
145+
let cart = POSCart(
146+
items: [makePOSCartItem(productID: 100, quantity: 1)],
147+
coupons: [.init(code: "SAVE10")]
148+
)
149+
_ = try await sut.syncOrder(cart: cart, order: order, currency: .USD)
150+
151+
// Then
152+
let updatedOrderCoupons = try #require(mockOrdersRemote.spyUpdatePOSOrder?.coupons)
153+
#expect(updatedOrderCoupons.count == 1)
154+
#expect(updatedOrderCoupons.first?.code == "SAVE10")
155+
}
156+
157+
@Test func syncOrder_after_removing_coupon_from_cart_removes_it_from_the_order() async throws {
158+
// Given
159+
let orderItems = [OrderItem.fake().copy(itemID: 1, name: "Item 1", productID: 100, quantity: 1)]
160+
let existingCoupons = [OrderCouponLine.fake().copy(code: "SAVE10")]
161+
let order = Order.fake().copy(siteID: 123, orderID: 456, items: orderItems, coupons: existingCoupons)
162+
163+
// When
164+
let cart = POSCart(
165+
items: [makePOSCartItem(productID: 100, quantity: 1)],
166+
coupons: [] // Empty coupons in cart
167+
)
168+
_ = try await sut.syncOrder(cart: cart, order: order, currency: .USD)
169+
170+
// Then
171+
let updatedOrderCoupons = try #require(mockOrdersRemote.spyUpdatePOSOrder?.coupons)
172+
#expect(updatedOrderCoupons.isEmpty)
173+
}
174+
175+
@Test func syncOrder_with_multiple_coupons_handles_mixed_changes() async throws {
176+
// Given
177+
let orderItems = [OrderItem.fake().copy(itemID: 1, name: "Item 1", productID: 100, quantity: 1)]
178+
let existingCoupons = [
179+
OrderCouponLine.fake().copy(code: "REMOVE1"),
180+
OrderCouponLine.fake().copy(code: "KEEP1"),
181+
OrderCouponLine.fake().copy(code: "REMOVE2")
182+
]
183+
let order = Order.fake().copy(siteID: 123, orderID: 456, items: orderItems, coupons: existingCoupons)
184+
185+
// When
186+
let cart = POSCart(
187+
items: [makePOSCartItem(productID: 100, quantity: 1)],
188+
coupons: [
189+
.init(code: "KEEP1"),
190+
.init(code: "NEW1"),
191+
.init(code: "NEW2")
192+
]
193+
)
194+
_ = try await sut.syncOrder(cart: cart, order: order, currency: .USD)
195+
196+
// Then
197+
let updatedOrderCoupons = try #require(mockOrdersRemote.spyUpdatePOSOrder?.coupons)
198+
199+
// Verify kept coupon
200+
#expect(updatedOrderCoupons.contains(where: { $0.code == "KEEP1" }))
201+
202+
// Verify removed coupons
203+
#expect(!updatedOrderCoupons.contains(where: { $0.code == "REMOVE1" }))
204+
#expect(!updatedOrderCoupons.contains(where: { $0.code == "REMOVE2" }))
205+
206+
// Verify new coupons
207+
#expect(updatedOrderCoupons.contains(where: { $0.code == "NEW1" }))
208+
#expect(updatedOrderCoupons.contains(where: { $0.code == "NEW2" }))
209+
210+
// Verify total count
211+
#expect(updatedOrderCoupons.count == 3)
212+
}
213+
214+
@Test func syncOrder_with_unchanged_coupons_preserves_existing_coupon_data() async throws {
215+
// Given
216+
let orderItems = [OrderItem.fake().copy(itemID: 1, name: "Item 1", productID: 100, quantity: 1)]
217+
let existingCoupon = OrderCouponLine.fake().copy(code: "KEEP1", discount: "10.00")
218+
let order = Order.fake().copy(siteID: 123, orderID: 456, items: orderItems, coupons: [existingCoupon])
219+
220+
// When
221+
let cart = POSCart(
222+
items: [makePOSCartItem(productID: 100, quantity: 1)],
223+
coupons: [.init(code: "KEEP1")] // Same coupon in cart
224+
)
225+
_ = try await sut.syncOrder(cart: cart, order: order, currency: .USD)
226+
227+
// Then
228+
let updatedOrderCoupons = try #require(mockOrdersRemote.spyUpdatePOSOrder?.coupons)
229+
#expect(updatedOrderCoupons.count == 1)
230+
#expect(updatedOrderCoupons.first?.code == "KEEP1")
231+
#expect(updatedOrderCoupons.first?.discount == "10.00", "Existing coupon data should be preserved")
232+
}
138233
}
139234

140235
private func makePOSCartItem(

0 commit comments

Comments
 (0)