// @cona/core/accounting/create-accounting-impact.ts
// Helper: Get Deferred Revenue Configuration
async function getDeferredRevenueConfig(organizationId: string) {
return await prisma.tools.findUnique({
where: {
tool_slug_org_id: {
tool_slug: "deferred_revenue",
org_id: organizationId,
},
},
});
}
// Type-safe settings access
type DeferredRevenueSettings = {
prap_account_id: string;
default_period: DEFERRED_REVENUE_PERIOD;
};
export async function createAccountingImpact({ documentId }: CreateAccountingImpactParams) {
const document = await getDocument(documentId);
// 1. Execute posting matrix to get the accounting entries
const postingMatrixResult = await executePostingMatrix({
documentType: document.type,
document,
});
// postingMatrixResult = [
// { account: "1200", debit: 1200 }, // Debitor
// { account: "8400", credit: 1200 } // Revenue (from posting matrix)
// ]
// 2. ✅ CHECK: Is this a prepaid invoice?
const isPrepaidInvoice =
document.deferred_revenue_start_date && document.deferred_revenue_end_date;
let finalEntries = postingMatrixResult;
let originalRevenueAccount = null;
if (isPrepaidInvoice) {
// 3. ✅ INTERCEPT: Redirect revenue to PRAP account
const deferredRevenueTool = await getDeferredRevenueConfig(organizationId);
if (!deferredRevenueTool?.settings?.prap_account_id) {
throw new Error(
"PRAP account not configured. Please configure deferred revenue tool settings."
);
}
const prapAccount = await prisma.chart_of_accounts.findUnique({
where: { id: deferredRevenueTool.settings.prap_account_id },
});
if (!prapAccount) {
throw new Error(`PRAP account ${deferredRevenueTool.settings.prap_account_id} not found`);
}
// Find the revenue (credit) entry from posting matrix
const revenueEntry = postingMatrixResult.find((e) => e.credit && e.credit > 0);
originalRevenueAccount = revenueEntry?.account;
// Replace revenue account with PRAP account
finalEntries = postingMatrixResult.map((entry) => {
if (entry.credit && entry.credit > 0) {
// This is the revenue entry - replace with PRAP
return {
...entry,
account: prapAccount.account_nr, // 2610 instead of 8400
_originalAccount: originalRevenueAccount, // Store for logging
};
}
return entry;
});
console.log(
`Prepaid invoice detected: Redirecting ${originalRevenueAccount} → ${prapAccount.account_nr} (PRAP)`
);
}
// 4. Create GL entries with (potentially modified) accounts
for (const entry of finalEntries) {
await createGeneralLedger({
documentId,
accountCode: entry.account,
debit: entry.debit,
credit: entry.credit,
});
}
// 5. ✅ LOG INTERCEPTION to audit logs
if (isPrepaidInvoice) {
await log({
message: "Deferred revenue interception applied",
level: "info",
metadata: {
documentId: document.id,
documentNumber: document.nr,
interceptionType: "prepaid_invoice",
originalAccount: originalRevenueAccount,
modifiedAccount: prapAccount.account_nr,
amount: postingMatrixResult.find((e) => e.credit)?.credit || 0,
deferralPeriod: {
start: document.deferred_revenue_start_date,
end: document.deferred_revenue_end_date,
frequency: document.deferred_revenue_period,
},
reason: "Prepaid subscription - revenue will be recognized monthly",
},
});
// Also create activity log entry visible in document history
await createActivityLog({
action: "deferred_revenue_interception",
documentId: document.id,
actorId: "system", // System action
details: `Posting matrix intercepted: Revenue account ${originalRevenueAccount} → PRAP account ${prapAccount.account_nr}. Revenue will be recognized monthly from ${format(document.deferred_revenue_start_date, "MMM yyyy")} to ${format(document.deferred_revenue_end_date, "MMM yyyy")}.`,
metadata: {
interception: {
originalAccount: originalRevenueAccount,
prapAccount: prapAccount.account_nr,
scheduleId: null, // Will be set after schedule creation
},
},
});
}
// 6. ✅ CREATE SCHEDULE if prepaid
if (isPrepaidInvoice && originalRevenueAccount) {
const schedule = await createDeferredRevenueSchedule({
document,
prapAccount,
originalRevenueAccount,
amount: postingMatrixResult.find((e) => e.credit)?.credit || 0,
});
// Update activity log with schedule ID
await updateActivityLog({
documentId: document.id,
action: "deferred_revenue_interception",
updateMetadata: {
"interception.scheduleId": schedule.id,
},
});
}
}
async function createDeferredRevenueSchedule({
document,
prapAccount,
originalRevenueAccount,
amount,
deferredRevenueTool,
}: CreateScheduleParams) {
// 1. Get period and dates from document fields (set by integration)
const period = document.deferred_revenue_period || deferredRevenueTool.settings.default_period;
const startDate = document.deferred_revenue_start_date;
const endDate = document.deferred_revenue_end_date;
// 2. VALIDATION: Dates are required - integration must provide them
if (!startDate || !endDate) {
throw new Error(
`Document ${document.id} missing required deferred revenue dates. ` +
`Integration must set deferred_revenue_start_date and deferred_revenue_end_date ` +
`from subscription billing period.`
);
}
if (!document.deferred_revenue_period) {
console.warn(
`Document ${document.id} missing deferred_revenue_period. ` +
`Using fallback: ${period} from tool settings`
);
}
// 3. Create deferred revenue schedule
const schedule = await prisma.deferred_revenue_schedules.create({
data: {
source_document_id: document.id,
org_id: document.org_id,
total_amount: amount,
currency_code: document.currency,
start_date: startDate,
end_date: endDate,
recognition_frequency: period, // Already DEFERRED_REVENUE_PERIOD enum value
deferred_revenue_account_id: prapAccount.id, // 2610 (PRAP)
target_revenue_account_id: originalRevenueAccount, // 8400 (from posting matrix)
status: "active",
},
});
// 4. Link schedule back to document
await prisma.documents.update({
where: { id: document.id },
data: {
deferred_revenue_schedule_id: schedule.id,
},
});
console.log(
`Created deferred revenue schedule ${schedule.id}: ` +
`${amount} from ${prapAccount.account_nr} → ${originalRevenueAccount} ` +
`over ${differenceInMonths(endDate, startDate)} months`
);
}