From: Yegor Kozlov Date: Sat, 2 Mar 2013 13:17:53 +0000 (+0000) Subject: Bugzilla 54469 - Support for financial functions IPMT and PPMT X-Git-Tag: 3.10-beta1~51 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ec930fd66aa22cb039b5cca52b0d297fe7208889;p=poi.git Bugzilla 54469 - Support for financial functions IPMT and PPMT git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1451886 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java index 733b14b26b..1bae7ed2da 100644 --- a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java @@ -161,6 +161,8 @@ public final class FunctionEval { retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature retval[162] = TextFunction.CLEAN; //Aniket Banerjee + retval[167] = new IPMT(); + retval[168] = new PPMT(); retval[169] = new Counta(); retval[183] = AggregateFunction.PRODUCT; diff --git a/src/java/org/apache/poi/ss/formula/functions/Finance.java b/src/java/org/apache/poi/ss/formula/functions/Finance.java new file mode 100644 index 0000000000..4058419125 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/Finance.java @@ -0,0 +1,155 @@ +package org.apache.poi.ss.formula.functions; + + +/** + * Implementation of the financial functions pmt, fv, ppmt, ipmt. + * + * @author Mike Argyriou micharg@gmail.com + */ +public class Finance { + + /** + * Emulates Excel/Calc's PMT(interest_rate, number_payments, PV, FV, Type) + * function, which calculates the payments for a loan or the future value of an investment + * + * @param r + * - periodic interest rate represented as a decimal. + * @param nper + * - number of total payments / periods. + * @param pv + * - present value -- borrowed or invested principal. + * @param fv + * - future value of loan or annuity. + * @param type + * - when payment is made: beginning of period is 1; end, 0. + * @return double representing periodic payment amount. + */ + // http://arachnoid.com/lutusp/finance.html + static public double pmt(double r, int nper, double pv, double fv, int type) { + double pmt = -r * (pv * Math.pow(1 + r, nper) + fv) / ((1 + r*type) * (Math.pow(1 + r, nper) - 1)); + return pmt; + } + + + /** + * Overloaded pmt() call omitting type, which defaults to 0. + * + * @see #pmt(double, int, double, double, int) + */ + static public double pmt(double r, int nper, double pv, double fv) { + return pmt(r, nper, pv, fv, 0); + } + + /** + * Overloaded pmt() call omitting fv and type, which both default to 0. + * + * @see #pmt(double, int, double, double, int) + */ + static public double pmt(double r, int nper, double pv) { + return pmt(r, nper, pv, 0); + } + + + /** + * Emulates Excel/Calc's IPMT(interest_rate, period, number_payments, PV, + * FV, Type) function, which calculates the portion of the payment at a + * given period that is the interest on previous balance. + * + * @param r + * - periodic interest rate represented as a decimal. + * @param per + * - period (payment number) to check value at. + * @param nper + * - number of total payments / periods. + * @param pv + * - present value -- borrowed or invested principal. + * @param fv + * - future value of loan or annuity. + * @param type + * - when payment is made: beginning of period is 1; end, 0. + * @return double representing interest portion of payment. + * + * @see #pmt(double, int, double, double, int) + * @see #fv(double, int, double, double, int) + */ + // http://doc.optadata.com/en/dokumentation/application/expression/functions/financial.html + static public double ipmt(double r, int per, int nper, double pv, double fv, int type) { + double ipmt = fv(r, per - 1, pmt(r, nper, pv, fv, type), pv, type) * r; + if (type==1) ipmt /= (1 + r); + return ipmt; + } + + static public double ipmt(double r, int per, int nper, double pv, double fv) { + return ipmt(r, per, nper, pv, fv, 0); + } + + static public double ipmt(double r, int per, int nper, double pv) { + return ipmt(r, per, nper, pv, 0); + } + + /** + * Emulates Excel/Calc's PPMT(interest_rate, period, number_payments, PV, + * FV, Type) function, which calculates the portion of the payment at a + * given period that will apply to principal. + * + * @param r + * - periodic interest rate represented as a decimal. + * @param per + * - period (payment number) to check value at. + * @param nper + * - number of total payments / periods. + * @param pv + * - present value -- borrowed or invested principal. + * @param fv + * - future value of loan or annuity. + * @param type + * - when payment is made: beginning of period is 1; end, 0. + * @return double representing principal portion of payment. + * + * @see #pmt(double, int, double, double, int) + * @see #ipmt(double, int, int, double, double, boolean) + */ + static public double ppmt(double r, int per, int nper, double pv, double fv, int type) { + return pmt(r, nper, pv, fv, type) - ipmt(r, per, nper, pv, fv, type); + } + + static public double ppmt(double r, int per, int nper, double pv, double fv) { + return pmt(r, nper, pv, fv) - ipmt(r, per, nper, pv, fv); + } + + static public double ppmt(double r, int per, int nper, double pv) { + return pmt(r, nper, pv) - ipmt(r, per, nper, pv); + } + + /** + * Emulates Excel/Calc's FV(interest_rate, number_payments, payment, PV, + * Type) function, which calculates future value or principal at period N. + * + * @param r + * - periodic interest rate represented as a decimal. + * @param nper + * - number of total payments / periods. + * @param pmt + * - periodic payment amount. + * @param pv + * - present value -- borrowed or invested principal. + * @param type + * - when payment is made: beginning of period is 1; end, 0. + * @return double representing future principal value. + */ + //http://en.wikipedia.org/wiki/Future_value + static public double fv(double r, int nper, double pmt, double pv, int type) { + double fv = -(pv * Math.pow(1 + r, nper) + pmt * (1+r*type) * (Math.pow(1 + r, nper) - 1) / r); + return fv; + } + + /** + * Overloaded fv() call omitting type, which defaults to 0. + * + * @see #fv(double, int, double, double, int) + */ + static public double fv(double r, int nper, double c, double pv) { + return fv(r, nper, c, pv, 0); + } +} + diff --git a/src/java/org/apache/poi/ss/formula/functions/IPMT.java b/src/java/org/apache/poi/ss/formula/functions/IPMT.java new file mode 100644 index 0000000000..35dca8a2f6 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/IPMT.java @@ -0,0 +1,56 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ + +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.ss.formula.eval.ErrorEval; +import org.apache.poi.ss.formula.eval.EvaluationException; +import org.apache.poi.ss.formula.eval.OperandResolver; +import org.apache.poi.ss.formula.eval.ValueEval; + +public class IPMT extends NumericFunction { + + @Override + public double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException { + + if(args.length != 4) + throw new EvaluationException(ErrorEval.VALUE_INVALID); + + double result; + + ValueEval v1 = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); + ValueEval v2 = OperandResolver.getSingleValue(args[1], srcCellRow, srcCellCol); + ValueEval v3 = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); + ValueEval v4 = OperandResolver.getSingleValue(args[3], srcCellRow, srcCellCol); + + double interestRate = OperandResolver.coerceValueToDouble(v1); + int period = OperandResolver.coerceValueToInt(v2); + int numberPayments = OperandResolver.coerceValueToInt(v3); + double PV = OperandResolver.coerceValueToDouble(v4); + + result = Finance.ipmt(interestRate, period, numberPayments, PV) ; + + checkValue(result); + + return result; + } + + + +} diff --git a/src/java/org/apache/poi/ss/formula/functions/PPMT.java b/src/java/org/apache/poi/ss/formula/functions/PPMT.java new file mode 100644 index 0000000000..68b8683323 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/PPMT.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ + +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.ss.formula.*; +import org.apache.poi.ss.formula.functions.*; +import org.apache.poi.ss.formula.eval.*; + +/** + * Compute the interest portion of a payment. + * + * @author Mike Argyriou micharg@gmail.com + */ +public class PPMT extends NumericFunction { + + @Override + public double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException { + + if(args.length < 4) + throw new EvaluationException(ErrorEval.VALUE_INVALID); + + double result; + + ValueEval v1 = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); + ValueEval v2 = OperandResolver.getSingleValue(args[1], srcCellRow, srcCellCol); + ValueEval v3 = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); + ValueEval v4 = OperandResolver.getSingleValue(args[3], srcCellRow, srcCellCol); + + double interestRate = OperandResolver.coerceValueToDouble(v1); + int period = OperandResolver.coerceValueToInt(v2); + int numberPayments = OperandResolver.coerceValueToInt(v3); + double PV = OperandResolver.coerceValueToDouble(v4); + + result = Finance.ppmt(interestRate, period, numberPayments, PV) ; + + checkValue(result); + + return result; + } + + + +} diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestIPMT.java b/src/testcases/org/apache/poi/ss/formula/functions/TestIPMT.java new file mode 100644 index 0000000000..d0c3e3d305 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestIPMT.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ + +package org.apache.poi.ss.formula.functions; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.formula.OperationEvaluationContext; +import org.apache.poi.ss.formula.eval.*; + +/** + * Test cases for IPMT() + * + */ +public final class TestIPMT extends TestCase { + + + /** + * from http://office.microsoft.com/en-001/excel-help/ipmt-HP005209145.aspx + */ + public void testFromFile() { + + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("finance.xls"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + + HSSFSheet example1 = wb.getSheet("IPMT"); + HSSFCell ex1cell1 = example1.getRow(6).getCell(0); + fe.evaluate(ex1cell1); + assertEquals(-22.41, ex1cell1.getNumericCellValue(), 0.1); + + HSSFCell ex1cell2 = example1.getRow(7).getCell(0); + fe.evaluate(ex1cell2); + assertEquals(-292.45, ex1cell2.getNumericCellValue(), 0.1); + + } +} diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestPPMT.java b/src/testcases/org/apache/poi/ss/formula/functions/TestPPMT.java new file mode 100644 index 0000000000..45472f6dc4 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestPPMT.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ + +package org.apache.poi.ss.formula.functions; + +import junit.framework.TestCase; +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; + +/** + * Test cases for PPMT() + * + */ +public final class TestPPMT extends TestCase { + + + /** + * http://office.microsoft.com/en-001/excel-help/ppmt-function-HP010342774.aspx + */ + public void testFromFile() { + + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("finance.xls"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + + HSSFSheet example1 = wb.getSheet("PPMT"); + HSSFCell ex1cell1 = example1.getRow(5).getCell(0); + fe.evaluate(ex1cell1); + assertEquals(-75.62, ex1cell1.getNumericCellValue(), 0.1); + + HSSFCell ex1cell2 = example1.getRow(16).getCell(0); + fe.evaluate(ex1cell2); + assertEquals(-27598.05, ex1cell2.getNumericCellValue(), 0.1); + + } +} diff --git a/test-data/spreadsheet/FormulaEvalTestData.xls b/test-data/spreadsheet/FormulaEvalTestData.xls index b8139601ef..c585b823ef 100644 Binary files a/test-data/spreadsheet/FormulaEvalTestData.xls and b/test-data/spreadsheet/FormulaEvalTestData.xls differ diff --git a/test-data/spreadsheet/finance.xls b/test-data/spreadsheet/finance.xls new file mode 100644 index 0000000000..1287c71d02 Binary files /dev/null and b/test-data/spreadsheet/finance.xls differ