use std::collections::HashMap;
use chrono::NaiveDate;
use datasynth_core::accounts::{control_accounts, tax_accounts};
use datasynth_core::models::journal_entry::{
BusinessProcess, JournalEntry, JournalEntryHeader, JournalEntryLine, TransactionSource,
};
use datasynth_core::models::{TaxLine, TaxableDocumentType};
pub struct TaxPostingGenerator;
impl TaxPostingGenerator {
pub fn generate_tax_posting_jes(
tax_lines: &[TaxLine],
company_code: &str,
doc_dates: &HashMap<String, NaiveDate>,
fallback_date: NaiveDate,
) -> Vec<JournalEntry> {
let mut jes = Vec::new();
for tax_line in tax_lines {
if tax_line.tax_amount.is_zero() {
continue;
}
let posting_date = doc_dates
.get(&tax_line.document_id)
.copied()
.unwrap_or(fallback_date);
match tax_line.document_type {
TaxableDocumentType::CustomerInvoice => {
jes.push(Self::output_vat_je(tax_line, company_code, posting_date));
}
TaxableDocumentType::VendorInvoice if tax_line.is_deductible => {
jes.push(Self::input_vat_je(tax_line, company_code, posting_date));
}
TaxableDocumentType::VendorInvoice
| TaxableDocumentType::JournalEntry
| TaxableDocumentType::Payment
| TaxableDocumentType::PayrollRun => {}
}
}
jes
}
fn output_vat_je(
tax_line: &TaxLine,
company_code: &str,
posting_date: NaiveDate,
) -> JournalEntry {
let doc_id_str = format!("JE-TAX-OUT-{}", tax_line.id);
let mut je = Self::build_je(
doc_id_str.clone(),
company_code,
posting_date,
format!(
"Output VAT posting for customer invoice {}",
tax_line.document_id
),
);
let uuid = je.header.document_id;
let amount = tax_line.tax_amount;
je.add_line(JournalEntryLine::debit(
uuid,
1,
control_accounts::AR_CONTROL.to_string(),
amount,
));
je.add_line(JournalEntryLine::credit(
uuid,
2,
tax_accounts::VAT_PAYABLE.to_string(),
amount,
));
je
}
fn input_vat_je(
tax_line: &TaxLine,
company_code: &str,
posting_date: NaiveDate,
) -> JournalEntry {
let doc_id_str = format!("JE-TAX-IN-{}", tax_line.id);
let mut je = Self::build_je(
doc_id_str.clone(),
company_code,
posting_date,
format!(
"Input VAT posting for vendor invoice {}",
tax_line.document_id
),
);
let uuid = je.header.document_id;
let amount = tax_line.tax_amount;
je.add_line(JournalEntryLine::debit(
uuid,
1,
tax_accounts::INPUT_VAT.to_string(),
amount,
));
je.add_line(JournalEntryLine::credit(
uuid,
2,
control_accounts::AP_CONTROL.to_string(),
amount,
));
je
}
fn build_je(
_doc_id_str: String,
company_code: &str,
posting_date: NaiveDate,
description: String,
) -> JournalEntry {
let mut header = JournalEntryHeader::new(company_code.to_string(), posting_date);
header.document_type = "TAX_POSTING".to_string();
header.created_by = "TAX_POSTING_ENGINE".to_string();
header.source = TransactionSource::Automated;
header.business_process = Some(BusinessProcess::R2R);
header.header_text = Some(description);
JournalEntry::new(header)
}
}