]> source.dussan.org Git - poi.git/commitdiff
Bugzilla 54469 - Support for financial functions IPMT and PPMT
authorYegor Kozlov <yegor@apache.org>
Sat, 2 Mar 2013 13:17:53 +0000 (13:17 +0000)
committerYegor Kozlov <yegor@apache.org>
Sat, 2 Mar 2013 13:17:53 +0000 (13:17 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1451886 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
src/java/org/apache/poi/ss/formula/functions/Finance.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/functions/IPMT.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/functions/PPMT.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/formula/functions/TestIPMT.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/formula/functions/TestPPMT.java [new file with mode: 0644]
test-data/spreadsheet/FormulaEvalTestData.xls
test-data/spreadsheet/finance.xls [new file with mode: 0644]

index 733b14b26b9fc31e1b6c63118d1cb751a4a146c6..1bae7ed2da6891e3a0898768452786fc98732a24 100644 (file)
@@ -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 (file)
index 0000000..4058419
--- /dev/null
@@ -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 <code>double</code> 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 <code>double</code> 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 <code>double</code> 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 <code>double</code> 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 (file)
index 0000000..35dca8a
--- /dev/null
@@ -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 (file)
index 0000000..68b8683
--- /dev/null
@@ -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 (file)
index 0000000..d0c3e3d
--- /dev/null
@@ -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 (file)
index 0000000..45472f6
--- /dev/null
@@ -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);
+
+    }
+}
index b8139601efb4a39632a1e12bae955cb4bd9add7f..c585b823ef687f1e4bc9d0e3501e85602b5623b1 100644 (file)
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 (file)
index 0000000..1287c71
Binary files /dev/null and b/test-data/spreadsheet/finance.xls differ