diff --git a/order/src/main/java/com/yas/order/mapper/CheckoutMapper.java b/order/src/main/java/com/yas/order/mapper/CheckoutMapper.java index b8402b9bd5..76908564b1 100644 --- a/order/src/main/java/com/yas/order/mapper/CheckoutMapper.java +++ b/order/src/main/java/com/yas/order/mapper/CheckoutMapper.java @@ -14,17 +14,15 @@ @Component public interface CheckoutMapper { @Mapping(target = "id", ignore = true) - @Mapping(target = "checkoutId", ignore = true) + @Mapping(target = "checkout", ignore = true) CheckoutItem toModel(CheckoutItemPostVm checkoutItemPostVm); @Mapping(target = "id", ignore = true) @Mapping(target = "checkoutState", ignore = true) - @Mapping(target = "checkoutItem", source = "checkoutItemPostVms") Checkout toModel(CheckoutPostVm checkoutPostVm); - @Mapping(target = "checkoutId", source = "checkoutId.id") CheckoutItemVm toVm(CheckoutItem checkoutItem); - @Mapping(target = "checkoutItemVms", source = "checkoutItem") + @Mapping(target = "checkoutItemVms", ignore = true) CheckoutVm toVm(Checkout checkout); } diff --git a/order/src/main/java/com/yas/order/model/Checkout.java b/order/src/main/java/com/yas/order/model/Checkout.java index 8b99b873e1..9bd240350a 100644 --- a/order/src/main/java/com/yas/order/model/Checkout.java +++ b/order/src/main/java/com/yas/order/model/Checkout.java @@ -1,20 +1,20 @@ package com.yas.order.model; import com.yas.order.model.enumeration.CheckoutState; -import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; import jakarta.persistence.Table; -import java.util.Set; +import java.math.BigDecimal; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; @Entity @Table(name = "checkout") @@ -24,15 +24,59 @@ @AllArgsConstructor @Builder public class Checkout extends AbstractAuditEntity { + @Id private String id; + private String email; + private String note; + + @Column(name = "promotion_code") private String couponCode; + + @Column(name = "status") @Enumerated(EnumType.STRING) private CheckoutState checkoutState; - @OneToMany(mappedBy = "checkoutId", fetch = FetchType.EAGER, cascade = CascadeType.ALL) - private Set checkoutItem; + @SuppressWarnings("unused") + private String progress; + + @SuppressWarnings("unused") + private Long customerId; + + @SuppressWarnings("unused") + private String shipmentMethodId; + + @SuppressWarnings("unused") + private String paymentMethodId; + + @SuppressWarnings("unused") + private Long shippingAddressId; + + @SuppressWarnings("unused") + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "last_error", columnDefinition = "jsonb") + private String lastError; + + @SuppressWarnings("unused") + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "attributes", columnDefinition = "jsonb") + private String attributes; + + @SuppressWarnings("unused") + private BigDecimal totalAmount; + + @SuppressWarnings("unused") + private BigDecimal totalShipmentFee; + + @SuppressWarnings("unused") + private BigDecimal totalShipmentTax; + + @SuppressWarnings("unused") + private BigDecimal totalTax; + + @SuppressWarnings("unused") + private BigDecimal totalDiscountAmount; } \ No newline at end of file diff --git a/order/src/main/java/com/yas/order/model/CheckoutItem.java b/order/src/main/java/com/yas/order/model/CheckoutItem.java index fd20d470b4..c92ae8ef29 100644 --- a/order/src/main/java/com/yas/order/model/CheckoutItem.java +++ b/order/src/main/java/com/yas/order/model/CheckoutItem.java @@ -1,5 +1,6 @@ package com.yas.order.model; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -22,19 +23,43 @@ @Builder @AllArgsConstructor @NoArgsConstructor -public class CheckoutItem { +public class CheckoutItem extends AbstractAuditEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private Long productId; + + @Column(name = "checkout_id") + private String checkoutId; + + @Column(name = "name") private String productName; + private int quantity; + + @Column(name = "price") private BigDecimal productPrice; + + @Column(name = "description") private String note; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "checkout_id", insertable = false, updatable = false) + private Checkout checkout; + private BigDecimal discountAmount; + + @Column(name = "tax") private BigDecimal taxAmount; + private BigDecimal taxPercent; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "checkoutId", referencedColumnName = "id") - private Checkout checkoutId; + + @SuppressWarnings("unused") + private BigDecimal shipmentTax; + + @SuppressWarnings("unused") + private BigDecimal shipmentFee; + } diff --git a/order/src/main/java/com/yas/order/model/Order.java b/order/src/main/java/com/yas/order/model/Order.java index 4bfcfb7cd9..18390c8918 100644 --- a/order/src/main/java/com/yas/order/model/Order.java +++ b/order/src/main/java/com/yas/order/model/Order.java @@ -6,6 +6,7 @@ import com.yas.order.model.enumeration.OrderStatus; import com.yas.order.model.enumeration.PaymentStatus; import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -24,6 +25,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; @Entity @Table(name = "`order`") @@ -33,35 +36,84 @@ @AllArgsConstructor @Builder public class Order extends AbstractAuditEntity { + @OneToMany(mappedBy = "orderId", fetch = FetchType.EAGER, cascade = CascadeType.ALL) Set orderItems; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String email; + @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "shipping_address_id", referencedColumnName = "id") private OrderAddress shippingAddressId; + @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "billing_address_id", referencedColumnName = "id") private OrderAddress billingAddressId; + private String note; + + @Column(name = "total_tax") private float tax; + + @Column(name = "total_discount_amount") private float discount; + private int numberItem; + + @Column(name = "promotion_code") private String couponCode; + + @Column(name = "total_amount") private BigDecimal totalPrice; + + @Column(name = "total_shipment_fee") private BigDecimal deliveryFee; @Enumerated(EnumType.STRING) + + @Column(name = "status") private OrderStatus orderStatus; + + @Column(name = "shipment_method_id") @Enumerated(EnumType.STRING) private DeliveryMethod deliveryMethod; + + @Column(name = "shipment_status") @Enumerated(EnumType.STRING) private DeliveryStatus deliveryStatus; + @Enumerated(EnumType.STRING) private PaymentStatus paymentStatus; + private Long paymentId; + private String checkoutId; + private String rejectReason; + @SuppressWarnings("unused") + private String paymentMethodId; + + @SuppressWarnings("unused") + private String progress; + + @SuppressWarnings("unused") + private Long customerId; + + @SuppressWarnings("unused") + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "last_error", columnDefinition = "jsonb") + private String lastError; + + @SuppressWarnings("unused") + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "attributes", columnDefinition = "jsonb") + private String attributes; + + @SuppressWarnings("unused") + private BigDecimal totalShipmentTax; + } \ No newline at end of file diff --git a/order/src/main/java/com/yas/order/model/OrderItem.java b/order/src/main/java/com/yas/order/model/OrderItem.java index 78f0e8239a..b2e6eacd9e 100644 --- a/order/src/main/java/com/yas/order/model/OrderItem.java +++ b/order/src/main/java/com/yas/order/model/OrderItem.java @@ -1,5 +1,6 @@ package com.yas.order.model; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -14,6 +15,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; @Entity @Table(name = "order_item") @@ -22,19 +25,45 @@ @Builder @AllArgsConstructor @NoArgsConstructor -public class OrderItem { +public class OrderItem extends AbstractAuditEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private Long productId; + + @Column(name = "name") private String productName; + private int quantity; + + @Column(name = "price") private BigDecimal productPrice; + + @Column(name = "description") private String note; + private BigDecimal discountAmount; + private BigDecimal taxAmount; + private BigDecimal taxPercent; + @SuppressWarnings("unused") + private BigDecimal shipmentFee; + + @SuppressWarnings("unused") + private String status; + + @SuppressWarnings("unused") + private BigDecimal shipmentTax; + + @SuppressWarnings("unused") + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "processing_state", columnDefinition = "jsonb") + private String processingState; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "orderId", referencedColumnName = "id") private Order orderId; diff --git a/order/src/main/java/com/yas/order/repository/CheckoutItemRepository.java b/order/src/main/java/com/yas/order/repository/CheckoutItemRepository.java index d8d008d1c7..639d302802 100644 --- a/order/src/main/java/com/yas/order/repository/CheckoutItemRepository.java +++ b/order/src/main/java/com/yas/order/repository/CheckoutItemRepository.java @@ -1,9 +1,12 @@ package com.yas.order.repository; import com.yas.order.model.CheckoutItem; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface CheckoutItemRepository extends JpaRepository { + + List findAllByCheckoutId(String checkoutId); } diff --git a/order/src/main/java/com/yas/order/service/CheckoutService.java b/order/src/main/java/com/yas/order/service/CheckoutService.java index 8de0a81a1c..d5fcc1897c 100644 --- a/order/src/main/java/com/yas/order/service/CheckoutService.java +++ b/order/src/main/java/com/yas/order/service/CheckoutService.java @@ -6,19 +6,26 @@ import com.yas.commonlibrary.exception.NotFoundException; import com.yas.order.mapper.CheckoutMapper; import com.yas.order.model.Checkout; +import com.yas.order.model.CheckoutItem; import com.yas.order.model.Order; import com.yas.order.model.enumeration.CheckoutState; +import com.yas.order.repository.CheckoutItemRepository; import com.yas.order.repository.CheckoutRepository; import com.yas.order.utils.AuthenticationUtils; import com.yas.order.utils.Constants; +import com.yas.order.viewmodel.checkout.CheckoutItemVm; import com.yas.order.viewmodel.checkout.CheckoutPostVm; import com.yas.order.viewmodel.checkout.CheckoutStatusPutVm; import com.yas.order.viewmodel.checkout.CheckoutVm; +import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; @Slf4j @Service @@ -26,25 +33,64 @@ @RequiredArgsConstructor public class CheckoutService { private final CheckoutRepository checkoutRepository; + private final CheckoutItemRepository checkoutItemRepository; private final OrderService orderService; private final CheckoutMapper checkoutMapper; public CheckoutVm createCheckout(CheckoutPostVm checkoutPostVm) { + Checkout checkout = checkoutMapper.toModel(checkoutPostVm); checkout.setCheckoutState(CheckoutState.PENDING); checkout.setId(UUID.randomUUID().toString()); - checkout.getCheckoutItem().forEach(item -> item.setCheckoutId(checkout)); - return checkoutMapper.toVm(checkoutRepository.save(checkout)); + + Checkout savedCheckout = checkoutRepository.save(checkout); + + CheckoutVm checkoutVm = checkoutMapper.toVm(savedCheckout); + if (CollectionUtils.isEmpty(checkoutPostVm.checkoutItemPostVms())) { + return checkoutVm; + } + + List checkoutItemList = checkoutPostVm.checkoutItemPostVms() + .stream() + .map(checkoutItemPostVm -> { + CheckoutItem item = checkoutMapper.toModel(checkoutItemPostVm); + item.setCheckoutId(savedCheckout.getId()); + return item; + }) + .toList(); + + List savedCheckoutItems = checkoutItemRepository.saveAll(checkoutItemList); + + Set checkoutItemVms = savedCheckoutItems + .stream() + .map(checkoutMapper::toVm) + .collect(Collectors.toSet()); + + return checkoutVm.toBuilder().checkoutItemVms(checkoutItemVms).build(); } public CheckoutVm getCheckoutPendingStateWithItemsById(String id) { + Checkout checkout = checkoutRepository.findByIdAndCheckoutState(id, CheckoutState.PENDING).orElseThrow(() -> new NotFoundException(CHECKOUT_NOT_FOUND, id)); if (!checkout.getCreatedBy().equals(AuthenticationUtils.getCurrentUserId())) { throw new Forbidden(Constants.ErrorCode.FORBIDDEN); } - return checkoutMapper.toVm(checkout); + + CheckoutVm checkoutVm = checkoutMapper.toVm(checkout); + + List checkoutItems = checkoutItemRepository.findAllByCheckoutId(checkout.getId()); + if (CollectionUtils.isEmpty(checkoutItems)) { + return checkoutVm; + } + + Set checkoutItemVms = checkoutItems + .stream() + .map(checkoutMapper::toVm) + .collect(Collectors.toSet()); + + return checkoutVm.toBuilder().checkoutItemVms(checkoutItemVms).build(); } public Long updateCheckoutStatus(CheckoutStatusPutVm checkoutStatusPutVm) { diff --git a/order/src/main/java/com/yas/order/viewmodel/checkout/CheckoutItemPostVm.java b/order/src/main/java/com/yas/order/viewmodel/checkout/CheckoutItemPostVm.java index 2f6926ecd0..934fac4578 100644 --- a/order/src/main/java/com/yas/order/viewmodel/checkout/CheckoutItemPostVm.java +++ b/order/src/main/java/com/yas/order/viewmodel/checkout/CheckoutItemPostVm.java @@ -10,6 +10,8 @@ public record CheckoutItemPostVm( String note, BigDecimal discountAmount, BigDecimal taxAmount, - BigDecimal taxPercent + BigDecimal taxPercent, + BigDecimal shipmentFee, + BigDecimal shipmentTax ) { } diff --git a/order/src/main/resources/db/changelog/ddl/changelog-0015.sql b/order/src/main/resources/db/changelog/ddl/changelog-0015.sql new file mode 100644 index 0000000000..3395ad0f8e --- /dev/null +++ b/order/src/main/resources/db/changelog/ddl/changelog-0015.sql @@ -0,0 +1,81 @@ +ALTER TABLE IF EXISTS "checkout" +ADD COLUMN progress VARCHAR(255), +ADD COLUMN customer_id BIGSERIAL, +ADD COLUMN shipment_method_id VARCHAR(255), +ADD COLUMN payment_method_id VARCHAR(255), +ADD COLUMN shipping_address_id BIGSERIAL, +ADD COLUMN last_error JSONB, +ADD COLUMN attributes JSONB, +ADD COLUMN total_amount DECIMAL(19,2), +ADD COLUMN total_shipment_fee DECIMAL(19,2), +ADD COLUMN total_shipment_tax DECIMAL(19,2), +ADD COLUMN total_tax DECIMAL(19,2), +ADD COLUMN total_discount_amount DECIMAL(19,2); +ALTER TABLE "checkout" +RENAME COLUMN coupon_code TO promotion_code; +ALTER TABLE "checkout" +RENAME COLUMN checkout_state TO status; + +ALTER TABLE IF EXISTS "order" +ADD COLUMN progress VARCHAR(255), +ADD COLUMN customer_id BIGSERIAL, +ADD COLUMN payment_method_id VARCHAR(255), +ADD COLUMN last_error JSONB, +ADD COLUMN attributes JSONB, +ADD COLUMN total_shipment_tax DECIMAL(19,2); +ALTER TABLE IF EXISTS "order" +RENAME COLUMN tax TO total_tax; +ALTER TABLE IF EXISTS "order" +RENAME COLUMN discount TO total_discount_amount; +ALTER TABLE IF EXISTS "order" +RENAME COLUMN total_price TO total_amount; +ALTER TABLE IF EXISTS "order" +RENAME COLUMN delivery_fee TO total_shipment_fee; +ALTER TABLE IF EXISTS "order" +RENAME COLUMN delivery_method TO shipment_method_id; +ALTER TABLE IF EXISTS "order" +RENAME COLUMN delivery_status TO shipment_status; +ALTER TABLE IF EXISTS "order" +RENAME COLUMN order_status TO status; +ALTER TABLE IF EXISTS "order" +RENAME COLUMN coupon_code TO promotion_code; + +ALTER TABLE IF EXISTS "checkout_item" +ADD COLUMN created_by varchar(255), +ADD COLUMN created_on timestamp(6), +ADD COLUMN last_modified_by varchar(255), +ADD COLUMN last_modified_on timestamp(6), +ADD COLUMN shipment_fee DECIMAL(19,2), +ADD COLUMN shipment_tax DECIMAL(19,2); +ALTER TABLE IF EXISTS "checkout_item" +RENAME COLUMN tax_amount TO tax; +ALTER TABLE IF EXISTS "checkout_item" +RENAME COLUMN product_name TO name; +ALTER TABLE IF EXISTS "checkout_item" +RENAME COLUMN product_price TO price; +ALTER TABLE IF EXISTS "checkout_item" +RENAME COLUMN note TO description; + +ALTER TABLE IF EXISTS "order_item" +ADD COLUMN created_by varchar(255), +ADD COLUMN created_on timestamp(6), +ADD COLUMN last_modified_by varchar(255), +ADD COLUMN last_modified_on timestamp(6), +ADD COLUMN shipment_fee DECIMAL(19,2), +ADD COLUMN shipment_tax DECIMAL(19,2), +ADD COLUMN status varchar(255), +ADD COLUMN processing_state JSONB; +ALTER TABLE IF EXISTS "order_item" +RENAME COLUMN note TO description; +ALTER TABLE IF EXISTS "order_item" +RENAME COLUMN product_name TO name; +ALTER TABLE IF EXISTS "order_item" +RENAME COLUMN product_price TO price; + +ALTER TABLE "checkout" +ALTER COLUMN customer_id DROP NOT NULL; +ALTER TABLE "checkout" +ALTER COLUMN shipping_address_id DROP NOT NULL; + +ALTER TABLE "order" +ALTER COLUMN customer_id DROP NOT NULL; \ No newline at end of file diff --git a/order/src/test/java/com/yas/order/controller/CheckoutControllerTest.java b/order/src/test/java/com/yas/order/controller/CheckoutControllerTest.java index dd52327d34..0fd13b5140 100644 --- a/order/src/test/java/com/yas/order/controller/CheckoutControllerTest.java +++ b/order/src/test/java/com/yas/order/controller/CheckoutControllerTest.java @@ -112,6 +112,8 @@ void testGetOrderWithItemsById_whenRequestIsValid_thenReturnCheckoutVm() throws "First item note", new BigDecimal("5.00"), new BigDecimal("2.50"), + new BigDecimal("8.5"), + new BigDecimal("8.5"), new BigDecimal("8.5") ); @@ -123,7 +125,9 @@ void testGetOrderWithItemsById_whenRequestIsValid_thenReturnCheckoutVm() throws "Second item note", new BigDecimal("10.00"), new BigDecimal("5.00"), - new BigDecimal("10.0") + new BigDecimal("10.0"), + new BigDecimal("8.5"), + new BigDecimal("8.5") ); return List.of(item1, item2); diff --git a/order/src/test/java/com/yas/order/mapper/CheckoutMapperTest.java b/order/src/test/java/com/yas/order/mapper/CheckoutMapperTest.java index 8281883b97..618a02ece9 100644 --- a/order/src/test/java/com/yas/order/mapper/CheckoutMapperTest.java +++ b/order/src/test/java/com/yas/order/mapper/CheckoutMapperTest.java @@ -20,7 +20,7 @@ class CheckoutMapperTest { CheckoutMapper checkoutMapper; @Test - void testCheckoutItemPostVmToModel_convertToCorrectCheckoutItem(){ + void testCheckoutItemPostVmToModel_convertToCorrectCheckoutItem() { var src = Instancio.create(CheckoutItemPostVm.class); @@ -38,7 +38,7 @@ void testCheckoutItemPostVmToModel_convertToCorrectCheckoutItem(){ } @Test - void testCheckoutPostVmToModel_convertToCorrectCheckout(){ + void testCheckoutPostVmToModel_convertToCorrectCheckout() { CheckoutPostVm checkoutPostVm = Instancio.of(CheckoutPostVm.class) .create(); @@ -50,12 +50,10 @@ void testCheckoutPostVmToModel_convertToCorrectCheckout(){ .hasFieldOrPropertyWithValue("note", checkoutPostVm.note()) .hasFieldOrPropertyWithValue("couponCode", checkoutPostVm.couponCode()); - Assertions.assertThat(res.getCheckoutItem()) - .hasSize(checkoutPostVm.checkoutItemPostVms().size()); } @Test - void testCheckoutToVm_convertToCheckoutVmCorrectly(){ + void testCheckoutToVm_convertToCheckoutVmCorrectly() { Checkout checkout = Instancio.create(Checkout.class); @@ -66,11 +64,11 @@ void testCheckoutToVm_convertToCheckoutVmCorrectly(){ .hasFieldOrPropertyWithValue("note", checkout.getNote()) .hasFieldOrPropertyWithValue("couponCode", checkout.getCouponCode()); - Assertions.assertThat(res.checkoutItemVms()).hasSize(checkout.getCheckoutItem().size()); + Assertions.assertThat(res.checkoutItemVms()).isNull(); } @Test - void testCheckoutItemToVm_convertCheckoutItemCorrectly(){ + void testCheckoutItemToVm_convertCheckoutItemCorrectly() { CheckoutItem checkoutItem = Instancio.create(CheckoutItem.class); diff --git a/order/src/test/java/com/yas/order/service/CheckoutServiceTest.java b/order/src/test/java/com/yas/order/service/CheckoutServiceTest.java index df1092f879..78e5fc94ee 100644 --- a/order/src/test/java/com/yas/order/service/CheckoutServiceTest.java +++ b/order/src/test/java/com/yas/order/service/CheckoutServiceTest.java @@ -1,18 +1,26 @@ package com.yas.order.service; +import static com.yas.order.utils.SecurityContextUtils.setSubjectUpSecurityContext; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; +import com.yas.commonlibrary.exception.Forbidden; import com.yas.order.mapper.CheckoutMapperImpl; import com.yas.order.model.Checkout; import com.yas.order.model.CheckoutItem; import com.yas.order.model.enumeration.CheckoutState; +import com.yas.order.repository.CheckoutItemRepository; import com.yas.order.repository.CheckoutRepository; import com.yas.order.viewmodel.checkout.CheckoutPostVm; +import java.util.List; +import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; import org.instancio.Instancio; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,6 +35,10 @@ class CheckoutServiceTest { @MockBean CheckoutRepository checkoutRepository; + + @MockBean + CheckoutItemRepository checkoutItemRepository; + @MockBean OrderService orderService; @@ -34,11 +46,13 @@ class CheckoutServiceTest { CheckoutService checkoutService; CheckoutPostVm checkoutPostVm; + List checkoutItems; Checkout checkoutCreated; String checkoutId = UUID.randomUUID().toString(); @BeforeEach void setUp() { + checkoutPostVm = Instancio.create(CheckoutPostVm.class); checkoutCreated = Checkout.builder() .id(checkoutId) @@ -46,30 +60,31 @@ void setUp() { .note(checkoutPostVm.note()) .email(checkoutPostVm.email()) .couponCode(checkoutPostVm.couponCode()) - .checkoutItem(checkoutPostVm.checkoutItemPostVms().stream() - .map(itemVm -> CheckoutItem.builder() - .id(Instancio.create(Long.class)) - .productId(itemVm.productId()) - .productName(itemVm.productName()) - .quantity(itemVm.quantity()) - .productPrice(itemVm.productPrice()) - .discountAmount(itemVm.discountAmount()) - .taxAmount(itemVm.taxAmount()) - .taxPercent(itemVm.taxPercent()) - .note(itemVm.note()) - .build() - ).collect(Collectors.toSet())) .build(); - checkoutCreated.getCheckoutItem() - .forEach(item-> item.setCheckoutId(checkoutCreated)); + + checkoutItems = checkoutPostVm.checkoutItemPostVms().stream() + .map(itemVm -> CheckoutItem.builder() + .id(Instancio.create(Long.class)) + .productId(itemVm.productId()) + .productName(itemVm.productName()) + .quantity(itemVm.quantity()) + .productPrice(itemVm.productPrice()) + .discountAmount(itemVm.discountAmount()) + .taxAmount(itemVm.taxAmount()) + .taxPercent(itemVm.taxPercent()) + .note(itemVm.note()) + .checkoutId(checkoutId) + .build() + ).toList(); + } @Test - void createCheckout() { + void testCreateCheckout_whenNormalCase_returnCheckout() { when(checkoutRepository.save(any())).thenReturn(checkoutCreated); - - var res = checkoutService.createCheckout(checkoutPostVm); + when(checkoutItemRepository.saveAll(anyCollection())).thenReturn(checkoutItems); + var res = checkoutService.createCheckout(checkoutPostVm); assertThat(res) .hasFieldOrPropertyWithValue("id", checkoutId) @@ -79,6 +94,79 @@ void createCheckout() { assertThat(res.checkoutItemVms()) .hasSize(checkoutPostVm.checkoutItemPostVms().size()) - .allMatch(item->item.checkoutId().equals(checkoutId)); + .allMatch(item -> item.checkoutId().equals(checkoutId)); + } + + @Test + void testCreateCheckout_whenCheckoutItemsIsEmpty_returnCheckoutWithoutCheckoutItems() { + + when(checkoutRepository.save(any())).thenReturn(checkoutCreated); + when(checkoutItemRepository.saveAll(anyCollection())).thenReturn(List.of()); + var res = checkoutService.createCheckout(checkoutPostVm); + + assertThat(res) + .hasFieldOrPropertyWithValue("id", checkoutId) + .hasFieldOrPropertyWithValue("email", checkoutPostVm.email()) + .hasFieldOrPropertyWithValue("couponCode", checkoutPostVm.couponCode()) + .hasFieldOrPropertyWithValue("note", checkoutPostVm.note()); + + assertThat(res.checkoutItemVms()).isEmpty(); + } + + @Test + void testGetCheckoutPendingStateWithItemsById_whenNormalCase_returnCheckoutVm() { + + checkoutCreated.setCreatedBy("test-create-by"); + when(checkoutRepository.findByIdAndCheckoutState(anyString(), eq(CheckoutState.PENDING))) + .thenReturn(Optional.ofNullable(checkoutCreated)); + + setSubjectUpSecurityContext(checkoutCreated.getCreatedBy()); + when(checkoutItemRepository.findAllByCheckoutId(anyString())).thenReturn(checkoutItems); + var res = checkoutService.getCheckoutPendingStateWithItemsById("1"); + + assertThat(res) + .hasFieldOrPropertyWithValue("id", checkoutId) + .hasFieldOrPropertyWithValue("couponCode", checkoutPostVm.couponCode()) + .hasFieldOrPropertyWithValue("email", checkoutPostVm.email()) + .hasFieldOrPropertyWithValue("note", checkoutPostVm.note()); + + assertThat(res.checkoutItemVms()) + .allMatch(item -> item.checkoutId().equals(checkoutId)) + .hasSize(checkoutPostVm.checkoutItemPostVms().size()); + } + + @Test + void testGetCheckoutPendingStateWithItemsById_whenNotEqualsCreateBy_throwForbidden() { + + checkoutCreated.setCreatedBy("test-create-by"); + when(checkoutRepository.findByIdAndCheckoutState(anyString(), eq(CheckoutState.PENDING))) + .thenReturn(Optional.ofNullable(checkoutCreated)); + setSubjectUpSecurityContext("test--by"); + + Assertions.assertThrows(Forbidden.class, + () -> checkoutService.getCheckoutPendingStateWithItemsById("1"), + "You don't have permission to access this page"); + + + } + + @Test + void testGetCheckoutPendingStateWithItemsById_whenNormalCase_returnCheckoutVmWithoutCheckoutItems() { + + checkoutCreated.setCreatedBy("test-create-by"); + setSubjectUpSecurityContext(checkoutCreated.getCreatedBy()); + when(checkoutRepository.findByIdAndCheckoutState(anyString(), eq(CheckoutState.PENDING))) + .thenReturn(Optional.ofNullable(checkoutCreated)); + when(checkoutItemRepository.findAllByCheckoutId(anyString())).thenReturn(List.of()); + + var res = checkoutService.getCheckoutPendingStateWithItemsById("1"); + + assertThat(res) + .hasFieldOrPropertyWithValue("id", checkoutId) + .hasFieldOrPropertyWithValue("couponCode", checkoutPostVm.couponCode()) + .hasFieldOrPropertyWithValue("note", checkoutPostVm.note()) + .hasFieldOrPropertyWithValue("email", checkoutPostVm.email()); + + assertThat(res.checkoutItemVms()).isNull(); } } \ No newline at end of file diff --git a/order/src/test/java/com/yas/order/utils/SecurityContextUtils.java b/order/src/test/java/com/yas/order/utils/SecurityContextUtils.java index 2b80ac64f1..ee2a0140d2 100644 --- a/order/src/test/java/com/yas/order/utils/SecurityContextUtils.java +++ b/order/src/test/java/com/yas/order/utils/SecurityContextUtils.java @@ -7,6 +7,7 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; public class SecurityContextUtils { @@ -24,4 +25,14 @@ public static void setUpSecurityContext(String userName) { SecurityContextHolder.setContext(securityContext); } + public static void setSubjectUpSecurityContext(String subject) { + JwtAuthenticationToken auth = mock(JwtAuthenticationToken.class); + Jwt jwt = mock(Jwt.class); + when(auth.getToken()).thenReturn(jwt); + when(jwt.getSubject()).thenReturn(subject); + SecurityContext securityContext = mock(SecurityContext.class); + when(securityContext.getAuthentication()).thenReturn(auth); + SecurityContextHolder.setContext(securityContext); + } + } diff --git a/payment/src/main/java/com/yas/payment/model/Payment.java b/payment/src/main/java/com/yas/payment/model/Payment.java index 8a701c408f..5c2b93d583 100644 --- a/payment/src/main/java/com/yas/payment/model/Payment.java +++ b/payment/src/main/java/com/yas/payment/model/Payment.java @@ -10,14 +10,11 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import java.math.BigDecimal; -import java.time.ZonedDateTime; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.UpdateTimestamp; @Entity @Table(name = "payment") @@ -26,22 +23,31 @@ @NoArgsConstructor @AllArgsConstructor @Builder -public class Payment { +public class Payment extends AbstractAuditEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private Long orderId; + private String checkoutId; + private BigDecimal amount; + private BigDecimal paymentFee; + @Enumerated(EnumType.STRING) private PaymentMethod paymentMethod; + @Enumerated(EnumType.STRING) private PaymentStatus paymentStatus; + private String gatewayTransactionId; + private String failureMessage; - @CreationTimestamp - private ZonedDateTime createdOn; - @UpdateTimestamp - private ZonedDateTime lastModifiedOn; + + @SuppressWarnings("unused") + private String paymentProviderCheckoutId; + } diff --git a/payment/src/main/resources/db/changelog/ddl/changelog-0003.sql b/payment/src/main/resources/db/changelog/ddl/changelog-0003.sql new file mode 100644 index 0000000000..7f40544377 --- /dev/null +++ b/payment/src/main/resources/db/changelog/ddl/changelog-0003.sql @@ -0,0 +1,4 @@ +ALTER TABLE IF EXISTS "payment" +ADD COLUMN created_by varchar(255), +ADD COLUMN last_modified_by varchar(255), +ADD COLUMN payment_provider_checkout_id varchar(255); \ No newline at end of file