merge accrint, accrintm #58
This commit is contained in:
committed by
Nicolás Hatcher
parent
050677f905
commit
15b67323ed
@@ -771,6 +771,8 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
|
||||
Function::Duration => args_signature_scalars(arg_count, 5, 1),
|
||||
Function::Mduration => args_signature_scalars(arg_count, 5, 1),
|
||||
Function::Pduration => args_signature_scalars(arg_count, 3, 0),
|
||||
Function::Accrint => args_signature_scalars(arg_count, 6, 2),
|
||||
Function::Accrintm => args_signature_scalars(arg_count, 4, 1),
|
||||
Function::Pmt => args_signature_scalars(arg_count, 3, 2),
|
||||
Function::Ppmt => args_signature_scalars(arg_count, 4, 2),
|
||||
Function::Price => args_signature_scalars(arg_count, 6, 1),
|
||||
@@ -1047,6 +1049,8 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
||||
Function::Duration => not_implemented(args),
|
||||
Function::Mduration => not_implemented(args),
|
||||
Function::Pduration => not_implemented(args),
|
||||
Function::Accrint => not_implemented(args),
|
||||
Function::Accrintm => not_implemented(args),
|
||||
Function::Pmt => not_implemented(args),
|
||||
Function::Ppmt => not_implemented(args),
|
||||
Function::Price => not_implemented(args),
|
||||
|
||||
@@ -92,6 +92,38 @@ fn days360_eu(start: chrono::NaiveDate, end: chrono::NaiveDate) -> i32 {
|
||||
d2 + m2 * 30 + y2 * 360 - d1 - m1 * 30 - y1 * 360
|
||||
}
|
||||
|
||||
fn days_30us_360(start: chrono::NaiveDate, end: chrono::NaiveDate) -> i32 {
|
||||
let mut d1 = start.day() as i32;
|
||||
let mut d2 = end.day() as i32;
|
||||
let m1 = start.month() as i32;
|
||||
let m2 = end.month() as i32;
|
||||
let y1 = start.year();
|
||||
let y2 = end.year();
|
||||
if d1 == 31 {
|
||||
d1 = 30;
|
||||
}
|
||||
if d2 == 31 && (d1 == 30 || d1 == 31) {
|
||||
d2 = 30;
|
||||
}
|
||||
(y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)
|
||||
}
|
||||
|
||||
fn days_30e_360(start: chrono::NaiveDate, end: chrono::NaiveDate) -> i32 {
|
||||
let mut d1 = start.day() as i32;
|
||||
let mut d2 = end.day() as i32;
|
||||
let m1 = start.month() as i32;
|
||||
let m2 = end.month() as i32;
|
||||
let y1 = start.year();
|
||||
let y2 = end.year();
|
||||
if d1 == 31 {
|
||||
d1 = 30;
|
||||
}
|
||||
if d2 == 31 {
|
||||
d2 = 30;
|
||||
}
|
||||
(y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)
|
||||
}
|
||||
|
||||
fn days_between(start: i64, end: i64, basis: i32) -> Result<i32, String> {
|
||||
let start_date = from_excel_date(start)?;
|
||||
let end_date = from_excel_date(end)?;
|
||||
@@ -129,6 +161,22 @@ fn year_diff(start: i64, end: i64, basis: i32) -> Result<f64, String> {
|
||||
year_frac(start, end, basis)
|
||||
}
|
||||
|
||||
fn year_fraction(
|
||||
start: chrono::NaiveDate,
|
||||
end: chrono::NaiveDate,
|
||||
basis: i32,
|
||||
) -> Result<f64, String> {
|
||||
let days = match basis {
|
||||
0 => days_30us_360(start, end) as f64 / 360.0,
|
||||
1 => (end - start).num_days() as f64 / 365.0,
|
||||
2 => (end - start).num_days() as f64 / 360.0,
|
||||
3 => (end - start).num_days() as f64 / 365.0,
|
||||
4 => days_30e_360(start, end) as f64 / 360.0,
|
||||
_ => return Err("Invalid basis".to_string()),
|
||||
};
|
||||
Ok(days)
|
||||
}
|
||||
|
||||
fn compute_payment(
|
||||
rate: f64,
|
||||
nper: f64,
|
||||
@@ -542,6 +590,197 @@ impl Model {
|
||||
CalcResult::Number(result)
|
||||
}
|
||||
|
||||
// ACCRINT(issue, first_interest, settlement, rate, par, freq, [basis], [calc])
|
||||
pub(crate) fn fn_accrint(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||
let arg_count = args.len();
|
||||
if !(6..=8).contains(&arg_count) {
|
||||
return CalcResult::new_args_number_error(cell);
|
||||
}
|
||||
let issue = match self.get_number_no_bools(&args[0], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let first = match self.get_number_no_bools(&args[1], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let settlement = match self.get_number_no_bools(&args[2], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let rate = match self.get_number_no_bools(&args[3], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let par = match self.get_number_no_bools(&args[4], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let freq = match self.get_number_no_bools(&args[5], cell) {
|
||||
Ok(f) => f as i32,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let basis = if arg_count > 6 {
|
||||
match self.get_number_no_bools(&args[6], cell) {
|
||||
Ok(f) => f as i32,
|
||||
Err(s) => return s,
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let calc = if arg_count > 7 {
|
||||
match self.get_number(&args[7], cell) {
|
||||
Ok(f) => f != 0.0,
|
||||
Err(s) => return s,
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if !(freq == 1 || freq == 2 || freq == 4) {
|
||||
return CalcResult::new_error(Error::NUM, cell, "invalid frequency".to_string());
|
||||
}
|
||||
if !(0..=4).contains(&basis) {
|
||||
return CalcResult::new_error(Error::NUM, cell, "invalid basis".to_string());
|
||||
}
|
||||
if par < 0.0 {
|
||||
return CalcResult::new_error(Error::NUM, cell, "par cannot be negative".to_string());
|
||||
}
|
||||
if rate < 0.0 {
|
||||
return CalcResult::new_error(Error::NUM, cell, "rate cannot be negative".to_string());
|
||||
}
|
||||
|
||||
let issue_d = match from_excel_date(issue as i64) {
|
||||
Ok(d) => d,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string()),
|
||||
};
|
||||
let first_d = match from_excel_date(first as i64) {
|
||||
Ok(d) => d,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string()),
|
||||
};
|
||||
let settle_d = match from_excel_date(settlement as i64) {
|
||||
Ok(d) => d,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string()),
|
||||
};
|
||||
|
||||
if settle_d < issue_d {
|
||||
return CalcResult::new_error(Error::NUM, cell, "settlement < issue".to_string());
|
||||
}
|
||||
if first_d < issue_d {
|
||||
return CalcResult::new_error(Error::NUM, cell, "first_interest < issue".to_string());
|
||||
}
|
||||
if settle_d < first_d {
|
||||
return CalcResult::new_error(
|
||||
Error::NUM,
|
||||
cell,
|
||||
"settlement < first_interest".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let months = 12 / freq;
|
||||
let mut prev = first_d;
|
||||
if settle_d <= first_d {
|
||||
prev = issue_d;
|
||||
} else {
|
||||
while prev <= settle_d {
|
||||
let next = prev + chrono::Months::new(months as u32);
|
||||
if next > settle_d {
|
||||
break;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
let next_coupon = prev + chrono::Months::new(months as u32);
|
||||
|
||||
let mut result = 0.0;
|
||||
if calc {
|
||||
let mut next = first_d;
|
||||
while next < prev {
|
||||
result += rate * par / freq as f64;
|
||||
next = next + chrono::Months::new(months as u32);
|
||||
}
|
||||
}
|
||||
|
||||
let days_in_period = match year_fraction(prev, next_coupon, basis) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "invalid basis".to_string()),
|
||||
};
|
||||
let days_elapsed = match year_fraction(prev, settle_d, basis) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "invalid basis".to_string()),
|
||||
};
|
||||
|
||||
result += rate * par / freq as f64
|
||||
* if days_in_period == 0.0 {
|
||||
0.0
|
||||
} else {
|
||||
days_elapsed / days_in_period
|
||||
};
|
||||
CalcResult::Number(result)
|
||||
}
|
||||
|
||||
// ACCRINTM(issue, settlement, rate, par, [basis])
|
||||
pub(crate) fn fn_accrintm(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||
let arg_count = args.len();
|
||||
if !(4..=5).contains(&arg_count) {
|
||||
return CalcResult::new_args_number_error(cell);
|
||||
}
|
||||
let issue = match self.get_number_no_bools(&args[0], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let settlement = match self.get_number_no_bools(&args[1], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let rate = match self.get_number_no_bools(&args[2], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let par = match self.get_number_no_bools(&args[3], cell) {
|
||||
Ok(f) => f,
|
||||
Err(s) => return s,
|
||||
};
|
||||
let basis = if arg_count > 4 {
|
||||
match self.get_number_no_bools(&args[4], cell) {
|
||||
Ok(f) => f as i32,
|
||||
Err(s) => return s,
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if !(0..=4).contains(&basis) {
|
||||
return CalcResult::new_error(Error::NUM, cell, "invalid basis".to_string());
|
||||
}
|
||||
if par < 0.0 {
|
||||
return CalcResult::new_error(Error::NUM, cell, "par cannot be negative".to_string());
|
||||
}
|
||||
if rate < 0.0 {
|
||||
return CalcResult::new_error(Error::NUM, cell, "rate cannot be negative".to_string());
|
||||
}
|
||||
|
||||
let issue_d = match from_excel_date(issue as i64) {
|
||||
Ok(d) => d,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string()),
|
||||
};
|
||||
let settle_d = match from_excel_date(settlement as i64) {
|
||||
Ok(d) => d,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string()),
|
||||
};
|
||||
|
||||
if settle_d < issue_d {
|
||||
return CalcResult::new_error(Error::NUM, cell, "settlement < issue".to_string());
|
||||
}
|
||||
|
||||
let frac = match year_fraction(issue_d, settle_d, basis) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return CalcResult::new_error(Error::NUM, cell, "invalid basis".to_string()),
|
||||
};
|
||||
|
||||
CalcResult::Number(par * rate * frac)
|
||||
}
|
||||
|
||||
// RATE(nper, pmt, pv, [fv], [type], [guess])
|
||||
pub(crate) fn fn_rate(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||
let arg_count = args.len();
|
||||
|
||||
@@ -217,6 +217,8 @@ pub enum Function {
|
||||
Isoweeknum,
|
||||
|
||||
// Financial
|
||||
Accrint,
|
||||
Accrintm,
|
||||
Cumipmt,
|
||||
Cumprinc,
|
||||
Db,
|
||||
@@ -325,7 +327,7 @@ pub enum Function {
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn into_iter() -> IntoIter<Function, 268> {
|
||||
pub fn into_iter() -> IntoIter<Function, 270> {
|
||||
[
|
||||
Function::And,
|
||||
Function::False,
|
||||
@@ -486,6 +488,8 @@ impl Function {
|
||||
Function::WorkdayIntl,
|
||||
Function::Yearfrac,
|
||||
Function::Isoweeknum,
|
||||
Function::Accrint,
|
||||
Function::Accrintm,
|
||||
Function::Pmt,
|
||||
Function::Pv,
|
||||
Function::Rate,
|
||||
@@ -837,6 +841,8 @@ impl Function {
|
||||
"YEARFRAC" => Some(Function::Yearfrac),
|
||||
"ISOWEEKNUM" | "_XLFN.ISOWEEKNUM" => Some(Function::Isoweeknum),
|
||||
// Financial
|
||||
"ACCRINT" => Some(Function::Accrint),
|
||||
"ACCRINTM" => Some(Function::Accrintm),
|
||||
"PMT" => Some(Function::Pmt),
|
||||
"PV" => Some(Function::Pv),
|
||||
"RATE" => Some(Function::Rate),
|
||||
@@ -1088,6 +1094,8 @@ impl fmt::Display for Function {
|
||||
Function::WorkdayIntl => write!(f, "WORKDAY.INTL"),
|
||||
Function::Yearfrac => write!(f, "YEARFRAC"),
|
||||
Function::Isoweeknum => write!(f, "ISOWEEKNUM"),
|
||||
Function::Accrint => write!(f, "ACCRINT"),
|
||||
Function::Accrintm => write!(f, "ACCRINTM"),
|
||||
Function::Pmt => write!(f, "PMT"),
|
||||
Function::Pv => write!(f, "PV"),
|
||||
Function::Rate => write!(f, "RATE"),
|
||||
@@ -1377,6 +1385,9 @@ impl Model {
|
||||
Function::WorkdayIntl => self.fn_workday_intl(args, cell),
|
||||
Function::Yearfrac => self.fn_yearfrac(args, cell),
|
||||
Function::Isoweeknum => self.fn_isoweeknum(args, cell),
|
||||
// Financial
|
||||
Function::Accrint => self.fn_accrint(args, cell),
|
||||
Function::Accrintm => self.fn_accrintm(args, cell),
|
||||
Function::Pmt => self.fn_pmt(args, cell),
|
||||
Function::Pv => self.fn_pv(args, cell),
|
||||
Function::Rate => self.fn_rate(args, cell),
|
||||
|
||||
@@ -11,6 +11,8 @@ mod test_datedif_leap_month_end;
|
||||
mod test_days360_month_end;
|
||||
mod test_degrees_radians;
|
||||
mod test_error_propagation;
|
||||
mod test_fn_accrint;
|
||||
mod test_fn_accrintm;
|
||||
mod test_fn_average;
|
||||
mod test_fn_averageifs;
|
||||
mod test_fn_choose;
|
||||
|
||||
134
base/src/test/test_fn_accrint.rs
Normal file
134
base/src/test/test_fn_accrint.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use crate::{cell::CellValue, test::util::new_empty_model};
|
||||
|
||||
#[test]
|
||||
fn fn_accrint() {
|
||||
let mut model = new_empty_model();
|
||||
model._set("A1", "=DATE(2020,1,1)");
|
||||
model._set("A2", "=DATE(2020,1,1)");
|
||||
model._set("A3", "=DATE(2020,1,31)");
|
||||
model._set("A4", "10%");
|
||||
model._set("A5", "$1,000");
|
||||
model._set("A6", "2");
|
||||
|
||||
model._set("B1", "=ACCRINT(A1,A2,A3,A4,A5,A6)");
|
||||
model._set("C1", "=ACCRINT(A1)");
|
||||
model._set("C2", "=ACCRINT(A1,A2,A3,A4,A5,3)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
match model.get_cell_value_by_ref("Sheet1!B1") {
|
||||
Ok(CellValue::Number(v)) => {
|
||||
assert!((v - 8.333333333333334).abs() < 1e-9);
|
||||
}
|
||||
other => unreachable!("Expected number for B1, got {:?}", other),
|
||||
}
|
||||
assert_eq!(model._get_text("C1"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("C2"), *"#NUM!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_accrint_parameters() {
|
||||
let mut model = new_empty_model();
|
||||
|
||||
model._set("A1", "=DATE(2020,1,1)");
|
||||
model._set("A2", "=DATE(2020,1,1)");
|
||||
model._set("A3", "=DATE(2020,7,1)");
|
||||
model._set("A4", "8%");
|
||||
model._set("A5", "1000");
|
||||
|
||||
model._set("B1", "=ACCRINT(A1,A2,A3,A4,A5,2,0,TRUE)");
|
||||
model._set("B2", "=ACCRINT(A1,A2,A3,A4,A5,2,1,TRUE)");
|
||||
model._set("B3", "=ACCRINT(A1,A2,A3,A4,A5,2,4,TRUE)");
|
||||
model._set("B4", "=ACCRINT(A1,A2,A3,A4,A5,1)");
|
||||
model._set("B5", "=ACCRINT(A1,A2,A3,A4,A5,4)");
|
||||
model._set("B6", "=ACCRINT(A1,A2,A3,A4,A5,2)");
|
||||
model._set("B7", "=ACCRINT(A1,A2,A3,A4,A5,2,0)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
match model.get_cell_value_by_ref("Sheet1!B1") {
|
||||
Ok(CellValue::Number(v)) => {
|
||||
assert!((v - 40.0).abs() < 1e-9);
|
||||
}
|
||||
other => unreachable!("Expected number for B1, got {:?}", other),
|
||||
}
|
||||
|
||||
match (
|
||||
model.get_cell_value_by_ref("Sheet1!B1"),
|
||||
model.get_cell_value_by_ref("Sheet1!B6"),
|
||||
) {
|
||||
(Ok(CellValue::Number(v1)), Ok(CellValue::Number(v2))) => {
|
||||
assert!((v1 - v2).abs() < 1e-12);
|
||||
}
|
||||
other => unreachable!("Expected matching numbers, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_accrint_errors() {
|
||||
let mut model = new_empty_model();
|
||||
|
||||
model._set("A1", "=DATE(2020,1,1)");
|
||||
model._set("A2", "=DATE(2020,1,1)");
|
||||
model._set("A3", "=DATE(2020,7,1)");
|
||||
model._set("A4", "8%");
|
||||
model._set("A5", "1000");
|
||||
|
||||
model._set("B1", "=ACCRINT()");
|
||||
model._set("B2", "=ACCRINT(A1,A2,A3,A4,A5)");
|
||||
model._set("B3", "=ACCRINT(A1,A2,A3,A4,A5,2,0,TRUE,1)");
|
||||
model._set("C1", "=ACCRINT(A1,A2,A3,A4,A5,0)");
|
||||
model._set("C2", "=ACCRINT(A1,A2,A3,A4,A5,3)");
|
||||
model._set("C3", "=ACCRINT(A1,A2,A3,A4,A5,-1)");
|
||||
model._set("D1", "=ACCRINT(A1,A2,A3,A4,A5,2,-1)");
|
||||
model._set("D2", "=ACCRINT(A1,A2,A3,A4,A5,2,5)");
|
||||
model._set("E1", "=ACCRINT(A3,A2,A1,A4,A5,2)");
|
||||
model._set("E2", "=ACCRINT(A1,A3,A1,A4,A5,2)");
|
||||
model._set("F1", "=ACCRINT(A1,A2,A3,A4,0,2)");
|
||||
model._set("F2", "=ACCRINT(A1,A2,A3,A4,-1000,2)");
|
||||
model._set("F3", "=ACCRINT(A1,A2,A3,-8%,A5,2)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_text("B1"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("B2"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("B3"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("C1"), *"#NUM!");
|
||||
assert_eq!(model._get_text("C2"), *"#NUM!");
|
||||
assert_eq!(model._get_text("C3"), *"#NUM!");
|
||||
assert_eq!(model._get_text("D1"), *"#NUM!");
|
||||
assert_eq!(model._get_text("D2"), *"#NUM!");
|
||||
assert_eq!(model._get_text("E1"), *"#NUM!");
|
||||
assert_eq!(model._get_text("E2"), *"#NUM!");
|
||||
assert_eq!(model._get_text("F2"), *"#NUM!");
|
||||
assert_eq!(model._get_text("F3"), *"#NUM!");
|
||||
|
||||
match model.get_cell_value_by_ref("Sheet1!F1") {
|
||||
Ok(CellValue::Number(v)) => {
|
||||
assert!((v - 0.0).abs() < 1e-9);
|
||||
}
|
||||
other => unreachable!("Expected 0 for F1, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_accrint_combined() {
|
||||
let mut model = new_empty_model();
|
||||
model._set("A1", "=DATE(2018,10,15)");
|
||||
model._set("A2", "=DATE(2019,2,1)");
|
||||
model._set("A3", "5%");
|
||||
model._set("A4", "1000");
|
||||
|
||||
model._set("B1", "=ACCRINT(A1,A1,A2,A3,A4,2)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
match model.get_cell_value_by_ref("Sheet1!B1") {
|
||||
Ok(CellValue::Number(v)) => {
|
||||
assert!((v - 14.722222222222221).abs() < 1e-9);
|
||||
}
|
||||
other => unreachable!("Expected number for B1, got {:?}", other),
|
||||
}
|
||||
}
|
||||
122
base/src/test/test_fn_accrintm.rs
Normal file
122
base/src/test/test_fn_accrintm.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use crate::{cell::CellValue, test::util::new_empty_model};
|
||||
|
||||
#[test]
|
||||
fn fn_accrintm() {
|
||||
let mut model = new_empty_model();
|
||||
model._set("A1", "=DATE(2020,1,1)");
|
||||
model._set("A2", "=DATE(2020,7,1)");
|
||||
model._set("A3", "10%");
|
||||
model._set("A4", "$1,000");
|
||||
|
||||
model._set("B1", "=ACCRINTM(A1,A2,A3,A4)");
|
||||
model._set("C1", "=ACCRINTM(A1)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
match model.get_cell_value_by_ref("Sheet1!B1") {
|
||||
Ok(CellValue::Number(v)) => {
|
||||
assert!((v - 50.0).abs() < 1e-9);
|
||||
}
|
||||
other => unreachable!("Expected number for B1, got {:?}", other),
|
||||
}
|
||||
assert_eq!(model._get_text("C1"), *"#ERROR!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_accrintm_parameters() {
|
||||
let mut model = new_empty_model();
|
||||
|
||||
model._set("A1", "=DATE(2020,1,1)");
|
||||
model._set("A2", "=DATE(2020,7,1)");
|
||||
model._set("A3", "8%");
|
||||
model._set("A4", "1000");
|
||||
|
||||
model._set("B1", "=ACCRINTM(A1,A2,A3,A4,0)");
|
||||
model._set("B2", "=ACCRINTM(A1,A2,A3,A4,1)");
|
||||
model._set("B3", "=ACCRINTM(A1,A2,A3,A4,4)");
|
||||
model._set("C1", "=ACCRINTM(A1,A2,A3,A4)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
match (
|
||||
model.get_cell_value_by_ref("Sheet1!B1"),
|
||||
model.get_cell_value_by_ref("Sheet1!B2"),
|
||||
) {
|
||||
(Ok(CellValue::Number(v1)), Ok(CellValue::Number(v2))) => {
|
||||
assert!(v1 > 0.0 && v2 > 0.0);
|
||||
}
|
||||
other => unreachable!("Expected numbers for basis test, got {:?}", other),
|
||||
}
|
||||
|
||||
match (
|
||||
model.get_cell_value_by_ref("Sheet1!B1"),
|
||||
model.get_cell_value_by_ref("Sheet1!C1"),
|
||||
) {
|
||||
(Ok(CellValue::Number(v1)), Ok(CellValue::Number(v2))) => {
|
||||
assert!((v1 - v2).abs() < 1e-12);
|
||||
}
|
||||
other => unreachable!(
|
||||
"Expected matching numbers for default test, got {:?}",
|
||||
other
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_accrintm_errors() {
|
||||
let mut model = new_empty_model();
|
||||
|
||||
model._set("A1", "=DATE(2020,1,1)");
|
||||
model._set("A2", "=DATE(2020,7,1)");
|
||||
model._set("A3", "8%");
|
||||
model._set("A4", "1000");
|
||||
|
||||
model._set("B1", "=ACCRINTM()");
|
||||
model._set("B2", "=ACCRINTM(A1,A2,A3)");
|
||||
model._set("B3", "=ACCRINTM(A1,A2,A3,A4,0,1)");
|
||||
model._set("C1", "=ACCRINTM(A1,A2,A3,A4,-1)");
|
||||
model._set("C2", "=ACCRINTM(A1,A2,A3,A4,5)");
|
||||
model._set("D1", "=ACCRINTM(A2,A1,A3,A4)");
|
||||
model._set("E1", "=ACCRINTM(A1,A2,A3,0)");
|
||||
model._set("E2", "=ACCRINTM(A1,A2,A3,-1000)");
|
||||
model._set("E3", "=ACCRINTM(A1,A2,-8%,A4)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_text("B1"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("B2"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("B3"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("C1"), *"#NUM!");
|
||||
assert_eq!(model._get_text("C2"), *"#NUM!");
|
||||
assert_eq!(model._get_text("D1"), *"#NUM!");
|
||||
assert_eq!(model._get_text("E2"), *"#NUM!");
|
||||
assert_eq!(model._get_text("E3"), *"#NUM!");
|
||||
|
||||
match model.get_cell_value_by_ref("Sheet1!E1") {
|
||||
Ok(CellValue::Number(v)) => {
|
||||
assert!((v - 0.0).abs() < 1e-9);
|
||||
}
|
||||
other => unreachable!("Expected 0 for E1, got {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_accrintm_combined() {
|
||||
let mut model = new_empty_model();
|
||||
model._set("C1", "=DATE(2016,4,5)");
|
||||
model._set("C2", "=DATE(2019,2,1)");
|
||||
model._set("A3", "5%");
|
||||
model._set("A4", "1000");
|
||||
model._set("B2", "=ACCRINTM(C1,C2,A3,A4)");
|
||||
|
||||
model.evaluate();
|
||||
|
||||
match model.get_cell_value_by_ref("Sheet1!B2") {
|
||||
Ok(CellValue::Number(v)) => {
|
||||
assert!((v - 141.11111111111111).abs() < 1e-9);
|
||||
}
|
||||
other => unreachable!("Expected number for B2, got {:?}", other),
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ You can track the progress in this [GitHub issue](https://github.com/ironcalc/Ir
|
||||
|
||||
| Function | Status | Documentation |
|
||||
| ---------- | ---------------------------------------------- | ------------------ |
|
||||
| ACCRINT | <Badge type="info" text="Not implemented yet" /> | – |
|
||||
| ACCRINTM | <Badge type="info" text="Not implemented yet" /> | – |
|
||||
| ACCRINT | <Badge type="tip" text="Available" /> | [ACCRINT](financial/accrint) |
|
||||
| ACCRINTM | <Badge type="tip" text="Available" /> | [ACCRINTM](financial/accrintm) |
|
||||
| AMORDEGRC | <Badge type="info" text="Not implemented yet" /> | – |
|
||||
| AMORLINC | <Badge type="info" text="Not implemented yet" /> | – |
|
||||
| COUPDAYBS | <Badge type="info" text="Not implemented yet" /> | – |
|
||||
|
||||
@@ -7,6 +7,5 @@ lang: en-US
|
||||
# ACCRINT
|
||||
|
||||
::: warning
|
||||
🚧 This function is not yet available in IronCalc.
|
||||
[Follow development here](https://github.com/ironcalc/IronCalc/labels/Functions)
|
||||
🚧 This function is implemented but currently lacks detailed documentation. For guidance, you may refer to the equivalent functionality in [Microsoft Excel documentation](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb).
|
||||
:::
|
||||
@@ -7,6 +7,5 @@ lang: en-US
|
||||
# ACCRINTM
|
||||
|
||||
::: warning
|
||||
🚧 This function is not yet available in IronCalc.
|
||||
[Follow development here](https://github.com/ironcalc/IronCalc/labels/Functions)
|
||||
🚧 This function is implemented but currently lacks detailed documentation. For guidance, you may refer to the equivalent functionality in [Microsoft Excel documentation](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb).
|
||||
:::
|
||||
Reference in New Issue
Block a user