ref #65: new formula functions ACCRINTM and AMORDEGRC
This commit is contained in:
parent
cf8766df83
commit
c89b64c53c
147
calc.go
147
calc.go
|
@ -261,10 +261,12 @@ type formulaFuncs struct {
|
||||||
// Supported formula functions:
|
// Supported formula functions:
|
||||||
//
|
//
|
||||||
// ABS
|
// ABS
|
||||||
|
// ACCRINTM
|
||||||
// ACOS
|
// ACOS
|
||||||
// ACOSH
|
// ACOSH
|
||||||
// ACOT
|
// ACOT
|
||||||
// ACOTH
|
// ACOTH
|
||||||
|
// AMORDEGRC
|
||||||
// AND
|
// AND
|
||||||
// ARABIC
|
// ARABIC
|
||||||
// ASIN
|
// ASIN
|
||||||
|
@ -8312,6 +8314,151 @@ func (fn *formulaFuncs) ENCODEURL(argsList *list.List) formulaArg {
|
||||||
|
|
||||||
// Financial Functions
|
// Financial Functions
|
||||||
|
|
||||||
|
// ACCRINTM function returns the accrued interest for a security that pays
|
||||||
|
// interest at maturity. The syntax of the function is:
|
||||||
|
//
|
||||||
|
// ACCRINTM(issue,settlement,rate,[par],[basis])
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) ACCRINTM(argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() != 4 && argsList.Len() != 5 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "ACCRINTM requires 4 or 5 arguments")
|
||||||
|
}
|
||||||
|
args := list.New().Init()
|
||||||
|
args.PushBack(argsList.Front().Value.(formulaArg))
|
||||||
|
issue := fn.DATEVALUE(args)
|
||||||
|
if issue.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||||
|
}
|
||||||
|
args.Init()
|
||||||
|
args.PushBack(argsList.Front().Next().Value.(formulaArg))
|
||||||
|
settlement := fn.DATEVALUE(args)
|
||||||
|
if settlement.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||||
|
}
|
||||||
|
if settlement.Number < issue.Number {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
|
||||||
|
par := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
|
||||||
|
if rate.Type != ArgNumber || par.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
if par.Number <= 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
basis := newNumberFormulaArg(0)
|
||||||
|
if argsList.Len() == 5 {
|
||||||
|
if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frac := yearFrac(issue.Number, settlement.Number, int(basis.Number))
|
||||||
|
if frac.Type != ArgNumber {
|
||||||
|
return frac
|
||||||
|
}
|
||||||
|
return newNumberFormulaArg(frac.Number * rate.Number * par.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareAmorArgs checking and prepare arguments for the formula functions
|
||||||
|
// AMORDEGRC and AMORLINC.
|
||||||
|
func (fn *formulaFuncs) prepareAmorArgs(name string, argsList *list.List) formulaArg {
|
||||||
|
cost := argsList.Front().Value.(formulaArg).ToNumber()
|
||||||
|
if cost.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires cost to be number argument", name))
|
||||||
|
}
|
||||||
|
if cost.Number < 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires cost >= 0", name))
|
||||||
|
}
|
||||||
|
args := list.New().Init()
|
||||||
|
args.PushBack(argsList.Front().Next().Value.(formulaArg))
|
||||||
|
datePurchased := fn.DATEVALUE(args)
|
||||||
|
if datePurchased.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||||
|
}
|
||||||
|
args.Init()
|
||||||
|
args.PushBack(argsList.Front().Next().Next().Value.(formulaArg))
|
||||||
|
firstPeriod := fn.DATEVALUE(args)
|
||||||
|
if firstPeriod.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||||
|
}
|
||||||
|
if firstPeriod.Number < datePurchased.Number {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
salvage := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
|
||||||
|
if salvage.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
if salvage.Number < 0 || salvage.Number > cost.Number {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
period := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
|
||||||
|
if period.Type != ArgNumber || period.Number < 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
rate := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
|
||||||
|
if rate.Type != ArgNumber || rate.Number < 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
basis := newNumberFormulaArg(0)
|
||||||
|
if argsList.Len() == 7 {
|
||||||
|
if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newListFormulaArg([]formulaArg{cost, datePurchased, firstPeriod, salvage, period, rate, basis})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMORDEGRC function is provided for users of the French accounting system.
|
||||||
|
// The function calculates the prorated linear depreciation of an asset for a
|
||||||
|
// specified accounting period. The syntax of the function is:
|
||||||
|
//
|
||||||
|
// AMORDEGRC(cost,date_purchased,first_period,salvage,period,rate,[basis])
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) AMORDEGRC(argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() != 6 && argsList.Len() != 7 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "AMORDEGRC requires 6 or 7 arguments")
|
||||||
|
}
|
||||||
|
args := fn.prepareAmorArgs("AMORDEGRC", argsList)
|
||||||
|
if args.Type != ArgList {
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
cost, datePurchased, firstPeriod, salvage, period, rate, basis := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6]
|
||||||
|
if rate.Number >= 0.5 {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, "AMORDEGRC requires rate to be < 0.5")
|
||||||
|
}
|
||||||
|
assetsLife, amorCoeff := 1/rate.Number, 2.5
|
||||||
|
if assetsLife < 3 {
|
||||||
|
amorCoeff = 1
|
||||||
|
} else if assetsLife < 5 {
|
||||||
|
amorCoeff = 1.5
|
||||||
|
} else if assetsLife <= 6 {
|
||||||
|
amorCoeff = 2
|
||||||
|
}
|
||||||
|
rate.Number *= amorCoeff
|
||||||
|
frac := yearFrac(datePurchased.Number, firstPeriod.Number, int(basis.Number))
|
||||||
|
if frac.Type != ArgNumber {
|
||||||
|
return frac
|
||||||
|
}
|
||||||
|
nRate := float64(int((frac.Number * cost.Number * rate.Number) + 0.5))
|
||||||
|
cost.Number -= nRate
|
||||||
|
rest := cost.Number - salvage.Number
|
||||||
|
for n := 0; n < int(period.Number); n++ {
|
||||||
|
nRate = float64(int((cost.Number * rate.Number) + 0.5))
|
||||||
|
rest -= nRate
|
||||||
|
if rest < 0 {
|
||||||
|
switch int(period.Number) - n {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
return newNumberFormulaArg(float64(int((cost.Number * 0.5) + 0.5)))
|
||||||
|
default:
|
||||||
|
return newNumberFormulaArg(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cost.Number -= nRate
|
||||||
|
}
|
||||||
|
return newNumberFormulaArg(nRate)
|
||||||
|
}
|
||||||
|
|
||||||
// CUMIPMT function calculates the cumulative interest paid on a loan or
|
// CUMIPMT function calculates the cumulative interest paid on a loan or
|
||||||
// investment, between two specified periods. The syntax of the function is:
|
// investment, between two specified periods. The syntax of the function is:
|
||||||
//
|
//
|
||||||
|
|
36
calc_test.go
36
calc_test.go
|
@ -1232,6 +1232,16 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
// ENCODEURL
|
// ENCODEURL
|
||||||
"=ENCODEURL(\"https://xuri.me/excelize/en/?q=Save As\")": "https%3A%2F%2Fxuri.me%2Fexcelize%2Fen%2F%3Fq%3DSave%20As",
|
"=ENCODEURL(\"https://xuri.me/excelize/en/?q=Save As\")": "https%3A%2F%2Fxuri.me%2Fexcelize%2Fen%2F%3Fq%3DSave%20As",
|
||||||
// Financial Functions
|
// Financial Functions
|
||||||
|
// ACCRINTM
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"12/31/2012\",8%,10000)": "800",
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"12/31/2012\",8%,10000,3)": "800",
|
||||||
|
// AMORDEGRC
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,20%)": "42",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,20%,4)": "42",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,40%,4)": "42",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,25%,4)": "41",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",109,1,25%,4)": "54",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",110,2,25%,4)": "0",
|
||||||
// CUMIPMT
|
// CUMIPMT
|
||||||
"=CUMIPMT(0.05/12,60,50000,1,12,0)": "-2294.97753732664",
|
"=CUMIPMT(0.05/12,60,50000,1,12,0)": "-2294.97753732664",
|
||||||
"=CUMIPMT(0.05/12,60,50000,13,24,0)": "-1833.1000665738893",
|
"=CUMIPMT(0.05/12,60,50000,13,24,0)": "-1833.1000665738893",
|
||||||
|
@ -2291,6 +2301,32 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
// ENCODEURL
|
// ENCODEURL
|
||||||
"=ENCODEURL()": "ENCODEURL requires 1 argument",
|
"=ENCODEURL()": "ENCODEURL requires 1 argument",
|
||||||
// Financial Functions
|
// Financial Functions
|
||||||
|
// ACCRINTM
|
||||||
|
"=ACCRINTM()": "ACCRINTM requires 4 or 5 arguments",
|
||||||
|
"=ACCRINTM(\"\",\"01/01/2012\",8%,10000)": "#VALUE!",
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"\",8%,10000)": "#VALUE!",
|
||||||
|
"=ACCRINTM(\"12/31/2012\",\"01/01/2012\",8%,10000)": "#NUM!",
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"12/31/2012\",\"\",10000)": "#NUM!",
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"12/31/2012\",8%,\"\",10000)": "#NUM!",
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"12/31/2012\",8%,-1,10000)": "#NUM!",
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"12/31/2012\",8%,10000,\"\")": "#NUM!",
|
||||||
|
"=ACCRINTM(\"01/01/2012\",\"12/31/2012\",8%,10000,5)": "invalid basis",
|
||||||
|
// AMORDEGRC
|
||||||
|
"=AMORDEGRC()": "AMORDEGRC requires 6 or 7 arguments",
|
||||||
|
"=AMORDEGRC(\"\",\"01/01/2015\",\"09/30/2015\",20,1,20%)": "AMORDEGRC requires cost to be number argument",
|
||||||
|
"=AMORDEGRC(-1,\"01/01/2015\",\"09/30/2015\",20,1,20%)": "AMORDEGRC requires cost >= 0",
|
||||||
|
"=AMORDEGRC(150,\"\",\"09/30/2015\",20,1,20%)": "#VALUE!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"\",20,1,20%)": "#VALUE!",
|
||||||
|
"=AMORDEGRC(150,\"09/30/2015\",\"01/01/2015\",20,1,20%)": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",\"\",1,20%)": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",-1,1,20%)": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,\"\",20%)": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,-1,20%)": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,\"\")": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,-1)": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,20%,\"\")": "#NUM!",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,50%)": "AMORDEGRC requires rate to be < 0.5",
|
||||||
|
"=AMORDEGRC(150,\"01/01/2015\",\"09/30/2015\",20,1,20%,5)": "invalid basis",
|
||||||
// CUMIPMT
|
// CUMIPMT
|
||||||
"=CUMIPMT()": "CUMIPMT requires 6 arguments",
|
"=CUMIPMT()": "CUMIPMT requires 6 arguments",
|
||||||
"=CUMIPMT(0,0,0,0,0,2)": "#N/A",
|
"=CUMIPMT(0,0,0,0,0,2)": "#N/A",
|
||||||
|
|
Loading…
Reference in New Issue