Merge remote-tracking branch 'origin/test' into dev
This commit is contained in:
+7
-2
@@ -43,8 +43,13 @@ public final class OverseasFeeSourceDtos {
|
||||
@Size(max = 128) String paymentReferenceNo,
|
||||
@Size(max = 20) List<@NotBlank String> paymentVoucherFileIds,
|
||||
@NotNull LocalDate businessDate,
|
||||
@NotBlank @Pattern(regexp = "CNY") String currency,
|
||||
@NotNull @DecimalMin(value = "0.00", inclusive = false) BigDecimal amount
|
||||
@NotBlank @Pattern(regexp = "CNY|USD") String currency,
|
||||
@NotNull @DecimalMin(value = "0.00", inclusive = false) BigDecimal amount,
|
||||
@DecimalMin(value = "0.00", inclusive = false) BigDecimal chargeableQuantity,
|
||||
@Size(max = 32) String quantityUnit,
|
||||
@DecimalMin(value = "0.00", inclusive = false) BigDecimal unitPrice,
|
||||
LocalDate periodStart,
|
||||
LocalDate periodEnd
|
||||
) { }
|
||||
|
||||
public record ActionRequest(@NotBlank @Size(max = 128) String idempotencyKey) { }
|
||||
|
||||
+9
@@ -46,6 +46,15 @@ public class OverseasFeeSource {
|
||||
private LocalDate businessDate;
|
||||
private String currency;
|
||||
private BigDecimal amount;
|
||||
private String originalCurrency;
|
||||
private BigDecimal originalAmount;
|
||||
private BigDecimal exchangeRate;
|
||||
private String exchangeRateMonth;
|
||||
private BigDecimal chargeableQuantity;
|
||||
private String quantityUnit;
|
||||
private BigDecimal unitPrice;
|
||||
private LocalDate periodStart;
|
||||
private LocalDate periodEnd;
|
||||
private String reviewStatus;
|
||||
private String publicationStatus;
|
||||
private String sourceStatus;
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.baiyue.lastleg.financefee.service;
|
||||
|
||||
import com.baiyue.lastleg.masterdata.exchange.entity.MonthlyExchangeRate;
|
||||
import com.baiyue.lastleg.masterdata.exchange.mapper.MonthlyExchangeRateMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class OverseasFeeCurrencyConverter {
|
||||
private static final DateTimeFormatter MONTH = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
private final MonthlyExchangeRateMapper rates;
|
||||
|
||||
public Conversion convert(String currency, BigDecimal amount, LocalDate businessDate) {
|
||||
String originalCurrency = currency == null ? null : currency.trim().toUpperCase();
|
||||
BigDecimal originalAmount = amount == null ? null : amount.setScale(2, RoundingMode.HALF_UP);
|
||||
if ("CNY".equals(originalCurrency)) {
|
||||
return new Conversion(originalCurrency, originalAmount, BigDecimal.ONE.setScale(6),
|
||||
businessDate.format(MONTH), originalAmount);
|
||||
}
|
||||
if (!"USD".equals(originalCurrency)) throw bad("海外费用原币只支持CNY或USD");
|
||||
String month = businessDate.format(MONTH);
|
||||
MonthlyExchangeRate rate = rates.findMonth(month);
|
||||
if (rate == null || !"ACTIVE".equals(rate.getStatus()) || rate.getUsdToCnyAverage() == null
|
||||
|| rate.getUsdToCnyAverage().signum() <= 0) {
|
||||
throw bad("业务月份缺少有效USD兑CNY汇率,不能发布财务费用");
|
||||
}
|
||||
BigDecimal exchangeRate = rate.getUsdToCnyAverage().setScale(6, RoundingMode.HALF_UP);
|
||||
return new Conversion(originalCurrency, originalAmount, exchangeRate, month,
|
||||
originalAmount.multiply(exchangeRate).setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
private static ResponseStatusException bad(String message) {
|
||||
return new ResponseStatusException(HttpStatus.BAD_REQUEST, message);
|
||||
}
|
||||
|
||||
public record Conversion(String originalCurrency, BigDecimal originalAmount,
|
||||
BigDecimal exchangeRate, String exchangeRateMonth,
|
||||
BigDecimal settlementAmount) { }
|
||||
}
|
||||
+4
-1
@@ -66,7 +66,10 @@ public class OverseasFeeOutboxService {
|
||||
text(value.getSupplierId()), value.getSupplierName(), value.getSupplierCategory(),
|
||||
value.getSettlementMode(), instant(value.getPaidAt()), value.getPaymentMethod(),
|
||||
value.getPaymentReferenceNo(), value.getPaymentVoucherFileIds() == null ? List.of() : value.getPaymentVoucherFileIds(),
|
||||
value.getBusinessDate(), value.getCurrency(), value.getAmount(), value.getReviewStatus(),
|
||||
value.getBusinessDate(), value.getCurrency(), value.getAmount(),
|
||||
value.getOriginalCurrency(), value.getOriginalAmount(), value.getExchangeRate(), value.getExchangeRateMonth(),
|
||||
value.getChargeableQuantity(), value.getQuantityUnit(), value.getUnitPrice(), value.getPeriodStart(), value.getPeriodEnd(),
|
||||
value.getReviewStatus(),
|
||||
"PUBLISHED", action == null && hash == null ? "ACTIVE" : value.getSourceStatus(), hash);
|
||||
}
|
||||
|
||||
|
||||
+15
-3
@@ -19,7 +19,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -40,6 +39,7 @@ public class OverseasFeeSourceService {
|
||||
private final OverseasFeeCommandMapper commands;
|
||||
private final OverseasFeeBusinessGuard businessGuard;
|
||||
private final OverseasFeeOutboxService outbox;
|
||||
private final OverseasFeeCurrencyConverter currencyConverter;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public List<OverseasFeeSource> list(String customerId, String businessType, String businessNo,
|
||||
@@ -151,8 +151,14 @@ public class OverseasFeeSourceService {
|
||||
value.setDirection(request.direction()); value.setSupplierId(request.supplierId()); value.setSupplierName(text(request.supplierName()));
|
||||
value.setSupplierCategory(text(request.supplierCategory())); value.setSettlementMode(text(request.settlementMode()));
|
||||
value.setPaidAt(request.paidAt()); value.setPaymentMethod(text(request.paymentMethod())); value.setPaymentReferenceNo(text(request.paymentReferenceNo()));
|
||||
value.setPaymentVoucherFileIds(request.paymentVoucherFileIds()); value.setBusinessDate(request.businessDate()); value.setCurrency(request.currency());
|
||||
value.setAmount(request.amount().setScale(2, RoundingMode.HALF_UP)); value.setReviewStatus("DRAFT");
|
||||
value.setPaymentVoucherFileIds(request.paymentVoucherFileIds()); value.setBusinessDate(request.businessDate());
|
||||
OverseasFeeCurrencyConverter.Conversion conversion = currencyConverter.convert(request.currency(), request.amount(), request.businessDate());
|
||||
value.setCurrency("CNY"); value.setAmount(conversion.settlementAmount());
|
||||
value.setOriginalCurrency(conversion.originalCurrency()); value.setOriginalAmount(conversion.originalAmount());
|
||||
value.setExchangeRate(conversion.exchangeRate()); value.setExchangeRateMonth(conversion.exchangeRateMonth());
|
||||
value.setChargeableQuantity(request.chargeableQuantity()); value.setQuantityUnit(text(request.quantityUnit()));
|
||||
value.setUnitPrice(request.unitPrice()); value.setPeriodStart(request.periodStart()); value.setPeriodEnd(request.periodEnd());
|
||||
value.setReviewStatus("DRAFT");
|
||||
value.setPublicationStatus("UNPUBLISHED"); value.setSourceStatus("ACTIVE"); value.setCreatedBy(operatorId); value.setUpdatedBy(operatorId);
|
||||
value.setCreatedAt(now()); value.setUpdatedAt(now());
|
||||
return value;
|
||||
@@ -172,6 +178,12 @@ public class OverseasFeeSourceService {
|
||||
if ("OVERSEAS_CONTAINER".equals(value.businessType())
|
||||
&& !OVERSEAS_CONTAINER_FEE_ITEMS.contains(value.feeItemCode().trim()))
|
||||
throw bad("海外货柜费用项目不受支持");
|
||||
if ((value.periodStart() == null) != (value.periodEnd() == null)
|
||||
|| value.periodStart() != null && value.periodStart().isAfter(value.periodEnd()))
|
||||
throw bad("计费期间必须同时填写且开始日期不能晚于结束日期");
|
||||
if ((value.chargeableQuantity() == null) != (text(value.quantityUnit()) == null)
|
||||
|| value.unitPrice() != null && value.chargeableQuantity() == null)
|
||||
throw bad("计费数量、单位和单价信息不完整");
|
||||
boolean payable = "PAYABLE".equals(value.direction());
|
||||
boolean files = value.paymentVoucherFileIds() != null && !value.paymentVoucherFileIds().isEmpty();
|
||||
if (!payable) {
|
||||
|
||||
+4
@@ -83,6 +83,10 @@ public class DestuffingPalletCostService {
|
||||
v.setBusinessId(costId); v.setBusinessNo("DESTUFF-"+bookingId); v.setContainerBookingId(parseLong(bookingId)); v.setContainerNo(containerNo);
|
||||
v.setWarehouseId(warehouseId); v.setFeeItemCode("DESTUFF_PALLET"); v.setFeeItemName("拆柜打托费"); v.setDirection("RECEIVABLE");
|
||||
v.setPaymentVoucherFileIds(List.of()); v.setBusinessDate(LocalDate.now()); v.setCurrency("CNY"); v.setAmount(amount);
|
||||
v.setOriginalCurrency("CNY"); v.setOriginalAmount(amount); v.setExchangeRate(BigDecimal.ONE.setScale(6));
|
||||
v.setExchangeRateMonth(LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM")));
|
||||
v.setChargeableQuantity(volume.volume()); v.setQuantityUnit("CBM");
|
||||
v.setUnitPrice(volume.volume().signum() > 0 ? amount.divide(volume.volume(), 6, RoundingMode.HALF_UP) : null);
|
||||
v.setReviewStatus("APPROVED"); v.setPublicationStatus("PUBLISHED"); v.setSourceStatus("ACTIVE"); v.setReviewerId(operator);
|
||||
v.setReviewedAt(now); v.setCreatedBy(operator); v.setUpdatedBy(operator); v.setCreatedAt(now); v.setUpdatedAt(now); return v;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user