Skip to content

Commit

Permalink
Merge pull request #69 from Try-AngIe/feat/billing
Browse files Browse the repository at this point in the history
feat: 청구를 생성할 수 있다.
  • Loading branch information
pogihae authored Jul 10, 2024
2 parents 6c3c00e + 70b6dd6 commit 7af4397
Show file tree
Hide file tree
Showing 21 changed files with 312 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers("/api/v1/vendor/auth/refresh").permitAll()
.requestMatchers("/api/v1/vendor/auth/username-check").permitAll()
.requestMatchers("/", "/api/v1/**").permitAll()
.requestMatchers("/error").permitAll()
.requestMatchers("/vendor").hasRole("VENDOR")
.requestMatchers("/member").hasRole("MEMBER")
.anyRequest().authenticated());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

public class SortPageDto {

public static int calcTotalPageNumber(int totalItemNum, int pageSize) {
return (int) Math.ceil((double) totalItemNum / pageSize);
}

@Getter
@Setter
public static class Req {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

Expand All @@ -14,7 +15,7 @@ public class GlobalExceptionHandler {
public ResponseEntity<String> handleEntityNotFoundException(EntityNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}

//
// @ExceptionHandler(MethodArgumentNotValidException.class)
// public ResponseEntity<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
// return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;
import kr.or.kosa.cmsplusmain.domain.base.dto.SortPageDto;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingListItem;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingReq;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingSearch;
import kr.or.kosa.cmsplusmain.domain.billing.service.BillingService;
import lombok.RequiredArgsConstructor;
import lombok.Value;

@RestController
@RequestMapping("/api/v1/vendor/billing")
Expand All @@ -24,16 +29,27 @@ public class BillingController {
* 계약 상세 - 청구 목록
* */
@GetMapping("/contract")
public List<BillingListItem> getBillingListByContract(@RequestParam(name = "id") Long contractId,
SortPageDto.Req pageable) {
return billingService.findBillingsByContract(contractId, pageable);
public List<BillingListItem> getBillingListByContract(@RequestParam(name = "id") Long contractId, SortPageDto.Req pageable) {
String vendorUsername = "vendor1";
return billingService.findBillingsByContract(vendorUsername, contractId, pageable);
}

/*
* 청구목록 조회
* */
@GetMapping
public List<BillingListItem> getBillingListWithCondition(BillingSearch search, SortPageDto.Req pageable) {
return billingService.findBillings(search, pageable);
String vendorUsername = "vendor1";
return billingService.findBillings(vendorUsername, search, pageable);
}

/*
* 청구생성
* */
@PostMapping
public void createBilling(@RequestBody @Valid BillingReq billingReq) {
// TODO security
String vendorUsername = "vendor1";
billingService.createBilling(vendorUsername, billingReq);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kr.or.kosa.cmsplusmain.domain.billing.dto;

import kr.or.kosa.cmsplusmain.domain.billing.entity.BillingProduct;
import kr.or.kosa.cmsplusmain.domain.product.entity.Product;
import lombok.Getter;

@Getter
public class BillingProductReq {
private Long productId;
private Integer price;
private Integer quantity;

public BillingProduct toEntity() {
return BillingProduct.builder()
.product(Product.of(productId))
.price(price)
.quantity(quantity)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package kr.or.kosa.cmsplusmain.domain.billing.dto;

import java.time.LocalDate;
import java.util.List;

import jakarta.validation.constraints.NotNull;
import kr.or.kosa.cmsplusmain.domain.billing.entity.BillingType;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
public class BillingReq {
@NotNull
private BillingType billingType;
@NotNull
private LocalDate billingDate;
@NotNull
private Long contractId;
@NotNull
private List<BillingProductReq> billingProducts;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import jakarta.validation.constraints.NotNull;
import kr.or.kosa.cmsplusmain.domain.base.entity.BaseEntity;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand All @@ -26,6 +28,8 @@
@Entity
@Table(name = "billing")
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Billing extends BaseEntity {

Expand All @@ -49,7 +53,7 @@ public class Billing extends BaseEntity {
@Enumerated(EnumType.STRING)
@Column(name = "billing_status", nullable = false)
@NotNull
private BillingStatus billingStatus;
private BillingStatus billingStatus = BillingStatus.CREATED;

@Comment("청구서 메시지")
@Column(name = "billing_memo", length = 2000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
import kr.or.kosa.cmsplusmain.domain.base.entity.BaseEntity;
import kr.or.kosa.cmsplusmain.domain.product.entity.Product;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Table(name = "billing_product")
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BillingProduct extends BaseEntity {

Expand All @@ -31,6 +36,7 @@ public class BillingProduct extends BaseEntity {
@Comment("청구상품의 청구기준")
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "billing_standard_id")
@Setter
private BillingStandard billingStandard;

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.SQLRestriction;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand All @@ -19,8 +20,11 @@
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotNull;
import kr.or.kosa.cmsplusmain.domain.base.entity.BaseEntity;
import kr.or.kosa.cmsplusmain.domain.billing.exception.EmptyBillingProductException;
import kr.or.kosa.cmsplusmain.domain.contract.entity.Contract;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -45,7 +49,7 @@ public class BillingStandard extends BaseEntity {
@Enumerated(EnumType.STRING)
@Column(name = "billing_standard_status", nullable = false)
@NotNull
private BillingStandardStatus status;
private BillingStandardStatus status = BillingStandardStatus.ENABLED;

@Comment("청구 타입 [정기 or 추가]")
@Enumerated(EnumType.STRING)
Expand All @@ -58,10 +62,32 @@ public class BillingStandard extends BaseEntity {
private int contractDay;

/* 청구 상품 목록 */
@OneToMany(mappedBy = "billingStandard")
@OneToMany(mappedBy = "billingStandard", cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@SQLRestriction(BaseEntity.NON_DELETED_QUERY)
private List<BillingProduct> billingProducts = new ArrayList<>();

@Builder
public BillingStandard(Contract contract, BillingType type, int contractDay, List<BillingProduct> billingProducts) {
// 청구는 최소 한 개의 상품을 가져야한다.
if (billingProducts.isEmpty()) {
throw new EmptyBillingProductException();
}

this.contract = contract;
this.type = type;
this.contractDay = contractDay;

billingProducts.forEach(this::addBillingProduct);
}

/*
* 청구 상품 추가
* */
public void addBillingProduct(BillingProduct billingProduct) {
billingProduct.setBillingStandard(this);
billingProducts.add(billingProduct);
}

/*
* 청구금액
* */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kr.or.kosa.cmsplusmain.domain.billing.exception;

public class EmptyBillingProductException extends RuntimeException {
public EmptyBillingProductException() {
super("청구는 최소 한개의 상품을 포함해야합니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import jakarta.persistence.EntityManager;
import kr.or.kosa.cmsplusmain.domain.base.dto.SortPageDto;
import kr.or.kosa.cmsplusmain.domain.base.repository.BaseCustomRepository;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingReq;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingSearch;
import kr.or.kosa.cmsplusmain.domain.billing.entity.Billing;
import lombok.extern.slf4j.Slf4j;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package kr.or.kosa.cmsplusmain.domain.billing.repository;

import org.springframework.stereotype.Repository;

import kr.or.kosa.cmsplusmain.domain.base.repository.BaseRepository;
import kr.or.kosa.cmsplusmain.domain.billing.entity.Billing;

@Repository
public interface BillingRepository extends BaseRepository<Billing, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package kr.or.kosa.cmsplusmain.domain.billing.repository;

import org.springframework.stereotype.Repository;

import kr.or.kosa.cmsplusmain.domain.base.repository.BaseRepository;
import kr.or.kosa.cmsplusmain.domain.billing.entity.BillingStandard;

@Repository
public interface BillingStandardRepository extends BaseRepository<BillingStandard, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,44 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.persistence.EntityNotFoundException;
import kr.or.kosa.cmsplusmain.domain.base.dto.SortPageDto;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingListItem;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingProductReq;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingReq;
import kr.or.kosa.cmsplusmain.domain.billing.dto.BillingSearch;
import kr.or.kosa.cmsplusmain.domain.billing.entity.Billing;
import kr.or.kosa.cmsplusmain.domain.billing.entity.BillingProduct;
import kr.or.kosa.cmsplusmain.domain.billing.entity.BillingStandard;
import kr.or.kosa.cmsplusmain.domain.billing.entity.BillingStatus;
import kr.or.kosa.cmsplusmain.domain.billing.repository.BillingCustomRepository;
import kr.or.kosa.cmsplusmain.domain.billing.repository.BillingRepository;
import kr.or.kosa.cmsplusmain.domain.billing.repository.BillingStandardRepository;
import kr.or.kosa.cmsplusmain.domain.contract.entity.Contract;
import kr.or.kosa.cmsplusmain.domain.contract.repository.ContractCustomRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class BillingService {

private final BillingCustomRepository billingRepository;
private final BillingRepository billingRepository;
private final BillingCustomRepository billingCustomRepository;
private final BillingStandardRepository billingStandardRepository;

private final ContractCustomRepository contractCustomRepository;


/*
* 계약 상세 - 청구 목록 조회
*
* 계약 id로 청구 목록을 가져온다.
* 최신순 정렬
* */
public List<BillingListItem> findBillingsByContract(Long contractId, SortPageDto.Req pageable) {
return billingRepository.findBillingsByContractId(contractId, pageable).stream()
public List<BillingListItem> findBillingsByContract(String vendorUsername, Long contractId, SortPageDto.Req pageable) {
return billingCustomRepository.findBillingsByContractId(contractId, pageable).stream()
.map(BillingListItem::fromEntity)
.toList();
}
Expand All @@ -35,10 +52,46 @@ public List<BillingListItem> findBillingsByContract(Long contractId, SortPageDto
*
* 검색, 정렬 조건을 반영한다.
* */
public List<BillingListItem> findBillings(BillingSearch search, SortPageDto.Req pageable) {
String vendorUsername = "vendor1";
return billingRepository.findBillingListWithCondition(vendorUsername, search, pageable).stream()
public List<BillingListItem> findBillings(String vendorUsername, BillingSearch search, SortPageDto.Req pageable) {
return billingCustomRepository.findBillingListWithCondition(vendorUsername, search, pageable).stream()
.map(BillingListItem::fromEntity)
.toList();
}

/*
* 청구 생성
* */
@Transactional
public void createBilling(String vendorUsername, BillingReq billingReq) {
// 계약 존재 여부 확인
if (!contractCustomRepository.isExistContractByUsername(billingReq.getContractId(), vendorUsername)) {
throw new EntityNotFoundException("해당하는 계약이 없습니다." + billingReq.getContractId().toString());
}

// 청구 상품
List<BillingProduct> billingProducts = billingReq.getBillingProducts()
.stream()
.map(BillingProductReq::toEntity)
.toList();

// 청구 기준 생성
BillingStandard billingStandard = billingStandardRepository.save(BillingStandard.builder()
.contract(Contract.of(billingReq.getContractId()))
.type(billingReq.getBillingType())

// 청구 생성시 결제일을 넣어주는데 연월일 까지 넣어준다.
// 정기 청구 시 필요한 약정일은 입력된 결제일에서 일 부분만 빼서 사용
// ex. 입력 결제일=2024.07.13 => 약정일=13
.contractDay(billingReq.getBillingDate().getDayOfMonth())
.billingProducts(billingProducts)
.build());


// 청구 생성
Billing billing = billingRepository.save(Billing.builder()
.billingStandard(billingStandard)
.billingDate(billingReq.getBillingDate())
.billingStatus(BillingStatus.CREATED)
.build());
}
}
Loading

0 comments on commit 7af4397

Please sign in to comment.