use super::state::App;
use crate::db::transaction_store::TransactionStore;
use crate::model::{RecurrenceFrequency, Transaction};
use crate::recurring::{generate_recurring_transactions, remove_generated_recurring_transactions};
use chrono::{Duration, NaiveDate};
impl App {
pub(crate) fn generate_recurring_transactions(&mut self) {
remove_generated_recurring_transactions(&mut self.transactions);
let recurring_transactions: Vec<Transaction> = self
.transactions
.iter()
.filter(|tx| tx.is_recurring)
.cloned()
.collect();
let today = chrono::Local::now().date_naive();
let generated = generate_recurring_transactions(&recurring_transactions, today);
self.transactions.extend(generated);
self.sort_transactions();
self.apply_filter();
self.calculate_monthly_summaries();
self.calculate_category_summaries();
}
pub(crate) fn start_recurring_settings(&mut self) {
if let Some(view_index) = self.table_state.selected() {
if let Some(original_index) = self.get_original_index(view_index) {
let tx = self.transactions[original_index].clone();
if let Some(target_index) = self.jump_to_original_if_needed(
&tx,
original_index,
crate::app::util::JumpToOriginalAction::RecurringSettings,
) {
let target_tx = &self.transactions[target_index];
self.mode = crate::app::state::AppMode::RecurringSettings;
self.recurring_transaction_index = Some(target_index);
self.current_recurring_field = 0;
self.recurring_settings_fields[0] =
if target_tx.is_recurring { "Yes" } else { "No" }.to_string();
self.recurring_settings_fields[1] = target_tx
.recurrence_frequency
.map(|f| f.to_string().to_string())
.unwrap_or(String::from("Monthly"));
self.recurring_settings_fields[2] = target_tx
.recurrence_end_date
.map(|d| d.format(crate::model::DATE_FORMAT).to_string())
.unwrap_or_default();
if target_index == original_index {
self.clear_status_message();
}
}
} else {
self.set_status_message("Error: Could not map view index to transaction", None);
}
} else {
self.set_status_message("Select a transaction to configure recurring settings", None);
}
}
pub(crate) fn exit_recurring_settings(&mut self, cancelled: bool) {
self.mode = crate::app::state::AppMode::Normal;
self.recurring_transaction_index = None;
self.current_recurring_field = 0;
self.recurring_settings_fields = Default::default();
if cancelled {
self.set_status_message("Recurring settings cancelled.", Some(Duration::seconds(3)));
}
}
pub(crate) fn save_recurring_settings(&mut self) {
if let Some(index) = self.recurring_transaction_index {
if index < self.transactions.len() {
let is_recurring = self.recurring_settings_fields[0].to_lowercase() == "yes";
let frequency_str = &self.recurring_settings_fields[1];
let end_date_str = &self.recurring_settings_fields[2];
let frequency = if is_recurring {
Some(
RecurrenceFrequency::from_label(frequency_str)
.unwrap_or(RecurrenceFrequency::Monthly),
)
} else {
None
};
let end_date = if !end_date_str.is_empty() {
match NaiveDate::parse_from_str(end_date_str, crate::model::DATE_FORMAT) {
Ok(date) => Some(date),
Err(_) => {
self.set_status_message(
format!(
"Error: Invalid end date format (Expected {})",
crate::model::DATE_FORMAT
),
None,
);
return;
}
}
} else {
None
};
let Some(id) = self.transactions[index].id else {
self.set_status_message("Error: transaction has no database id", None);
self.exit_recurring_settings(true);
return;
};
let mut draft = self.transactions[index].to_draft();
draft.is_recurring = is_recurring;
draft.recurrence_frequency = frequency;
draft.recurrence_end_date = end_date;
match self
.transaction_store()
.update(id, &draft)
.and_then(|_| self.reload_transactions_from_db())
{
Ok(_) => {
self.set_status_message(
"Recurring settings saved successfully.",
Some(Duration::seconds(3)),
);
self.exit_recurring_settings(false);
}
Err(e) => {
self.set_status_message(
format!("Error saving recurring settings: {}", e),
None,
);
}
}
} else {
self.set_status_message("Error: Invalid transaction index", None);
self.exit_recurring_settings(true);
}
} else {
self.set_status_message(
"Error: No transaction selected for recurring settings",
None,
);
self.exit_recurring_settings(true);
}
}
pub(crate) fn next_recurring_field(&mut self) {
self.current_recurring_field = (self.current_recurring_field + 1) % 3;
}
pub(crate) fn previous_recurring_field(&mut self) {
self.current_recurring_field = if self.current_recurring_field == 0 {
2
} else {
self.current_recurring_field - 1
};
}
pub(crate) fn toggle_recurring_enabled(&mut self) {
if self.current_recurring_field == 0 {
self.recurring_settings_fields[0] = if self.recurring_settings_fields[0] == "Yes" {
"No".to_string()
} else {
"Yes".to_string()
};
}
}
pub(crate) fn start_frequency_selection(&mut self) {
if self.current_recurring_field == 1 {
self.mode = crate::app::state::AppMode::SelectingRecurrenceFrequency;
self.current_selection_list = RecurrenceFrequency::all()
.iter()
.map(|f| f.to_string().to_string())
.collect();
self.selection_list_state.select(Some(0));
}
}
pub(crate) fn insert_char_recurring(&mut self, c: char) {
if self.current_recurring_field == 2 {
if let Some(new_date) = crate::validation::validate_and_insert_date_char(
&self.recurring_settings_fields[2],
c,
) {
self.recurring_settings_fields[2] = new_date;
self.clear_status_message(); } else {
self.set_status_message("Invalid date input. Use YYYY-MM-DD format.", None);
}
}
}
pub(crate) fn delete_char_recurring(&mut self) {
if self.current_recurring_field == 2 {
crate::validation::handle_date_backspace(&mut self.recurring_settings_fields[2]);
self.clear_status_message();
}
}
pub(crate) fn increment_date_recurring(&mut self) {
if self.current_recurring_field == 2
&& let Some(new_date) = self.increment_date_field(&self.recurring_settings_fields[2])
{
self.recurring_settings_fields[2] = new_date;
self.clear_status_message();
}
}
pub(crate) fn decrement_date_recurring(&mut self) {
if self.current_recurring_field == 2
&& let Some(new_date) = self.decrement_date_field(&self.recurring_settings_fields[2])
{
self.recurring_settings_fields[2] = new_date;
self.clear_status_message();
}
}
pub(crate) fn increment_month_recurring(&mut self) {
if self.current_recurring_field == 2
&& let Some(new_date) = self.increment_month_field(&self.recurring_settings_fields[2])
{
self.recurring_settings_fields[2] = new_date;
self.clear_status_message();
}
}
pub(crate) fn decrement_month_recurring(&mut self) {
if self.current_recurring_field == 2
&& let Some(new_date) = self.decrement_month_field(&self.recurring_settings_fields[2])
{
self.recurring_settings_fields[2] = new_date;
self.clear_status_message();
}
}
}