diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java
index 0ea6a7f107..58628053c0 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java
@@ -14,61 +14,78 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
-public class Pmt extends FinanceFunction {
+/**
+ * Implementation for the PMT() Excel function.
+ *
+ * Syntax:
+ * PMT(rate, nper, pv, fv, type)
+ *
+ * Returns the constant repayment amount required for a loan assuming a constant interest rate.
+ *
+ * rate the loan interest rate.
+ * nper the number of loan repayments.
+ * pv the present value of the future payments (or principle).
+ * fv the future value (default zero) surplus cash at the end of the loan lifetime.
+ * type whether payments are due at the beginning(1) or end(0 - default) of each payment period.
+ *
+ */
+public final class Pmt extends FinanceFunction {
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- double rate = 0, fv = 0, nper = 0, pv = 0, d = 0;
- boolean type = false;
- ValueEval retval = null;
- ValueEval ve = null;
-
- switch (operands.length) {
- default:
- retval = ErrorEval.VALUE_INVALID;
- break;
- case 5:
- ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol);
- if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; }
- type = ((BoolEval) ve).getBooleanValue();
- case 4:
- ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue();
- else { retval = ErrorEval.VALUE_INVALID; break; }
-
- ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
- if (ve instanceof NumericValueEval) nper = ((NumericValueEval) ve).getNumberValue();
- else { retval = ErrorEval.VALUE_INVALID; break; }
-
- ve = singleOperandEvaluate(operands[2], srcRow, srcCol);
- if (ve instanceof NumericValueEval) pv = ((NumericValueEval) ve).getNumberValue();
- else { retval = ErrorEval.VALUE_INVALID; break; }
-
- ve = singleOperandEvaluate(operands[3], srcRow, srcCol);
- if (ve instanceof NumericValueEval) fv = ((NumericValueEval) ve).getNumberValue();
- else { retval = ErrorEval.VALUE_INVALID; break; }
- }
-
- if (retval == null) {
- d = FinanceLib.pmt(rate, nper, pv, fv, type);
- retval = (Double.isNaN(d))
- ? (ValueEval) ErrorEval.VALUE_INVALID
- : (Double.isInfinite(d))
- ? (ValueEval) ErrorEval.NUM_ERROR
- : new NumberEval(d);
- }
- return retval;
- }
+ public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
+
+ if(args.length < 3 || args.length > 5) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ try {
+ // evaluate first three (always present) args
+ double rate = evalArg(args[0], srcRow, srcCol);
+ double nper = evalArg(args[1], srcRow, srcCol);
+ double pv = evalArg(args[2], srcRow, srcCol);
+ double fv = 0;
+ boolean arePaymentsAtPeriodBeginning = false;
+
+ switch (args.length) {
+ case 5:
+ ValueEval ve = singleOperandNumericAsBoolean(args[4], srcRow, srcCol);
+ if (ve instanceof ErrorEval) {
+ return ve;
+ }
+ arePaymentsAtPeriodBeginning = ((BoolEval) ve).getBooleanValue();
+ case 4:
+ fv = evalArg(args[3], srcRow, srcCol);
+ }
+ double d = FinanceLib.pmt(rate, nper, pv, fv, arePaymentsAtPeriodBeginning);
+ if (Double.isNaN(d)) {
+ return (ValueEval) ErrorEval.VALUE_INVALID;
+ }
+ if (Double.isInfinite(d)) {
+ return (ValueEval) ErrorEval.NUM_ERROR;
+ }
+ return new NumberEval(d);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ }
+
+ private double evalArg(Eval arg, int srcRow, short srcCol) throws EvaluationException {
+ ValueEval ve = singleOperandEvaluate(arg, srcRow, srcCol);
+ if(ve instanceof ErrorEval) {
+ throw new EvaluationException((ErrorEval) ve);
+ }
+ if (ve instanceof NumericValueEval) {
+ return ((NumericValueEval) ve).getNumberValue();
+ }
+ throw new EvaluationException(ErrorEval.VALUE_INVALID);
+ }
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
index d3e9c4c412..66d2a1d270 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
@@ -14,7 +14,6 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.hssf.record.formula.functions;
@@ -41,6 +40,7 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestMid.class);
result.addTestSuite(TestMathX.class);
result.addTestSuite(TestMatch.class);
+ result.addTestSuite(TestPmt.class);
result.addTestSuite(TestOffset.class);
result.addTestSuite(TestRowCol.class);
result.addTestSuite(TestSumproduct.class);
@@ -50,5 +50,4 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestXYNumericFunction.class);
return result;
}
-
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java
new file mode 100644
index 0000000000..935615acae
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java
@@ -0,0 +1,87 @@
+/* ====================================================================
+ 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.hssf.record.formula.functions;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
+
+/**
+ *
+ * @author Josh Micich
+ */
+public final class TestPmt extends TestCase {
+
+ private static void confirm(double expected, NumberEval ne) {
+ // only asserting accuracy to 4 fractional digits
+ assertEquals(expected, ne.getNumberValue(), 0.00005);
+ }
+ private static Eval invoke(Eval[] args) {
+ return new Pmt().evaluate(args, -1, (short)-1);
+ }
+ /**
+ * Invocation when not expecting an error result
+ */
+ private static NumberEval invokeNormal(Eval[] args) {
+ Eval ev = invoke(args);
+ if(ev instanceof ErrorEval) {
+ throw new AssertionFailedError("Normal evaluation failed with error code: "
+ + ev.toString());
+ }
+ return (NumberEval) ev;
+ }
+
+ private static void confirm(double expected, double rate, double nper, double pv, double fv, boolean isBeginning) {
+ Eval[] args = {
+ new NumberEval(rate),
+ new NumberEval(nper),
+ new NumberEval(pv),
+ new NumberEval(fv),
+ new NumberEval(isBeginning ? 1 : 0),
+ };
+ confirm(expected, invokeNormal(args));
+ }
+
+
+ public void testBasic() {
+ confirm(-1037.0321, (0.08/12), 10, 10000, 0, false);
+ confirm(-1030.1643, (0.08/12), 10, 10000, 0, true);
+ }
+
+ public void test3args() {
+
+ Eval[] args = {
+ new NumberEval(0.005),
+ new NumberEval(24),
+ new NumberEval(1000),
+ };
+ Eval ev = invoke(args);
+ if(ev instanceof ErrorEval) {
+ ErrorEval err = (ErrorEval) ev;
+ if(err.getErrorCode() == HSSFErrorConstants.ERROR_VALUE) {
+ throw new AssertionFailedError("Identified bug 44691");
+ }
+ }
+
+ confirm(-44.3206, invokeNormal(args));
+ }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestBug44691.java b/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestBug44691.java
deleted file mode 100644
index 59b04b498a..0000000000
--- a/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestBug44691.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/* ====================================================================
- 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.hssf.usermodel;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
-import junit.framework.TestCase;
-
-import org.apache.poi.hssf.util.CellReference;
-
-/**
- * The PMT formula seems to be giving some grief
- */
-public final class TestBug44691 extends TestCase {
- String dirname;
-
- protected void setUp() throws Exception {
- super.setUp();
- dirname = System.getProperty("HSSF.testdata.path");
- }
-
- public void DISABLEDtestBug44691() throws Exception {
- HSSFWorkbook outWorkbook = new HSSFWorkbook();
- HSSFSheet outPMTSheet = outWorkbook.createSheet("PMT Sheet");
- HSSFRow row = outPMTSheet.createRow((short) 0);
- HSSFCell cell = row.createCell((short) 0);
- cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
- cell.setCellFormula("PMT(0.09/12,48,-10000)");
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- outWorkbook.write(baos);
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-
- HSSFWorkbook inWorkbook = new HSSFWorkbook(bais);
-
- HSSFSheet inPMTSheet = inWorkbook.getSheet("PMT Sheet");
- HSSFFormulaEvaluator evaluator = new
- HSSFFormulaEvaluator(inPMTSheet, inWorkbook);
- CellReference cellReference = new CellReference("A1");
- HSSFRow inRow = inPMTSheet.getRow(cellReference.getRow());
- HSSFCell inCell = inRow.getCell(cellReference.getCol());
-
- assertEquals("PMT(0.09/12,48,-10000)",
- inCell.getCellFormula());
- assertEquals(HSSFCell.CELL_TYPE_FORMULA, inCell.getCellType());
-
- evaluator.setCurrentRow(inRow);
- HSSFFormulaEvaluator.CellValue inCellValue =
- evaluator.evaluate(inCell);
-
- assertEquals(0, inCellValue.getErrorValue());
- assertEquals(HSSFCell.CELL_TYPE_NUMERIC, inCellValue.getCellType());
- assertEquals(248.85, inCellValue.getNumberValue(), 0.0001);
- }
-}