[공통] util → framework 유틸 (ACM-CM-002)
This commit is contained in:
parent
3fdaa56fab
commit
45406e9974
1 changed files with 438 additions and 0 deletions
|
|
@ -0,0 +1,438 @@
|
||||||
|
package com.klaro.acquirecore.framework.util;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility class for amount/money operations migrated from legacy/util_amount.c
|
||||||
|
* Uses java.math.BigDecimal for precise monetary calculations.
|
||||||
|
*/
|
||||||
|
public final class AmountUtil {
|
||||||
|
|
||||||
|
public static final int DEFAULT_SCALE = 2;
|
||||||
|
public static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_UP;
|
||||||
|
public static final String KOREAN_WON_SYMBOL = "₩";
|
||||||
|
public static final String USDollar_SYMBOL = "$";
|
||||||
|
public static final String EURO_SYMBOL = "€";
|
||||||
|
|
||||||
|
private static final DecimalFormat KOREAN_WON_FORMAT = new DecimalFormat("#,###");
|
||||||
|
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.##");
|
||||||
|
private static final DecimalFormat CURRENCY_FORMAT = new DecimalFormat("#,###.00");
|
||||||
|
|
||||||
|
private AmountUtil() {
|
||||||
|
throw new UnsupportedOperationException("Utility class cannot be instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigDecimal from a string value.
|
||||||
|
*
|
||||||
|
* @param value the string value to convert
|
||||||
|
* @return the BigDecimal value
|
||||||
|
* @throws NumberFormatException if the string is not a valid number
|
||||||
|
*/
|
||||||
|
public static BigDecimal of(String value) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
return new BigDecimal(value.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigDecimal from a long value.
|
||||||
|
*
|
||||||
|
* @param value the long value to convert
|
||||||
|
* @return the BigDecimal value
|
||||||
|
*/
|
||||||
|
public static BigDecimal of(long value) {
|
||||||
|
return BigDecimal.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigDecimal from a double value.
|
||||||
|
*
|
||||||
|
* @param value the double value to convert
|
||||||
|
* @return the BigDecimal value
|
||||||
|
*/
|
||||||
|
public static BigDecimal of(double value) {
|
||||||
|
return BigDecimal.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds two BigDecimal values.
|
||||||
|
*
|
||||||
|
* @param a the first operand
|
||||||
|
* @param b the second operand
|
||||||
|
* @return the sum
|
||||||
|
*/
|
||||||
|
public static BigDecimal add(BigDecimal a, BigDecimal b) {
|
||||||
|
Objects.requireNonNull(a, "a must not be null");
|
||||||
|
Objects.requireNonNull(b, "b must not be null");
|
||||||
|
return a.add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts the second BigDecimal from the first.
|
||||||
|
*
|
||||||
|
* @param a the minuend
|
||||||
|
* @param b the subtrahend
|
||||||
|
* @return the difference
|
||||||
|
*/
|
||||||
|
public static BigDecimal subtract(BigDecimal a, BigDecimal b) {
|
||||||
|
Objects.requireNonNull(a, "a must not be null");
|
||||||
|
Objects.requireNonNull(b, "b must not be null");
|
||||||
|
return a.subtract(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies two BigDecimal values.
|
||||||
|
*
|
||||||
|
* @param a the first operand
|
||||||
|
* @param b the second operand
|
||||||
|
* @return the product
|
||||||
|
*/
|
||||||
|
public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
|
||||||
|
Objects.requireNonNull(a, "a must not be null");
|
||||||
|
Objects.requireNonNull(b, "b must not be null");
|
||||||
|
return a.multiply(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divides the first BigDecimal by the second.
|
||||||
|
*
|
||||||
|
* @param dividend the dividend
|
||||||
|
* @param divisor the divisor
|
||||||
|
* @return the quotient
|
||||||
|
* @throws ArithmeticException if divisor is zero
|
||||||
|
*/
|
||||||
|
public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor) {
|
||||||
|
Objects.requireNonNull(dividend, "dividend must not be null");
|
||||||
|
Objects.requireNonNull(divisor, "divisor must not be null");
|
||||||
|
if (BigDecimal.ZERO.compareTo(divisor) == 0) {
|
||||||
|
throw new ArithmeticException("Division by zero");
|
||||||
|
}
|
||||||
|
return dividend.divide(divisor, DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divides the first BigDecimal by the second with specified scale and rounding mode.
|
||||||
|
*
|
||||||
|
* @param dividend the dividend
|
||||||
|
* @param divisor the divisor
|
||||||
|
* @param scale the scale
|
||||||
|
* @param roundingMode the rounding mode
|
||||||
|
* @return the quotient
|
||||||
|
* @throws ArithmeticException if divisor is zero
|
||||||
|
*/
|
||||||
|
public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor, int scale, RoundingMode roundingMode) {
|
||||||
|
Objects.requireNonNull(dividend, "dividend must not be null");
|
||||||
|
Objects.requireNonNull(divisor, "divisor must not be null");
|
||||||
|
Objects.requireNonNull(roundingMode, "roundingMode must not be null");
|
||||||
|
if (BigDecimal.ZERO.compareTo(divisor) == 0) {
|
||||||
|
throw new ArithmeticException("Division by zero");
|
||||||
|
}
|
||||||
|
return dividend.divide(divisor, scale, roundingMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds a BigDecimal to the default scale (2) using default rounding mode (HALF_UP).
|
||||||
|
*
|
||||||
|
* @param value the value to round
|
||||||
|
* @return the rounded value
|
||||||
|
*/
|
||||||
|
public static BigDecimal round(BigDecimal value) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
return value.setScale(DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds a BigDecimal to the specified scale using the specified rounding mode.
|
||||||
|
*
|
||||||
|
* @param value the value to round
|
||||||
|
* @param scale the scale
|
||||||
|
* @param roundingMode the rounding mode
|
||||||
|
* @return the rounded value
|
||||||
|
*/
|
||||||
|
public static BigDecimal round(BigDecimal value, int scale, RoundingMode roundingMode) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
Objects.requireNonNull(roundingMode, "roundingMode must not be null");
|
||||||
|
return value.setScale(scale, roundingMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates percentage of a value.
|
||||||
|
*
|
||||||
|
* @param value the base value
|
||||||
|
* @param percentage the percentage (e.g., 10 for 10%)
|
||||||
|
* @return the percentage amount
|
||||||
|
*/
|
||||||
|
public static BigDecimal percentage(BigDecimal value, BigDecimal percentage) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
Objects.requireNonNull(percentage, "percentage must not be null");
|
||||||
|
return value.multiply(percentage).divide(BigDecimal.valueOf(100), DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates percentage of a value and adds it.
|
||||||
|
*
|
||||||
|
* @param value the base value
|
||||||
|
* @param percentage the percentage to add
|
||||||
|
* @return the value plus percentage amount
|
||||||
|
*/
|
||||||
|
public static BigDecimal addPercentage(BigDecimal value, BigDecimal percentage) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
Objects.requireNonNull(percentage, "percentage must not be null");
|
||||||
|
return value.add(percentage(value, percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates percentage of a value and subtracts it.
|
||||||
|
*
|
||||||
|
* @param value the base value
|
||||||
|
* @param percentage the percentage to subtract
|
||||||
|
* @return the value minus percentage amount
|
||||||
|
*/
|
||||||
|
public static BigDecimal subtractPercentage(BigDecimal value, BigDecimal percentage) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
Objects.requireNonNull(percentage, "percentage must not be null");
|
||||||
|
return value.subtract(percentage(value, percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a BigDecimal as a Korean Won amount (e.g., "₩1,000").
|
||||||
|
*
|
||||||
|
* @param amount the amount to format
|
||||||
|
* @return the formatted string
|
||||||
|
*/
|
||||||
|
public static String formatKoreanWon(BigDecimal amount) {
|
||||||
|
Objects.requireNonNull(amount, "amount must not be null");
|
||||||
|
return KOREAN_WON_SYMBOL + KOREAN_WON_FORMAT.format(amount.setScale(0, RoundingMode.DOWN));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a BigDecimal as a currency amount with 2 decimal places.
|
||||||
|
*
|
||||||
|
* @param amount the amount to format
|
||||||
|
* @return the formatted string
|
||||||
|
*/
|
||||||
|
public static String formatCurrency(BigDecimal amount) {
|
||||||
|
Objects.requireNonNull(amount, "amount must not be null");
|
||||||
|
return CURRENCY_FORMAT.format(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a BigDecimal as a decimal number.
|
||||||
|
*
|
||||||
|
* @param amount the amount to format
|
||||||
|
* @return the formatted string
|
||||||
|
*/
|
||||||
|
public static String formatDecimal(BigDecimal amount) {
|
||||||
|
Objects.requireNonNull(amount, "amount must not be null");
|
||||||
|
return DECIMAL_FORMAT.format(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a BigDecimal with a custom pattern.
|
||||||
|
*
|
||||||
|
* @param amount the amount to format
|
||||||
|
* @param pattern the DecimalFormat pattern
|
||||||
|
* @return the formatted string
|
||||||
|
*/
|
||||||
|
public static String format(BigDecimal amount, String pattern) {
|
||||||
|
Objects.requireNonNull(amount, "amount must not be null");
|
||||||
|
Objects.requireNonNull(pattern, "pattern must not be null");
|
||||||
|
DecimalFormat formatter = new DecimalFormat(pattern);
|
||||||
|
return formatter.format(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two BigDecimal values.
|
||||||
|
*
|
||||||
|
* @param a the first value
|
||||||
|
* @param b the second value
|
||||||
|
* @return -1 if a < b, 0 if a == b, 1 if a > b
|
||||||
|
*/
|
||||||
|
public static int compare(BigDecimal a, BigDecimal b) {
|
||||||
|
Objects.requireNonNull(a, "a must not be null");
|
||||||
|
Objects.requireNonNull(b, "b must not be null");
|
||||||
|
return a.compareTo(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the first value is greater than the second.
|
||||||
|
*
|
||||||
|
* @param a the first value
|
||||||
|
* @param b the second value
|
||||||
|
* @return true if a > b
|
||||||
|
*/
|
||||||
|
public static boolean isGreaterThan(BigDecimal a, BigDecimal b) {
|
||||||
|
return compare(a, b) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the first value is less than the second.
|
||||||
|
*
|
||||||
|
* @param a the first value
|
||||||
|
* @param b the second value
|
||||||
|
* @return true if a < b
|
||||||
|
*/
|
||||||
|
public static boolean isLessThan(BigDecimal a, BigDecimal b) {
|
||||||
|
return compare(a, b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the first value is greater than or equal to the second.
|
||||||
|
*
|
||||||
|
* @param a the first value
|
||||||
|
* @param b the second value
|
||||||
|
* @return true if a >= b
|
||||||
|
*/
|
||||||
|
public static boolean isGreaterThanOrEqual(BigDecimal a, BigDecimal b) {
|
||||||
|
return compare(a, b) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the first value is less than or equal to the second.
|
||||||
|
*
|
||||||
|
* @param a the first value
|
||||||
|
* @param b the second value
|
||||||
|
* @return true if a <= b
|
||||||
|
*/
|
||||||
|
public static boolean isLessThanOrEqual(BigDecimal a, BigDecimal b) {
|
||||||
|
return compare(a, b) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the value is zero.
|
||||||
|
*
|
||||||
|
* @param value the value to check
|
||||||
|
* @return true if value is zero
|
||||||
|
*/
|
||||||
|
public static boolean isZero(BigDecimal value) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
return BigDecimal.ZERO.compareTo(value) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the value is positive (greater than zero).
|
||||||
|
*
|
||||||
|
* @param value the value to check
|
||||||
|
* @return true if value > 0
|
||||||
|
*/
|
||||||
|
public static boolean isPositive(BigDecimal value) {
|
||||||
|
return compare(value, BigDecimal.ZERO) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the value is negative (less than zero).
|
||||||
|
*
|
||||||
|
* @param value the value to check
|
||||||
|
* @return true if value < 0
|
||||||
|
*/
|
||||||
|
public static boolean isNegative(BigDecimal value) {
|
||||||
|
return compare(value, BigDecimal.ZERO) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute value.
|
||||||
|
*
|
||||||
|
* @param value the value
|
||||||
|
* @return the absolute value
|
||||||
|
*/
|
||||||
|
public static BigDecimal abs(BigDecimal value) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
return value.abs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Negates the value.
|
||||||
|
*
|
||||||
|
* @param value the value
|
||||||
|
* @return the negated value
|
||||||
|
*/
|
||||||
|
public static BigDecimal negate(BigDecimal value) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
return value.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum of two values.
|
||||||
|
*
|
||||||
|
* @param a the first value
|
||||||
|
* @param b the second value
|
||||||
|
* @return the maximum value
|
||||||
|
*/
|
||||||
|
public static BigDecimal max(BigDecimal a, BigDecimal b) {
|
||||||
|
Objects.requireNonNull(a, "a must not be null");
|
||||||
|
Objects.requireNonNull(b, "b must not be null");
|
||||||
|
return a.max(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum of two values.
|
||||||
|
*
|
||||||
|
* @param a the first value
|
||||||
|
* @param b the second value
|
||||||
|
* @return the minimum value
|
||||||
|
*/
|
||||||
|
public static BigDecimal min(BigDecimal a, BigDecimal b) {
|
||||||
|
Objects.requireNonNull(a, "a must not be null");
|
||||||
|
Objects.requireNonNull(b, "b must not be null");
|
||||||
|
return a.min(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a string that may contain currency symbols and commas.
|
||||||
|
*
|
||||||
|
* @param value the string to parse
|
||||||
|
* @return the parsed BigDecimal
|
||||||
|
*/
|
||||||
|
public static BigDecimal parseAmount(String value) {
|
||||||
|
Objects.requireNonNull(value, "value must not be null");
|
||||||
|
String cleaned = value.replace(KOREAN_WON_SYMBOL, "")
|
||||||
|
.replace(USDollar_SYMBOL, "")
|
||||||
|
.replace(EURO_SYMBOL, "")
|
||||||
|
.replace(",", "")
|
||||||
|
.replace(" ", "")
|
||||||
|
.trim();
|
||||||
|
return new BigDecimal(cleaned);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates if a string is a valid amount.
|
||||||
|
*
|
||||||
|
* @param value the string to validate
|
||||||
|
* @return true if valid, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isValidAmount(String value) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
parseAmount(value);
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts cents/won to decimal amount.
|
||||||
|
*
|
||||||
|
* @param cents the amount in cents
|
||||||
|
* @return the decimal amount
|
||||||
|
*/
|
||||||
|
public static BigDecimal fromCents(long cents) {
|
||||||
|
return BigDecimal.valueOf(cents).divide(BigDecimal.valueOf(100), DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts decimal amount to cents/won.
|
||||||
|
*
|
||||||
|
* @param amount the decimal amount
|
||||||
|
* @return the amount in cents
|
||||||
|
*/
|
||||||
|
public static long toCents(BigDecimal amount) {
|
||||||
|
Objects.requireNonNull(amount, "amount must not be null");
|
||||||
|
return amount.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.DOWN).longValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in a new issue