use crate::model::ViolationDetails;
pub fn service_non_recurring_should_not_have_interval<'a>(
data: &'a serde_json::Value,
) -> Option<ViolationDetails> {
let service_items = data
.get("itemization")
.and_then(|itemization| itemization.get("service"))
.and_then(|service| service.get("service_items"))
.and_then(|items| items.as_array());
if let Some(items) = service_items {
for (index, item) in items.iter().enumerate() {
let recurring = item.get("recurring").and_then(|r| r.as_bool());
let interval = item.get("interval");
let interval_count = item.get("interval_count");
if recurring == Some(false) {
let has_interval = interval.is_some() && !interval.unwrap().is_null();
let has_interval_count = interval_count.is_some() && !interval_count.unwrap().is_null();
if has_interval || has_interval_count {
return Some(ViolationDetails {
details: Some(format!(
"service_items[{}]: recurring=false but interval={} and interval_count={}",
index,
interval.and_then(|v| v.as_str()).unwrap_or("null"),
interval_count
.and_then(|v| v.as_i64())
.map(|n| n.to_string())
.unwrap_or_else(|| "null".to_string())
)),
});
}
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_non_recurring_without_interval_passes() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": false,
"amount": 1000
}
]
}
}
});
assert!(service_non_recurring_should_not_have_interval(&data).is_none());
}
#[test]
fn test_non_recurring_with_null_interval_passes() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": false,
"interval": null,
"interval_count": null,
"amount": 1000
}
]
}
}
});
assert!(service_non_recurring_should_not_have_interval(&data).is_none());
}
#[test]
fn test_recurring_with_interval_passes() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": true,
"interval": "month",
"interval_count": 1,
"amount": 1000
}
]
}
}
});
assert!(service_non_recurring_should_not_have_interval(&data).is_none());
}
#[test]
fn test_non_recurring_with_interval_fails() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": false,
"interval": "month",
"amount": 1000
}
]
}
}
});
let result = service_non_recurring_should_not_have_interval(&data);
assert!(result.is_some());
let violation = result.unwrap();
assert!(violation.details.is_some());
assert!(violation.details.unwrap().contains("service_items[0]"));
}
#[test]
fn test_non_recurring_with_interval_count_fails() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": false,
"interval_count": 3,
"amount": 1000
}
]
}
}
});
let result = service_non_recurring_should_not_have_interval(&data);
assert!(result.is_some());
}
#[test]
fn test_non_recurring_with_both_interval_and_count_fails() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": false,
"interval": "week",
"interval_count": 2,
"amount": 1000
}
]
}
}
});
let result = service_non_recurring_should_not_have_interval(&data);
assert!(result.is_some());
}
#[test]
fn test_no_service_itemization_passes() {
let data = json!({
"itemization": {
"general": {
"items": []
}
}
});
assert!(service_non_recurring_should_not_have_interval(&data).is_none());
}
#[test]
fn test_multiple_items_first_violates() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": false,
"interval": "month",
"amount": 1000
},
{
"recurring": true,
"interval": "year",
"interval_count": 1,
"amount": 2000
}
]
}
}
});
let result = service_non_recurring_should_not_have_interval(&data);
assert!(result.is_some());
assert!(
result
.unwrap()
.details
.unwrap()
.contains("service_items[0]")
);
}
#[test]
fn test_multiple_items_all_valid() {
let data = json!({
"itemization": {
"service": {
"service_items": [
{
"recurring": false,
"amount": 1000
},
{
"recurring": true,
"interval": "month",
"interval_count": 1,
"amount": 2000
}
]
}
}
});
assert!(service_non_recurring_should_not_have_interval(&data).is_none());
}
}