diff --git a/calc.go b/calc.go index e867d2fd23..9784ea6059 100644 --- a/calc.go +++ b/calc.go @@ -478,6 +478,7 @@ type formulaFuncs struct { // DISC // DMAX // DMIN +// DOLLAR // DOLLARDE // DOLLARFR // DPRODUCT @@ -16341,6 +16342,49 @@ func (fn *formulaFuncs) DISC(argsList *list.List) formulaArg { return fn.discIntrate("DISC", argsList) } +// DOLLAR function rounds a supplied number to a specified number of decimal +// places and then converts this into a text string with a currency format. The +// syntax of the function is: +// +// DOLLAR(number,[decimals]) +func (fn *formulaFuncs) DOLLAR(argsList *list.List) formulaArg { + if argsList.Len() == 0 { + return newErrorFormulaArg(formulaErrorVALUE, "DOLLAR requires at least 1 argument") + } + if argsList.Len() > 2 { + return newErrorFormulaArg(formulaErrorVALUE, "DOLLAR requires 1 or 2 arguments") + } + numArg := argsList.Front().Value.(formulaArg) + n := numArg.ToNumber() + if n.Type != ArgNumber { + return n + } + decimals, dot, value := 2, ".", numArg.Value() + if argsList.Len() == 2 { + d := argsList.Back().Value.(formulaArg).ToNumber() + if d.Type != ArgNumber { + return d + } + if d.Number < 0 { + value = strconv.FormatFloat(fn.round(n.Number, d.Number, down), 'f', -1, 64) + } + if d.Number >= 128 { + return newErrorFormulaArg(formulaErrorVALUE, "decimal value should be less than 128") + } + if decimals = int(d.Number); decimals < 0 { + decimals, dot = 0, "" + } + } + symbol := map[CultureName]string{ + CultureNameUnknown: "$", + CultureNameEnUS: "$", + CultureNameZhCN: "¥", + }[fn.f.options.CultureInfo] + numFmtCode := fmt.Sprintf("%s#,##0%s%s;(%s#,##0%s%s)", + symbol, dot, strings.Repeat("0", decimals), symbol, dot, strings.Repeat("0", decimals)) + return newStringFormulaArg(format(value, numFmtCode, false, CellTypeNumber, nil)) +} + // DOLLARDE function converts a dollar value in fractional notation, into a // dollar value expressed as a decimal. The syntax of the function is: // diff --git a/calc_test.go b/calc_test.go index 868533c088..c2ae332f64 100644 --- a/calc_test.go +++ b/calc_test.go @@ -2117,6 +2117,16 @@ func TestCalcCellValue(t *testing.T) { "=DDB(10000,1000,5,5)": "296", // DISC "=DISC(\"04/01/2016\",\"03/31/2021\",95,100)": "0.01", + // DOLLAR + "=DOLLAR(1234.56)": "$1,234.56", + "=DOLLAR(1234.56,0)": "$1,235", + "=DOLLAR(1234.56,1)": "$1,234.6", + "=DOLLAR(1234.56,2)": "$1,234.56", + "=DOLLAR(1234.56,3)": "$1,234.560", + "=DOLLAR(1234.56,-2)": "$1,200", + "=DOLLAR(1234.56,-3)": "$1,000", + "=DOLLAR(-1234.56,3)": "($1,234.560)", + "=DOLLAR(-1234.56,-3)": "($1,000)", // DOLLARDE "=DOLLARDE(1.01,16)": "1.0625", // DOLLARFR @@ -4250,6 +4260,12 @@ func TestCalcCellValue(t *testing.T) { "=DISC(\"04/01/2016\",\"03/31/2021\",0,100)": {"#NUM!", "DISC requires pr > 0"}, "=DISC(\"04/01/2016\",\"03/31/2021\",95,0)": {"#NUM!", "DISC requires redemption > 0"}, "=DISC(\"04/01/2016\",\"03/31/2021\",95,100,5)": {"#NUM!", "invalid basis"}, + // DOLLAR + "DOLLAR()": {"#VALUE!", "DOLLAR requires at least 1 argument"}, + "DOLLAR(0,0,0)": {"#VALUE!", "DOLLAR requires 1 or 2 arguments"}, + "DOLLAR(\"\")": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"}, + "DOLLAR(0,\"\")": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"}, + "DOLLAR(1,200)": {"#VALUE!", "decimal value should be less than 128"}, // DOLLARDE "=DOLLARDE()": {"#VALUE!", "DOLLARDE requires 2 arguments"}, "=DOLLARDE(\"\",0)": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},