diff --git a/app/src/main/java/com/hotelJava/accommodation/domain/Accommodation.java b/app/src/main/java/com/hotelJava/accommodation/domain/Accommodation.java index 296557b..33cb302 100644 --- a/app/src/main/java/com/hotelJava/accommodation/domain/Accommodation.java +++ b/app/src/main/java/com/hotelJava/accommodation/domain/Accommodation.java @@ -3,6 +3,7 @@ import com.hotelJava.common.embeddable.Address; import com.hotelJava.common.util.BaseTimeEntity; import com.hotelJava.picture.domain.Picture; +import com.hotelJava.picture.domain.PictureType; import com.hotelJava.reservation.domain.ReservationStatus; import com.hotelJava.room.domain.Room; import jakarta.persistence.CascadeType; @@ -84,11 +85,12 @@ public Accommodation updateAccommodation( // == 연관관계 편의 메소드 ==// public void setPicture(Picture picture) { this.picture = picture; + picture.setPictureType(PictureType.ACCOMMODATION); picture.setAccommodation(this); } public void createAccommodation(List rooms, Picture accommodationPicture) { - this.picture = accommodationPicture; + setPicture(accommodationPicture); this.rooms.addAll(rooms); rooms.forEach(room -> room.setAccommodation(this)); } diff --git a/app/src/main/java/com/hotelJava/common/embeddable/CheckDate.java b/app/src/main/java/com/hotelJava/common/embeddable/CheckDate.java index a10cea5..8c27eff 100644 --- a/app/src/main/java/com/hotelJava/common/embeddable/CheckDate.java +++ b/app/src/main/java/com/hotelJava/common/embeddable/CheckDate.java @@ -21,4 +21,13 @@ public class CheckDate { @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate checkOutDate; + + public CheckDate(LocalDate checkInDate, int duration) { + this.checkInDate = checkInDate; + this.checkOutDate = checkInDate.plusDays(duration); + } + + public boolean matches(LocalDate date) { + return date.isEqual(checkInDate) || (date.isAfter(checkInDate) && date.isBefore(checkOutDate)); + } } diff --git a/app/src/main/java/com/hotelJava/common/error/ErrorCode.java b/app/src/main/java/com/hotelJava/common/error/ErrorCode.java index deae3ff..9aa8751 100644 --- a/app/src/main/java/com/hotelJava/common/error/ErrorCode.java +++ b/app/src/main/java/com/hotelJava/common/error/ErrorCode.java @@ -25,11 +25,23 @@ public enum ErrorCode { DUPLICATED_NAME_FOUND(409, "숙소명이 이미 존재합니다."), NO_MINIMUM_PRICE_FOUND(500, "숙소의 최소 가격을 찾을 수 없습니다."), + // 객실 관련 에러 + ROOM_NOT_FOUND(404, "해당 객실이 존재하지 않습니다"), + + // 예약 관련 에러 + OUT_OF_STOCK(400, "재고 수량이 부족합니다"), + OVER_MAX_OCCUPANCY(400, "객실 최대 인원을 초과하였습니다"), + RESERVATION_NOT_FOUND(404, "결제 대기중인 예약을 찾을 수 없습니다"), + + // 결제 관련 에러 + PAYMENT_FAIL(400, "결제 오류"), + PAYMENT_TIME_OUT(408, "결제 유효 시간을 초과하였습니다"), + // 클라이언트 에러 - BAD_REQUEST_ERROR(400, "요청값이 잘못되었습니다."), - + BAD_REQUEST_ERROR(400, "요청값이 잘못되었습니다"), + // 서버 에러 - INTERNAL_SERVER_ERROR(500, "요청을 정상 처리하지 못하였습니다."); + INTERNAL_SERVER_ERROR(500, "요청을 정상 처리하지 못하였습니다"); private final int code; private final String message; diff --git a/app/src/main/java/com/hotelJava/inventory/Inventory.java b/app/src/main/java/com/hotelJava/inventory/domain/Inventory.java similarity index 53% rename from app/src/main/java/com/hotelJava/inventory/Inventory.java rename to app/src/main/java/com/hotelJava/inventory/domain/Inventory.java index e7a462c..79e760a 100644 --- a/app/src/main/java/com/hotelJava/inventory/Inventory.java +++ b/app/src/main/java/com/hotelJava/inventory/domain/Inventory.java @@ -1,10 +1,13 @@ -package com.hotelJava.inventory; +package com.hotelJava.inventory.domain; import com.hotelJava.common.util.BaseTimeEntity; +import com.hotelJava.room.domain.Room; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import java.time.LocalDate; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -12,7 +15,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Entity @Getter @Setter @@ -25,11 +30,28 @@ public class Inventory extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private Long accommodationId; + @ManyToOne + @JoinColumn(name = "room_id") + private Room room; - private Long roomId; - private LocalDate date; - + private long quantity; + + public Inventory(LocalDate date, long quantity) { + this.date = date; + this.quantity = quantity; + } + + public boolean isZeroQuantity() { + if (quantity <= 0) { + log.info("quantity at {} is 0", date); + return true; + } + return false; + } + + public void calcQuantity(int value) { + quantity += value; + } } diff --git a/app/src/main/java/com/hotelJava/inventory/repository/InventoryRepository.java b/app/src/main/java/com/hotelJava/inventory/repository/InventoryRepository.java index 78dcb4b..463f524 100644 --- a/app/src/main/java/com/hotelJava/inventory/repository/InventoryRepository.java +++ b/app/src/main/java/com/hotelJava/inventory/repository/InventoryRepository.java @@ -1,11 +1,11 @@ package com.hotelJava.inventory.repository; -import com.hotelJava.inventory.Inventory; +import com.hotelJava.inventory.domain.Inventory; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;import java.util.List; @Repository public interface InventoryRepository extends JpaRepository { - List findByAccommodationIdAndRoomId(Long accommodationId, Long roomId); + List findByRoomId(Long roomId); } diff --git a/app/src/main/java/com/hotelJava/payment/domain/Payment.java b/app/src/main/java/com/hotelJava/payment/domain/Payment.java new file mode 100644 index 0000000..5be3834 --- /dev/null +++ b/app/src/main/java/com/hotelJava/payment/domain/Payment.java @@ -0,0 +1,63 @@ +package com.hotelJava.payment.domain; + +import com.hotelJava.reservation.domain.Reservation; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Payment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private int amount; + + @Enumerated(EnumType.STRING) + private PaymentType paymentType; + + private LocalDateTime paymentDate; + + @Enumerated(EnumType.STRING) + @Default + private PaymentStatus status = PaymentStatus.WAITING; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "reservation_id") + private Reservation reservation; + + public Payment(int amount) { + this.amount = amount; + } + + public PaymentStatus approve(PaymentResult paymentResult) { + if (paymentResult.isPayed(amount)) { + return status = PaymentStatus.COMPLETE; + } + log.info("payment fail"); + return status = PaymentStatus.ERROR; + } + + public boolean isExpired() { + return status != PaymentStatus.WAITING; + } +} diff --git a/app/src/main/java/com/hotelJava/payment/domain/PaymentResult.java b/app/src/main/java/com/hotelJava/payment/domain/PaymentResult.java new file mode 100644 index 0000000..fdc9692 --- /dev/null +++ b/app/src/main/java/com/hotelJava/payment/domain/PaymentResult.java @@ -0,0 +1,5 @@ +package com.hotelJava.payment.domain; + +public interface PaymentResult { + boolean isPayed(int money); +} diff --git a/app/src/main/java/com/hotelJava/payment/domain/PaymentStatus.java b/app/src/main/java/com/hotelJava/payment/domain/PaymentStatus.java new file mode 100644 index 0000000..e498264 --- /dev/null +++ b/app/src/main/java/com/hotelJava/payment/domain/PaymentStatus.java @@ -0,0 +1,15 @@ +package com.hotelJava.payment.domain; + +public enum PaymentStatus { + WAITING("결제대기"), + COMPLETE("결제완료"), + CANCEL("결제취소"), + ERROR("결제오류"), + TIMEOUT("결제시간초과"); + + private final String label; + + PaymentStatus(String label) { + this.label = label; + } +} diff --git a/app/src/main/java/com/hotelJava/reservation/domain/GuestInfo.java b/app/src/main/java/com/hotelJava/reservation/domain/GuestInfo.java new file mode 100644 index 0000000..d401741 --- /dev/null +++ b/app/src/main/java/com/hotelJava/reservation/domain/GuestInfo.java @@ -0,0 +1,14 @@ +package com.hotelJava.reservation.domain; + +import com.hotelJava.common.embeddable.CheckDate; + +public interface GuestInfo { + + String getGuestName(); + + String getGuestPhone(); + + int getNumberOfGuests(); + + CheckDate getCheckDate(); +} diff --git a/app/src/main/java/com/hotelJava/reservation/domain/Reservation.java b/app/src/main/java/com/hotelJava/reservation/domain/Reservation.java index cf14894..070e0bc 100644 --- a/app/src/main/java/com/hotelJava/reservation/domain/Reservation.java +++ b/app/src/main/java/com/hotelJava/reservation/domain/Reservation.java @@ -3,56 +3,47 @@ import com.hotelJava.common.embeddable.CheckDate; import com.hotelJava.common.util.BaseTimeEntity; import com.hotelJava.member.domain.Member; -import com.hotelJava.payment.domain.PaymentType; +import com.hotelJava.payment.domain.Payment; +import com.hotelJava.payment.domain.PaymentResult; import com.hotelJava.room.domain.Room; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Entity @Getter @Setter @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor -public class Reservation extends BaseTimeEntity { +public class Reservation extends BaseTimeEntity implements GuestInfo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String name; - private String reservationNo; - private String accommodationName; - - private String roomName; + @Builder.Default + @Enumerated(EnumType.STRING) + private ReservationStatus status = ReservationStatus.PAYMENT_PENDING; @Embedded private CheckDate checkDate; private int numberOfGuests; - @Enumerated(EnumType.STRING) - private PaymentType paymentType; + private String guestName; - private String phoneNumber; + private String guestPhone; - @Enumerated(EnumType.STRING) - private ReservationStatus status; + @OneToOne(fetch = FetchType.LAZY, mappedBy = "reservation", cascade = CascadeType.ALL) + private Payment payment; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") @@ -61,4 +52,51 @@ public class Reservation extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "room_id") private Room room; + + public Reservation(Member member, Room room, String reservationNo, GuestInfo guestInfo) { + this.member = member; + this.room = room; + this.reservationNo = reservationNo; + this.checkDate = guestInfo.getCheckDate(); + this.guestName = guestInfo.getGuestName(); + this.guestPhone = guestInfo.getGuestPhone(); + this.numberOfGuests = guestInfo.getNumberOfGuests(); + this.payment = new Payment(room.calcPrice()); + } + + public Reservation confirm(PaymentResult paymentResult) { + payment.approve(paymentResult); + status = ReservationStatus.RESERVATION_COMPLETED; + return this; + } + + public void consumeInventory() { + room.calcInventory(checkDate, -1); + } + + public void restoreInventory() { + room.calcInventory(checkDate, 1); + } + + public boolean isInvalidReservation() { + if (room.isNotEnoughInventoryAtCheckDate(checkDate)) { + log.info("room stock is not enough. payment declined"); + return true; + } + + if (room.isOverMaxOccupancy(numberOfGuests)) { + log.info("guest number is over max occupancy. payment declined"); + return true; + } + + return false; + } + + public boolean isExpiredPayment() { + if (payment.isExpired()) { + log.info("payment time out. payment declined"); + return true; + } + return false; + } } diff --git a/app/src/main/java/com/hotelJava/reservation/domain/ReservationStatus.java b/app/src/main/java/com/hotelJava/reservation/domain/ReservationStatus.java index 6bc2e2d..64897c5 100644 --- a/app/src/main/java/com/hotelJava/reservation/domain/ReservationStatus.java +++ b/app/src/main/java/com/hotelJava/reservation/domain/ReservationStatus.java @@ -10,7 +10,8 @@ public enum ReservationStatus { RESERVATION_AVAILABLE("예약가능"), RESERVATION_COMPLETED("예약완료"), RESERVATION_CANCEL("예약취소"), - SALES_COMPLETED("판매완료"); + SALES_COMPLETED("판매완료"), + PAYMENT_PENDING("결제대기"); private final String label; } diff --git a/app/src/main/java/com/hotelJava/reservation/dto/CreateReservationRequestDto.java b/app/src/main/java/com/hotelJava/reservation/dto/CreateReservationRequestDto.java index 4e27ebf..4d52ab1 100644 --- a/app/src/main/java/com/hotelJava/reservation/dto/CreateReservationRequestDto.java +++ b/app/src/main/java/com/hotelJava/reservation/dto/CreateReservationRequestDto.java @@ -2,11 +2,13 @@ import com.hotelJava.common.embeddable.CheckDate; import com.hotelJava.payment.dto.CreatePaymentRequestDto; +import com.hotelJava.reservation.domain.GuestInfo; import com.hotelJava.reservation.domain.ReservationCommand; import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Positive; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -17,16 +19,19 @@ @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor -public class CreateReservationRequestDto { +public class CreateReservationRequestDto implements GuestInfo { private ReservationCommand reservationCommand; @NotBlank(message = "예약자 이름을 입력해주세요.") - private String name; + private String guestName; @NotBlank(message = "휴대폰 번호의 형식이 맞지 않습니다.") @Pattern(regexp = "\\d{3}-\\d{4}-\\d{4}") - private String phoneNumber; + private String guestPhone; + + @Positive(message = "1명 이상의 인원을 입력해주세요.") + private int numberOfGuests; @NotNull(message = "체크인, 체크아웃 시간을 선택해주세요.") @Future diff --git a/app/src/main/java/com/hotelJava/room/domain/Room.java b/app/src/main/java/com/hotelJava/room/domain/Room.java index f5bb7fd..7c7d06f 100644 --- a/app/src/main/java/com/hotelJava/room/domain/Room.java +++ b/app/src/main/java/com/hotelJava/room/domain/Room.java @@ -1,12 +1,12 @@ package com.hotelJava.room.domain; import com.hotelJava.accommodation.domain.Accommodation; -import com.hotelJava.common.embeddable.CheckTime; +import com.hotelJava.common.embeddable.CheckDate; import com.hotelJava.common.util.BaseTimeEntity; +import com.hotelJava.inventory.domain.Inventory; import com.hotelJava.picture.domain.Picture; import com.hotelJava.reservation.domain.Reservation; import jakarta.persistence.CascadeType; -import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -22,11 +22,11 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Entity @Getter -@Setter @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @@ -46,8 +46,6 @@ public class Room extends BaseTimeEntity { @JoinColumn(name = "accommodation_id") // accommodation_id 외래 키로 연관관계를 맺는다. private Accommodation accommodation; - @Embedded private CheckTime checkTime; - @OneToMany(mappedBy = "room", cascade = CascadeType.ALL) @Builder.Default private List pictures = new ArrayList<>(); @@ -58,21 +56,45 @@ public class Room extends BaseTimeEntity { @OneToMany(mappedBy = "room", cascade = CascadeType.ALL) @Builder.Default - private List roomAvailabilities = new ArrayList<>(); + private List inventories = new ArrayList<>(); + + public void calcInventory(CheckDate checkDate, int value) { + inventories.stream() + .filter(i -> checkDate.matches(i.getDate())) + .forEach(i -> i.calcQuantity(value)); + } + + public boolean isNotEnoughInventoryAtCheckDate(CheckDate checkDate) { + return inventories.stream() + .filter(i -> checkDate.matches(i.getDate())) + .anyMatch(Inventory::isZeroQuantity); + } + + public boolean isOverMaxOccupancy(int guestNumber) { + return guestNumber > maxOccupancy; + } + + public int calcPrice() { + return price; + } // == 연관관계 편의 메소드 ==// + public void setAccommodation(Accommodation accommodation) { + this.accommodation = accommodation; + } + public void addPicture(Picture picture) { - this.pictures.add(picture); + pictures.add(picture); picture.setRoom(this); } public void addReservation(Reservation reservation) { - this.reservations.add(reservation); + reservations.add(reservation); reservation.setRoom(this); } - public void addRoomAvailability(RoomAvailability roomAvailability) { - this.roomAvailabilities.add(roomAvailability); - roomAvailability.setRoom(this); + public void addInventory(Inventory inventory) { + inventories.add(inventory); + inventory.setRoom(this); } } diff --git a/app/src/test/java/com/hotelJava/TestFixture.java b/app/src/test/java/com/hotelJava/TestFixture.java index f6c6973..7166469 100644 --- a/app/src/test/java/com/hotelJava/TestFixture.java +++ b/app/src/test/java/com/hotelJava/TestFixture.java @@ -4,7 +4,7 @@ import com.hotelJava.accommodation.domain.Accommodation; import com.hotelJava.accommodation.domain.AccommodationType; import com.hotelJava.common.embeddable.Address; -import com.hotelJava.inventory.Inventory; +import com.hotelJava.inventory.domain.Inventory; import com.hotelJava.member.domain.Grade; import com.hotelJava.member.domain.Member; import com.hotelJava.member.domain.Role; @@ -73,7 +73,7 @@ public static Picture getPicture() { return Picture.builder().pictureInfo(getPictureInfo()).build(); } - public static Accommodation getAccommodation(LocalDate start, int duration, int roomNumber) { + public static Accommodation getAccommodation(LocalDate from, int duration, int roomNumber) { Picture picture = getPicture(); Accommodation accommodation = Accommodation.builder() @@ -88,22 +88,23 @@ public static Accommodation getAccommodation(LocalDate start, int duration, int // mapping rooms & picture List rooms = new LinkedList<>(); for (int i = 0; i < roomNumber; i++) { - rooms.add(getRoom(faker.number().numberBetween(1, 5))); + int maxOccupancy = faker.number().numberBetween(1, 5); + int quantity = faker.number().numberBetween(1, 5); + rooms.add(getRoom(maxOccupancy, quantity, from, duration)); } accommodation.createAccommodation(rooms, picture); - // mapping inventory - - rooms.forEach( - room -> - start - .datesUntil(start.plusDays(duration)) - .forEach(date -> getInventory(room, date, faker.number().numberBetween(1, 5)))); - return accommodation; } - public static Room getRoom(int maxOccupancy) { + /** + * @param maxOccupancy 객실 최대 인원 + * @param quantity 객실 재고량 + * @param from 객실 재고를 언제부터 관리할 것인지 + * @param duration 객실 재고 보관 기간 + * @return 객실 및 객실의 재고 정보 + */ + public static Room getRoom(int maxOccupancy, int quantity, LocalDate from, int duration) { Picture picture = getPicture(); Room room = Room.builder() @@ -111,13 +112,15 @@ public static Room getRoom(int maxOccupancy) { .price(faker.number().numberBetween(50000, 100000)) .maxOccupancy(maxOccupancy) .build(); + room.addPicture(picture); + from.datesUntil(from.plusDays(duration)) + .forEach(d -> room.addInventory(getInventory(d, quantity))); + return room; } - public static Inventory getInventory(Room room, LocalDate date, int quantity) { - Inventory inventory = new Inventory(date, quantity); - room.addInventory(inventory); - return inventory; + private static Inventory getInventory(LocalDate date, int quantity) { + return new Inventory(date, quantity); } } diff --git a/app/src/test/java/com/hotelJava/common/embeddable/CheckDateTest.java b/app/src/test/java/com/hotelJava/common/embeddable/CheckDateTest.java new file mode 100644 index 0000000..80e09ee --- /dev/null +++ b/app/src/test/java/com/hotelJava/common/embeddable/CheckDateTest.java @@ -0,0 +1,31 @@ +package com.hotelJava.common.embeddable; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalDate; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CheckDateTest { + + @Test + @DisplayName("주어진 날짜가 숙박 기간 사이의 날짜라면 true 를 반환한다") + void 숙박기간_날짜비교_true() { + // given + CheckDate checkDate = new CheckDate(LocalDate.of(2023, 5, 10), 2); + + // then + assertThat(checkDate.matches(LocalDate.of(2023, 5, 10))).isTrue(); + assertThat(checkDate.matches(LocalDate.of(2023, 5, 11))).isTrue(); + } + + @Test + @DisplayName("주어진 날짜가 숙박 기간 사이의 날짜가 아니라면 false 를 반환한다") + void 숙박기간_날짜비교_false() { + // given + CheckDate checkDate = new CheckDate(LocalDate.of(2023, 5, 10), 2); + + // then + assertThat(checkDate.matches(LocalDate.of(2023, 5, 12))).isFalse(); + } +} diff --git a/app/src/test/java/com/hotelJava/room/domain/RoomTest.java b/app/src/test/java/com/hotelJava/room/domain/RoomTest.java new file mode 100644 index 0000000..e88539f --- /dev/null +++ b/app/src/test/java/com/hotelJava/room/domain/RoomTest.java @@ -0,0 +1,56 @@ +package com.hotelJava.room.domain; + +import static java.time.LocalDate.now; +import static org.assertj.core.api.Assertions.*; + +import com.hotelJava.TestFixture; +import com.hotelJava.common.embeddable.CheckDate; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RoomTest { + + @Test + @DisplayName("숙박 기간(체크인, 체크아웃)에 해당하는 재고를 1씩 감소시킨다") + void 객실재고감소() { + // given + Room room = TestFixture.getRoom(10, 10, now(), 10); + CheckDate checkDate = new CheckDate(now(), 1); + + // when + room.calcInventory(checkDate, -1); + + // then + assertThat( + room.getInventories().stream() + .filter(i -> checkDate.matches(i.getDate())) + .allMatch(i -> i.getQuantity() == 9)) + .isTrue(); + } + + @Test + @DisplayName("숙박 기간(체크인, 체크아웃)에 해당하는 재고 중 수량이 0 이하인 재고가 있으면 true 를 반환한다") + void 객실품절검사_true() { + // given + int duration = 10; + Room room = TestFixture.getRoom(10, 10, now(), duration); + room.getInventories().get(duration - 1).setQuantity(0); + CheckDate checkDate = new CheckDate(now(), duration); + + // when, then + room.isNotEnoughInventoryAtCheckDate(checkDate); + Assertions.assertThat(room.isNotEnoughInventoryAtCheckDate(checkDate)).isTrue(); + } + + @Test + @DisplayName("숙박 기간(체크인, 체크아웃)에 해당하는 재고 중 수량이 0 이하인 재고가 없다면 false 를 반환한다") + void 객실품절검사_false() { + // given + Room room = TestFixture.getRoom(10, 10, now(), 10); + CheckDate checkDate = new CheckDate(now(), 10); + + // when, then + Assertions.assertThat(room.isNotEnoughInventoryAtCheckDate(checkDate)).isFalse(); + } +}