116 lines
3.8 KiB
C#
116 lines
3.8 KiB
C#
|
|
using System.Text;
|
||
|
|
|
||
|
|
namespace Books.Api.AiBookkeeper;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Converts ChartOfAccountsDto to .toon format for AI Bookkeeper.
|
||
|
|
/// The .toon format is a structured text format with meta and accounts sections.
|
||
|
|
/// </summary>
|
||
|
|
public static class ToonFormatConverter
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Convert a chart of accounts to .toon format string.
|
||
|
|
/// </summary>
|
||
|
|
public static string ConvertToToon(ChartOfAccountsDto chartOfAccounts)
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
|
||
|
|
// Header comment
|
||
|
|
sb.AppendLine("# Chart of Accounts for AI Bookkeeper");
|
||
|
|
sb.AppendLine();
|
||
|
|
|
||
|
|
// Meta section
|
||
|
|
sb.AppendLine("meta:");
|
||
|
|
sb.AppendLine(" source: Books API");
|
||
|
|
sb.AppendLine($" organizationId: {chartOfAccounts.CompanyId}");
|
||
|
|
sb.AppendLine(" accountType: expense");
|
||
|
|
sb.AppendLine($" totalAccounts: {chartOfAccounts.Accounts.Count}");
|
||
|
|
sb.AppendLine();
|
||
|
|
|
||
|
|
// Accounts section
|
||
|
|
// Format: number,name,category,vatCode,region,vatRubric,suggestions
|
||
|
|
sb.AppendLine($"accounts[{chartOfAccounts.Accounts.Count}]{{number,name,category,vatCode,region,vatRubric,suggestions}}:");
|
||
|
|
|
||
|
|
foreach (var account in chartOfAccounts.Accounts)
|
||
|
|
{
|
||
|
|
var vatCode = MapVatCode(account.VatCodeId);
|
||
|
|
var region = DetermineRegion(account.VatCodeId);
|
||
|
|
var category = MapCategory(account.AccountType);
|
||
|
|
var suggestions = GenerateSuggestions(account.Name, account.AccountNumber);
|
||
|
|
|
||
|
|
// Format: number,name,category,vatCode,region,vatRubric,suggestions
|
||
|
|
sb.AppendLine($" {account.AccountNumber},{EscapeCommas(account.Name)},{category},{vatCode},{region},,{suggestions}");
|
||
|
|
}
|
||
|
|
|
||
|
|
return sb.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string MapVatCode(string? vatCodeId)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrEmpty(vatCodeId))
|
||
|
|
return "";
|
||
|
|
|
||
|
|
return vatCodeId.ToUpperInvariant() switch
|
||
|
|
{
|
||
|
|
"I25" => "I25",
|
||
|
|
"U25" => "", // Output VAT not relevant for expense accounts
|
||
|
|
"INGEN" => "",
|
||
|
|
_ => vatCodeId
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string DetermineRegion(string? vatCodeId)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrEmpty(vatCodeId))
|
||
|
|
return "";
|
||
|
|
|
||
|
|
return vatCodeId.ToUpperInvariant() switch
|
||
|
|
{
|
||
|
|
"IEUV" => "EU", // EU goods
|
||
|
|
"IEUY" => "EU", // EU services
|
||
|
|
"IVV" => "WORLD", // World goods
|
||
|
|
"IVY" => "WORLD", // World services
|
||
|
|
_ => "" // Empty = available for all regions
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string MapCategory(string accountType)
|
||
|
|
{
|
||
|
|
return accountType switch
|
||
|
|
{
|
||
|
|
"expense" => "Administrationsomkostninger",
|
||
|
|
"cogs" => "Variable omkostninger",
|
||
|
|
"personnel" => "Lønomkostninger",
|
||
|
|
"financial" => "Renteudgifter",
|
||
|
|
_ => "Øvrige omkostninger"
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string GenerateSuggestions(string name, string accountNumber)
|
||
|
|
{
|
||
|
|
// Generate search keywords from account name
|
||
|
|
var suggestions = new List<string>();
|
||
|
|
|
||
|
|
// Add words from name (lowercase, no special chars)
|
||
|
|
var words = name.ToLowerInvariant()
|
||
|
|
.Replace(",", " ")
|
||
|
|
.Replace(".", " ")
|
||
|
|
.Replace("-", " ")
|
||
|
|
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||
|
|
.Where(w => w.Length > 2);
|
||
|
|
|
||
|
|
suggestions.AddRange(words);
|
||
|
|
|
||
|
|
// Add account number as suggestion
|
||
|
|
suggestions.Add(accountNumber);
|
||
|
|
|
||
|
|
return string.Join("|", suggestions.Distinct());
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string EscapeCommas(string value)
|
||
|
|
{
|
||
|
|
// The .toon format uses commas as delimiters, so we need to handle commas in values
|
||
|
|
return value.Replace(",", " ");
|
||
|
|
}
|
||
|
|
}
|