Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 1x 1x 1x | import { BudgetsService, TransactionsService, TransactionTypeProperty } from "@billos/firefly-iii-sdk"
import pino from "pino"
import { client } from "../../client"
import DynamicConfig, { VConfig } from "../../modules/config/dynamic"
import { getNotifier } from "../../modules/notifiers"
import { getBudgetName } from "../../utils/budgetName"
import { bindTransactionToNotification } from "../../utils/notification"
import { renderTemplate, TemplateName } from "../../utils/renderTemplate"
import { addBudgetJobToQueue, addTransactionJobToQueue } from "../utils"
import { TransactionJob } from "./BaseJob"
import { CheckBudgetLimitJob } from "./checkBudgetLimit"
const logger = pino()
export class UnbudgetedTransactionsJob extends TransactionJob {
readonly id = "unbudgeted-transactions"
override readonly startDelay = 5
async run(id: string): Promise<void> {
logger.info("Checking that transaction %s exists", id)
try {
await TransactionsService.getTransaction({ client, path: { id } })
} catch {
logger.error("Transaction %s does not exist", id)
return
}
logger.info("Creating a new message for unbudgeted transaction with key %s", id)
const {
data: {
attributes: {
transactions: [transaction],
},
},
} = await TransactionsService.getTransaction({ client, path: { id } })
// Ensure the transaction is a withdrawal
const { type } = transaction
if (type !== TransactionTypeProperty.WITHDRAWAL) {
logger.info("Transaction %s is not a withdrawal", id)
return
}
if (!transaction) {
logger.info("Transaction %s not found", id)
return
}
if (transaction.budget_id) {
// We can assume that if a budget_id is set, the budget limit might need to be checked
await addBudgetJobToQueue(new CheckBudgetLimitJob(), transaction.budget_id)
logger.info("Transaction %s already budgeted", id)
return
}
const billsBudgetId = await DynamicConfig.get(VConfig.RoleBudgetBillsId)
if (!billsBudgetId) {
logger.error("Bills budget ID is not configured. Please set it in the environment variables or in Redis.")
return
}
const billsBudgetName = await getBudgetName(billsBudgetId)
const { data: allBudgets } = await BudgetsService.listBudget({ client, query: { page: 1, limit: 50 } })
const budgets = allBudgets.filter(({ attributes: { name } }) => name !== billsBudgetName)
const { title, content } = await renderTemplate(TemplateName.UnbudgetedTransaction, {
transaction,
transactionId: id,
budgets,
})
const notifier = await getNotifier()
if (!notifier) {
logger.warn("No notifier configured, skipping message creation for unbudgeted transaction %s", id)
return
}
const messageId = await notifier.getMessageId("BudgetMessageId", id)
if (messageId) {
const messageExists = await notifier.hasMessageId(messageId)
if (messageExists) {
logger.info("Budget message already exists for transaction %s", id)
return
}
logger.info("Budget message defined but not found in notifier for transaction %s", id)
}
const newMessageId = await notifier.sendMessage(title, content)
await bindTransactionToNotification(id, "BudgetMessageId", newMessageId)
}
override async init(): Promise<void> {
logger.info("Initializing UnbudgetedTransactions jobs for all unbudgeted transactions")
const notifier = await getNotifier()
if (notifier) {
const { data } = await BudgetsService.listTransactionWithoutBudget({ client, query: { page: 1, limit: 50 } })
for (const { id: transactionId } of data) {
await addTransactionJobToQueue(this, transactionId)
}
}
logger.info("Initialized UnbudgetedTransactions jobs for %d transactions", 0)
}
}
|