|
@@ -55,6 +55,7 @@ class MultiStoreCheckoutBatchServiceTest {
|
|
|
private final StringRedisTemplate orderNumbers = mock(StringRedisTemplate.class);
|
|
private final StringRedisTemplate orderNumbers = mock(StringRedisTemplate.class);
|
|
|
private final OrderNumberReservationService numberReservations = mock(OrderNumberReservationService.class);
|
|
private final OrderNumberReservationService numberReservations = mock(OrderNumberReservationService.class);
|
|
|
private final MultiStoreCheckoutActivationTrigger activationTrigger = mock(MultiStoreCheckoutActivationTrigger.class);
|
|
private final MultiStoreCheckoutActivationTrigger activationTrigger = mock(MultiStoreCheckoutActivationTrigger.class);
|
|
|
|
|
+ private final OrderInvoiceService invoices = mock(OrderInvoiceService.class);
|
|
|
|
|
|
|
|
@BeforeEach
|
|
@BeforeEach
|
|
|
void setUp() {
|
|
void setUp() {
|
|
@@ -72,7 +73,7 @@ class MultiStoreCheckoutBatchServiceTest {
|
|
|
|
|
|
|
|
private MultiStoreCheckoutBatchService subject() {
|
|
private MultiStoreCheckoutBatchService subject() {
|
|
|
return new MultiStoreCheckoutBatchService(quotes, config, checkout, parents, orders,
|
|
return new MultiStoreCheckoutBatchService(quotes, config, checkout, parents, orders,
|
|
|
- addresses, stores, promotions, orderNumbers, numberReservations, activationTrigger);
|
|
|
|
|
|
|
+ addresses, stores, promotions, orderNumbers, numberReservations, activationTrigger, invoices);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
@Test
|
|
@@ -112,6 +113,11 @@ class MultiStoreCheckoutBatchServiceTest {
|
|
|
"retry-1", "quote-1", List.of(1L, 2L), List.of(
|
|
"retry-1", "quote-1", List.of(1L, 2L), List.of(
|
|
|
new MultiStoreBatchCreateRequest.PaymentMethod(1L, "2"),
|
|
new MultiStoreBatchCreateRequest.PaymentMethod(1L, "2"),
|
|
|
new MultiStoreBatchCreateRequest.PaymentMethod(2L, "1"))), 42L));
|
|
new MultiStoreBatchCreateRequest.PaymentMethod(2L, "1"))), 42L));
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> subject().create(new MultiStoreBatchCreateRequest(
|
|
|
|
|
+ "retry-1", "quote-1", List.of(1L, 2L), List.of(
|
|
|
|
|
+ new MultiStoreBatchCreateRequest.PaymentMethod(1L, "3"),
|
|
|
|
|
+ new MultiStoreBatchCreateRequest.PaymentMethod(2L, "1")),
|
|
|
|
|
+ "PAPER", null, null, null, null, null, null, false), 42L));
|
|
|
verify(checkout, never()).insertOrGetBatch(any());
|
|
verify(checkout, never()).insertOrGetBatch(any());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -125,41 +131,7 @@ class MultiStoreCheckoutBatchServiceTest {
|
|
|
when(numberReservations.reserveIfUnused(org.mockito.ArgumentMatchers.eq("991789716730511"), anyString()))
|
|
when(numberReservations.reserveIfUnused(org.mockito.ArgumentMatchers.eq("991789716730511"), anyString()))
|
|
|
.thenReturn(false);
|
|
.thenReturn(false);
|
|
|
}
|
|
}
|
|
|
- arrangeQuote();
|
|
|
|
|
- when(config.selectCurrentForUpdate()).thenReturn(policy());
|
|
|
|
|
- InfoAddress address = new InfoAddress();
|
|
|
|
|
- address.setId(10L); address.setUserId(42L);
|
|
|
|
|
- address.setLatitude(new BigDecimal("22.6000")); address.setLongitude(new BigDecimal("120.3000"));
|
|
|
|
|
- when(addresses.selectById(10L)).thenReturn(address);
|
|
|
|
|
- when(stores.selectById(1L)).thenReturn(store(1));
|
|
|
|
|
- when(stores.selectById(2L)).thenReturn(store(2));
|
|
|
|
|
- PromotionCalcResponse food = new PromotionCalcResponse();
|
|
|
|
|
- food.setOriginalAmount(new BigDecimal("50")); food.setFinalAmount(new BigDecimal("50"));
|
|
|
|
|
- food.setDeliveryFee(new BigDecimal("36"));
|
|
|
|
|
- food.setDeliveryDiscountAmount(BigDecimal.ZERO);
|
|
|
|
|
- food.setCustomerDeliveryFee(new BigDecimal("36"));
|
|
|
|
|
- food.setPayableAmount(new BigDecimal("86"));
|
|
|
|
|
- when(quotes.priceFoodForBatch(any(), any(), any())).thenReturn(food);
|
|
|
|
|
- doAnswer(call -> {
|
|
|
|
|
- PosOrder order = call.getArgument(0);
|
|
|
|
|
- PromotionCalcResponse calculated = call.getArgument(1);
|
|
|
|
|
- order.setAmount(calculated.getPayableAmount().intValueExact());
|
|
|
|
|
- order.setFreight(calculated.getDeliveryFee().doubleValue());
|
|
|
|
|
- order.setFoodAmount(calculated.getOriginalAmount().intValueExact());
|
|
|
|
|
- order.setPromotionPricingVersion(2);
|
|
|
|
|
- order.setMerchantDeliverySubsidy(calculated.getDeliveryDiscountAmount());
|
|
|
|
|
- order.setCustomerDeliveryFee(calculated.getCustomerDeliveryFee());
|
|
|
|
|
- return null;
|
|
|
|
|
- }).when(promotions).applyVersion2Pricing(any(PosOrder.class), any(PromotionCalcResponse.class));
|
|
|
|
|
- when(promotions.buildServerFoodJson(any())).thenReturn("[]");
|
|
|
|
|
- when(parents.insert(any(OrderParent.class))).thenReturn(1);
|
|
|
|
|
- when(checkout.insertMember(any())).thenReturn(1);
|
|
|
|
|
- when(checkout.insertGroup(any())).thenReturn(1);
|
|
|
|
|
- when(checkout.activateIfPending(org.mockito.ArgumentMatchers.eq(99L), any())).thenReturn(1);
|
|
|
|
|
- doAnswer(call -> { ((OrderCheckoutBatch) call.getArgument(0)).setId(99L); return 1; })
|
|
|
|
|
- .when(checkout).insertOrGetBatch(any());
|
|
|
|
|
- doAnswer(call -> { ((PosOrder) call.getArgument(0)).setId(100L); return 1; })
|
|
|
|
|
- .when(orders).insert(any(PosOrder.class));
|
|
|
|
|
|
|
+ arrangeSuccessfulBatch();
|
|
|
|
|
|
|
|
var request = new com.fasterxml.jackson.databind.ObjectMapper().readValue("""
|
|
var request = new com.fasterxml.jackson.databind.ObjectMapper().readValue("""
|
|
|
{"requestKey":"retry-1","quoteToken":"quote-1","participatingStoreIds":[1,2],
|
|
{"requestKey":"retry-1","quoteToken":"quote-1","participatingStoreIds":[1,2],
|
|
@@ -219,6 +191,85 @@ class MultiStoreCheckoutBatchServiceTest {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void rejectsElectronicInvoiceWhenNoParticipatingStoreCanInvoice() throws Exception {
|
|
|
|
|
+ arrangeSuccessfulBatch();
|
|
|
|
|
+ var request = new com.fasterxml.jackson.databind.ObjectMapper().readValue("""
|
|
|
|
|
+ {"requestKey":"retry-invoice","quoteToken":"quote-1","participatingStoreIds":[1,2],
|
|
|
|
|
+ "paymentMethods":[{"storeId":1,"payType":"3"},{"storeId":2,"payType":"1"}],
|
|
|
|
|
+ "invoiceChoice":"COMPANY","buyerUbn":"12345678","buyerEmail":"a@example.com"}
|
|
|
|
|
+ """, MultiStoreBatchCreateRequest.class);
|
|
|
|
|
+
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> subject().create(request, 42L));
|
|
|
|
|
+ verify(checkout, never()).insertOrGetBatch(any());
|
|
|
|
|
+ verify(parents, never()).insert(any(OrderParent.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void capturesSharedInvoiceOnlyForCapableMemberAndKeepsDefaultProfileChoice() throws Exception {
|
|
|
|
|
+ arrangeSuccessfulBatch();
|
|
|
|
|
+ when(invoices.canInvoice(1L)).thenReturn(true);
|
|
|
|
|
+ var request = new com.fasterxml.jackson.databind.ObjectMapper().readValue("""
|
|
|
|
|
+ {"requestKey":"retry-invoice","quoteToken":"quote-1","participatingStoreIds":[1,2],
|
|
|
|
|
+ "paymentMethods":[{"storeId":1,"payType":"3"},{"storeId":2,"payType":"1"}],
|
|
|
|
|
+ "invoiceChoice":"COMPANY","buyerUbn":"12345678","buyerName":"甲公司",
|
|
|
|
|
+ "buyerEmail":"a@example.com","saveAsDefault":true}
|
|
|
|
|
+ """, MultiStoreBatchCreateRequest.class);
|
|
|
|
|
+
|
|
|
|
|
+ subject().create(request, 42L);
|
|
|
|
|
+
|
|
|
|
|
+ verify(invoices).validateCheckoutInvoiceIntent("COMPANY", null, null,
|
|
|
|
|
+ "12345678", "a@example.com", 1L);
|
|
|
|
|
+ var captured = org.mockito.ArgumentCaptor.forClass(com.ruoyi.system.domain.PosOrderInvoice.class);
|
|
|
|
|
+ verify(invoices).captureInvoiceIntent(captured.capture());
|
|
|
|
|
+ assertEquals(100L, captured.getValue().getOrderId());
|
|
|
|
|
+ assertEquals(1L, captured.getValue().getStoreId());
|
|
|
|
|
+ assertEquals("COMPANY", captured.getValue().getInvoiceChoice());
|
|
|
|
|
+ assertEquals("12345678", captured.getValue().getBuyerUbn());
|
|
|
|
|
+ assertEquals("甲公司", captured.getValue().getBuyerName());
|
|
|
|
|
+ assertEquals("a@example.com", captured.getValue().getBuyerEmail());
|
|
|
|
|
+ verify(invoices).saveDefaultInvoiceProfile("COMPANY", null, "12345678", "甲公司", 42L);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void arrangeSuccessfulBatch() {
|
|
|
|
|
+ arrangeQuote();
|
|
|
|
|
+ when(config.selectCurrentForUpdate()).thenReturn(policy());
|
|
|
|
|
+ InfoAddress address = new InfoAddress();
|
|
|
|
|
+ address.setId(10L); address.setUserId(42L);
|
|
|
|
|
+ address.setLatitude(new BigDecimal("22.6000")); address.setLongitude(new BigDecimal("120.3000"));
|
|
|
|
|
+ when(addresses.selectById(10L)).thenReturn(address);
|
|
|
|
|
+ when(stores.selectById(1L)).thenReturn(store(1));
|
|
|
|
|
+ when(stores.selectById(2L)).thenReturn(store(2));
|
|
|
|
|
+ PromotionCalcResponse food = new PromotionCalcResponse();
|
|
|
|
|
+ food.setOriginalAmount(new BigDecimal("50")); food.setFinalAmount(new BigDecimal("50"));
|
|
|
|
|
+ food.setDeliveryFee(new BigDecimal("36"));
|
|
|
|
|
+ food.setDeliveryDiscountAmount(BigDecimal.ZERO);
|
|
|
|
|
+ food.setCustomerDeliveryFee(new BigDecimal("36"));
|
|
|
|
|
+ food.setPayableAmount(new BigDecimal("86"));
|
|
|
|
|
+ when(quotes.priceFoodForBatch(any(), any(), any())).thenReturn(food);
|
|
|
|
|
+ doAnswer(call -> {
|
|
|
|
|
+ PosOrder order = call.getArgument(0);
|
|
|
|
|
+ PromotionCalcResponse calculated = call.getArgument(1);
|
|
|
|
|
+ order.setAmount(calculated.getPayableAmount().intValueExact());
|
|
|
|
|
+ order.setFreight(calculated.getDeliveryFee().doubleValue());
|
|
|
|
|
+ order.setFoodAmount(calculated.getOriginalAmount().intValueExact());
|
|
|
|
|
+ order.setPromotionPricingVersion(2);
|
|
|
|
|
+ order.setMerchantDeliverySubsidy(calculated.getDeliveryDiscountAmount());
|
|
|
|
|
+ order.setCustomerDeliveryFee(calculated.getCustomerDeliveryFee());
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }).when(promotions).applyVersion2Pricing(any(PosOrder.class), any(PromotionCalcResponse.class));
|
|
|
|
|
+ when(promotions.buildServerFoodJson(any())).thenReturn("[]");
|
|
|
|
|
+ when(parents.insert(any(OrderParent.class))).thenReturn(1);
|
|
|
|
|
+ when(checkout.insertMember(any())).thenReturn(1);
|
|
|
|
|
+ when(checkout.insertGroup(any())).thenReturn(1);
|
|
|
|
|
+ when(checkout.activateIfPending(org.mockito.ArgumentMatchers.eq(99L), any())).thenReturn(1);
|
|
|
|
|
+ doAnswer(call -> { ((OrderCheckoutBatch) call.getArgument(0)).setId(99L); return 1; })
|
|
|
|
|
+ .when(checkout).insertOrGetBatch(any());
|
|
|
|
|
+ doAnswer(call -> { ((PosOrder) call.getArgument(0)).setId(100L); return 1; })
|
|
|
|
|
+ .when(orders).insert(any(PosOrder.class));
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Test
|
|
@Test
|
|
|
void retryReturnsCommittedBatchWithoutRequotingOrCreatingAnotherParent() {
|
|
void retryReturnsCommittedBatchWithoutRequotingOrCreatingAnotherParent() {
|
|
|
OrderCheckoutBatch existing = new OrderCheckoutBatch();
|
|
OrderCheckoutBatch existing = new OrderCheckoutBatch();
|