Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

include agreement number in auto holds #232

Merged
merged 12 commits into from
Feb 4, 2025
5 changes: 3 additions & 2 deletions app/auto-hold/add-hold.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ const db = require('../data')
const { ADDED } = require('../constants/hold-statuses')
const { sendHoldEvent } = require('../event')

const addHold = async (frn, autoHoldCategoryId, marketingYear, transaction) => {
const hold = await db.autoHold.create({ frn, autoHoldCategoryId, marketingYear, added: Date.now() }, { transaction })
const addHold = async (deltaPaymentRequest, autoHoldCategoryId, transaction) => {
const { frn, marketingYear, agreementNumber } = deltaPaymentRequest
const hold = await db.autoHold.create({ frn, autoHoldCategoryId, marketingYear, agreementNumber, added: Date.now() }, { transaction })
await sendHoldEvent(hold.get({ plain: true }), ADDED)
}

Expand Down
8 changes: 5 additions & 3 deletions app/auto-hold/apply-auto-hold.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@ const applyAutoHold = async (paymentRequests) => {
return false
}

if (paymentRequests[0].paymentRequestNumber === 1) {
const paymentRequest = paymentRequests[0]

if (paymentRequest.paymentRequestNumber === 1) {
return false
}

const totalValue = getTotalValue(paymentRequests)

if (autoHold.topUp && totalValue >= 0) {
await applyHold(paymentRequests[0].schemeId, paymentRequests[0].paymentRequestId, paymentRequests[0].frn, paymentRequests[0].marketingYear, TOP_UP)
await applyHold(paymentRequest, TOP_UP)
return true
}

if (autoHold.recovery && totalValue < 0) {
await applyHold(paymentRequests[0].schemeId, paymentRequests[0].paymentRequestId, paymentRequests[0].frn, paymentRequests[0].marketingYear, RECOVERY)
await applyHold(paymentRequest, RECOVERY)
return true
}

Expand Down
7 changes: 4 additions & 3 deletions app/auto-hold/apply-hold.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ const db = require('../data')
const { getHoldCategoryId } = require('./get-hold-category-id')
const { holdAndReschedule } = require('./hold-and-reschedule')

const applyHold = async (schemeId, paymentRequestId, frn, marketingYear, category) => {
const applyHold = async (paymentRequest, category) => {
const transaction = await db.sequelize.transaction()
const { schemeId, frn, marketingYear, agreementNumber } = paymentRequest
try {
console.log(`${category} automatically held for marketing year ${marketingYear}: ${frn}`)
console.log(`${category} automatically held for marketing year ${marketingYear}, agreement number ${agreementNumber}: ${frn}`)
const holdCategoryId = await getHoldCategoryId(schemeId, category, transaction)
await holdAndReschedule(paymentRequestId, holdCategoryId, frn, marketingYear, transaction)
await holdAndReschedule(paymentRequest, holdCategoryId, transaction)
await transaction.commit()
} catch (error) {
console.error('An error occurred whilst applying hold:', error)
Expand Down
5 changes: 3 additions & 2 deletions app/auto-hold/get-existing-hold.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const db = require('../data')

const getExistingHold = async (autoHoldCategoryId, frn, marketingYear, transaction) => {
const getExistingHold = async (autoHoldCategoryId, paymentRequest, transaction) => {
const { frn, marketingYear, agreementNumber } = paymentRequest
return db.autoHold.findOne({
transaction,
where: { autoHoldCategoryId, frn, marketingYear, closed: null }
where: { autoHoldCategoryId, frn, marketingYear, agreementNumber, closed: null }
})
}

Expand Down
8 changes: 4 additions & 4 deletions app/auto-hold/hold-and-reschedule.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ const { getExistingHold } = require('./get-existing-hold')
const { addHold } = require('./add-hold')
const { ensureScheduled } = require('../reschedule/ensure-scheduled')

const holdAndReschedule = async (paymentRequestId, holdCategoryId, frn, marketingYear, transaction) => {
const existingHold = await getExistingHold(holdCategoryId, frn, marketingYear, transaction)
const holdAndReschedule = async (paymentRequest, holdCategoryId, transaction) => {
const existingHold = await getExistingHold(holdCategoryId, paymentRequest, transaction)
if (!existingHold) {
await addHold(frn, holdCategoryId, marketingYear, transaction)
await addHold(paymentRequest, holdCategoryId, transaction)
}
await ensureScheduled(paymentRequestId, transaction)
await ensureScheduled(paymentRequest.paymentRequestId, transaction)
}

module.exports = {
Expand Down
4 changes: 2 additions & 2 deletions app/auto-hold/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const { applyAutoHold } = require('./apply-auto-hold')
const { getHoldCategoryId } = require('./get-hold-category-id')
const { holdAndReschedule } = require('./hold-and-reschedule')
const { removeHoldByFrn } = require('./remove-hold-by-frn')
const { removeAutoHold } = require('./remove-auto-hold')

module.exports = {
applyAutoHold,
getHoldCategoryId,
holdAndReschedule,
removeHoldByFrn
removeAutoHold
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ const { getHoldCategoryId } = require('./get-hold-category-id')
const { sendHoldEvent } = require('../event')
const { REMOVED } = require('../constants/hold-statuses')

const removeHoldByFrn = async (schemeId, frn, holdCategoryName) => {
const removeAutoHold = async (paymentRequest, holdCategoryName) => {
const { schemeId, frn, marketingYear, agreementNumber } = paymentRequest
const autoHoldCategoryId = await getHoldCategoryId(schemeId, holdCategoryName)
const hold = await db.autoHold.findOne({ where: { frn, autoHoldCategoryId, closed: null }, raw: true })
const hold = await db.autoHold.findOne({ where: { frn, marketingYear, agreementNumber, autoHoldCategoryId, closed: null }, raw: true })
if (hold) {
const holdClosed = new Date()
await db.autoHold.update({ closed: holdClosed }, { where: { frn, autoHoldCategoryId, closed: null } })
Expand All @@ -14,5 +15,5 @@ const removeHoldByFrn = async (schemeId, frn, holdCategoryName) => {
}

module.exports = {
removeHoldByFrn
removeAutoHold
}
71 changes: 71 additions & 0 deletions app/constants/get-scheduled-payment-requests-query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module.exports = `
WITH "plannedSchedules" AS (
SELECT DISTINCT ON ("paymentRequests"."frn", "paymentRequests"."schemeId", "paymentRequests"."marketingYear", "paymentRequests"."agreementNumber")
"schedule"."scheduleId",
"paymentRequests"."frn",
"paymentRequests"."schemeId",
"paymentRequests"."marketingYear",
"paymentRequests"."agreementNumber",
"paymentRequests"."paymentRequestNumber"
FROM
"schedule"
INNER JOIN
"paymentRequests" ON "schedule"."paymentRequestId" = "paymentRequests"."paymentRequestId"
INNER JOIN
"schemes" ON "paymentRequests"."schemeId" = "schemes"."schemeId"
INNER JOIN
"invoiceLines" ON "paymentRequests"."paymentRequestId" = "invoiceLines"."paymentRequestId"
WHERE
"schemes"."active" = true
AND "schedule"."planned" <= NOW()
AND "schedule"."completed" IS NULL
AND ("schedule"."started" IS NULL OR "schedule"."started" <= NOW() - INTERVAL '5 minutes')
AND NOT EXISTS (
SELECT 1
FROM
"holds"
INNER JOIN
"holdCategories" ON "holds"."holdCategoryId" = "holdCategories"."holdCategoryId"
WHERE "holds"."closed" IS NULL
AND "paymentRequests"."frn" = "holds"."frn"
AND "schemes"."schemeId" = "holdCategories"."schemeId"
)
AND NOT EXISTS (
SELECT 1
FROM "autoHolds"
INNER JOIN "autoHoldCategories"
ON "autoHolds"."autoHoldCategoryId" = "autoHoldCategories"."autoHoldCategoryId"
WHERE "autoHolds"."closed" IS NULL
AND "paymentRequests"."frn" = "autoHolds"."frn"
AND "schemes"."schemeId" = "autoHoldCategories"."schemeId"
AND "paymentRequests"."marketingYear" = "autoHolds"."marketingYear"
AND "paymentRequests"."agreementNumber" = "autoHolds"."agreementNumber"
)
AND NOT EXISTS (
SELECT 1
FROM "schedule" "s2"
INNER JOIN "paymentRequests" "p2"
ON "s2"."paymentRequestId" = "p2"."paymentRequestId"
WHERE
"paymentRequests"."frn" = "p2"."frn"
AND "paymentRequests"."schemeId" = "p2"."schemeId"
AND "paymentRequests"."marketingYear" = "p2"."marketingYear"
AND "paymentRequests"."agreementNumber" = "p2"."agreementNumber"
AND "s2"."started" > NOW() - INTERVAL '5 minutes'
AND "s2"."completed" IS NULL
)
ORDER BY
"paymentRequests"."frn",
"paymentRequests"."schemeId",
"paymentRequests"."marketingYear",
"paymentRequests"."agreementNumber",
"paymentRequests"."paymentRequestNumber",
"schedule"."planned"
LIMIT :processingCap
)
UPDATE "schedule"
SET "started" = NOW()
FROM "plannedSchedules"
WHERE "schedule"."scheduleId" = "plannedSchedules"."scheduleId"
RETURNING "schedule"."scheduleId"
`
1 change: 1 addition & 0 deletions app/data/models/auto-hold.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = (sequelize, DataTypes) => {
autoHoldCategoryId: DataTypes.INTEGER,
frn: DataTypes.BIGINT,
marketingYear: DataTypes.INTEGER,
agreementNumber: DataTypes.STRING,
added: DataTypes.DATE,
closed: DataTypes.DATE
},
Expand Down
2 changes: 1 addition & 1 deletion app/holds/get-holds.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const getHolds = async (pageProperties, open = true) => {
}]
}],
raw: true,
attributes: [['autoHoldId', 'holdId'], 'frn', [db.Sequelize.col('autoHoldCategory.name'), 'holdCategoryName'], [db.Sequelize.col('autoHoldCategory.scheme.schemeId'), 'holdCategorySchemeId'], [db.Sequelize.col('autoHoldCategory.scheme.name'), 'holdCategorySchemeName'], 'marketingYear', [db.Sequelize.col('added'), 'dateTimeAdded'], [db.Sequelize.col('closed'), 'dateTimeClosed']]
attributes: [['autoHoldId', 'holdId'], 'frn', [db.Sequelize.col('autoHoldCategory.name'), 'holdCategoryName'], [db.Sequelize.col('autoHoldCategory.scheme.schemeId'), 'holdCategorySchemeId'], [db.Sequelize.col('autoHoldCategory.scheme.name'), 'holdCategorySchemeName'], 'marketingYear', 'agreementNumber', [db.Sequelize.col('added'), 'dateTimeAdded'], [db.Sequelize.col('closed'), 'dateTimeClosed']]
})

const mergedResults = [...holds, ...autoHolds]
Expand Down
85 changes: 12 additions & 73 deletions app/processing/scheduled/get-scheduled-payment-requests.js
Original file line number Diff line number Diff line change
@@ -1,86 +1,15 @@
const { Transaction } = require('sequelize')
const db = require('../../data')
const { processingConfig } = require('../../config')
const getScheduledPaymentRequestsQuery = require('../../constants/get-scheduled-payment-requests-query')

const getScheduledPaymentRequests = async () => {
// This is written as a raw query for performance reasons
const transaction = await db.sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
})
try {
const schedules = await db.sequelize.query(`
WITH "plannedSchedules" AS (
SELECT DISTINCT ON ("paymentRequests"."frn", "paymentRequests"."schemeId", "paymentRequests"."marketingYear")
"schedule"."scheduleId",
"paymentRequests"."frn",
"paymentRequests"."schemeId",
"paymentRequests"."marketingYear",
"paymentRequests"."paymentRequestNumber"
FROM
"schedule"
INNER JOIN
"paymentRequests" ON "schedule"."paymentRequestId" = "paymentRequests"."paymentRequestId"
INNER JOIN
"schemes" ON "paymentRequests"."schemeId" = "schemes"."schemeId"
INNER JOIN
"invoiceLines" ON "paymentRequests"."paymentRequestId" = "invoiceLines"."paymentRequestId"
WHERE
"schemes"."active" = true
AND "schedule"."planned" <= NOW()
AND "schedule"."completed" IS NULL
AND ("schedule"."started" IS NULL OR "schedule"."started" <= NOW() - INTERVAL '5 minutes')
AND NOT EXISTS (
SELECT 1
FROM
"holds"
INNER JOIN
"holdCategories" ON "holds"."holdCategoryId" = "holdCategories"."holdCategoryId"
WHERE "holds"."closed" IS NULL
AND "paymentRequests"."frn" = "holds"."frn"
AND "schemes"."schemeId" = "holdCategories"."schemeId"
)
AND NOT EXISTS (
SELECT 1
FROM "autoHolds"
INNER JOIN "autoHoldCategories"
ON "autoHolds"."autoHoldCategoryId" = "autoHoldCategories"."autoHoldCategoryId"
WHERE "autoHolds"."closed" IS NULL
AND "paymentRequests"."frn" = "autoHolds"."frn"
AND "schemes"."schemeId" = "autoHoldCategories"."schemeId"
AND "paymentRequests"."marketingYear" = "autoHolds"."marketingYear"
)
AND NOT EXISTS (
SELECT 1
FROM "schedule" "s2"
INNER JOIN "paymentRequests" "p2"
ON "s2"."paymentRequestId" = "p2"."paymentRequestId"
WHERE
"paymentRequests"."frn" = "p2"."frn"
AND "paymentRequests"."schemeId" = "p2"."schemeId"
AND "paymentRequests"."marketingYear" = "p2"."marketingYear"
AND "s2"."started" > NOW() - INTERVAL '5 minutes'
AND "s2"."completed" IS NULL
)
ORDER BY
"paymentRequests"."frn",
"paymentRequests"."schemeId",
"paymentRequests"."marketingYear",
"paymentRequests"."paymentRequestNumber",
"schedule"."planned"
LIMIT :processingCap
)
UPDATE "schedule"
SET "started" = NOW()
FROM "plannedSchedules"
WHERE "schedule"."scheduleId" = "plannedSchedules"."scheduleId"
RETURNING "schedule"."scheduleId"
`, {
replacements: {
processingCap: processingConfig.processingCap
},
raw: true,
transaction
})
const schedules = await determineSchedules(transaction)

await transaction.commit()

Expand Down Expand Up @@ -113,6 +42,16 @@ const getScheduledPaymentRequests = async () => {
}
}

const determineSchedules = async (transaction) => {
return db.sequelize.query(getScheduledPaymentRequestsQuery, {
replacements: {
processingCap: processingConfig.processingCap
},
raw: true,
transaction
})
}

module.exports = {
getScheduledPaymentRequests
}
4 changes: 2 additions & 2 deletions app/routing/prepare-for-reprocessing.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const db = require('../data')
const { AWAITING_DEBT_ENRICHMENT } = require('../constants/hold-categories-names')
const { removeHoldByFrn } = require('../auto-hold')
const { removeAutoHold } = require('../auto-hold')

const prepareForReprocessing = async (paymentRequest, debtType, recoveryDate) => {
await db.paymentRequest.update({
Expand All @@ -9,7 +9,7 @@ const prepareForReprocessing = async (paymentRequest, debtType, recoveryDate) =>
}, {
where: { paymentRequestId: paymentRequest.paymentRequestId }
})
await removeHoldByFrn(paymentRequest.schemeId, paymentRequest.frn, AWAITING_DEBT_ENRICHMENT)
await removeAutoHold(paymentRequest, AWAITING_DEBT_ENRICHMENT)
}

module.exports = {
Expand Down
2 changes: 1 addition & 1 deletion app/routing/route-debt-to-request-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const routeDebtToRequestEditor = async (paymentRequest) => {
await sendMessage(paymentRequest, ROUTED_DEBT, messageConfig.debtTopic)
console.log('Payment request routed to request editor:', util.inspect(paymentRequest, false, null, true))
const holdCategoryId = await getHoldCategoryId(paymentRequest.schemeId, AWAITING_DEBT_ENRICHMENT, transaction)
await holdAndReschedule(paymentRequest.paymentRequestId, holdCategoryId, paymentRequest.frn, paymentRequest.marketingYear, transaction)
await holdAndReschedule(paymentRequest, holdCategoryId, transaction)
await transaction.commit()
} catch (error) {
await transaction.rollback()
Expand Down
2 changes: 1 addition & 1 deletion app/routing/route-manual-ledger-to-request-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const routeManualLedgerToRequestEditor = async (deltaCalculationResult) => {
await sendMessage(manualLedgerMessage, ROUTED_LEDGER, messageConfig.manualTopic)
console.log('Payment request routed to request editor for manual ledger check:', util.inspect(manualLedgerMessage, false, null, true))
const holdCategoryId = await getHoldCategoryId(deltaPaymentRequest.schemeId, AWAITING_LEDGER_CHECK, transaction)
await holdAndReschedule(deltaPaymentRequest.paymentRequestId, holdCategoryId, deltaPaymentRequest.frn, deltaPaymentRequest.marketingYear, transaction)
await holdAndReschedule(deltaPaymentRequest, holdCategoryId, transaction)
await transaction.commit()
} catch (error) {
await transaction.rollback()
Expand Down
2 changes: 1 addition & 1 deletion app/routing/route-to-cross-border.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const routeToCrossBorder = async (paymentRequest) => {
await sendMessage(paymentRequest, CROSS_BORDER, messageConfig.xbTopic)
console.log('Payment request routed to Cross Border:', util.inspect(paymentRequest, false, null, true))
const holdCategoryId = await getHoldCategoryId(paymentRequest.schemeId, CROSS_BORDER_HOLD, transaction)
await holdAndReschedule(paymentRequest.paymentRequestId, holdCategoryId, paymentRequest.frn, paymentRequest.marketingYear, transaction)
await holdAndReschedule(paymentRequest, holdCategoryId, transaction)
await transaction.commit()
} catch (error) {
await transaction.rollback()
Expand Down
4 changes: 2 additions & 2 deletions app/routing/update-requests-awaiting-cross-border.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const db = require('../data')
const { removeHoldByFrn } = require('../auto-hold')
const { removeAutoHold } = require('../auto-hold')
const { CROSS_BORDER } = require('../constants/hold-categories-names')
const { saveInvoiceLines } = require('../inbound/save-invoice-lines')
const { invalidateInvoiceLines } = require('./invalidate-invoice-lines')
Expand All @@ -23,7 +23,7 @@ const updateRequestsAwaitingCrossBorder = async (paymentRequest) => {

await invalidateInvoiceLines(originalPaymentRequest.paymentRequestId, transaction)
await saveInvoiceLines(paymentRequest.invoiceLines, originalPaymentRequest.paymentRequestId, transaction)
await removeHoldByFrn(paymentRequest.schemeId, paymentRequest.frn, CROSS_BORDER)
await removeAutoHold(paymentRequest, CROSS_BORDER)
await transaction.commit()
} catch (err) {
await transaction.rollback()
Expand Down
4 changes: 2 additions & 2 deletions app/routing/update-requests-awaiting-manual-ledger-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { getScheduleId } = require('./get-schedule-id')
const { transformPaymentRequest } = require('./transform-payment-request')
const { mapAccountCodes } = require('../processing/account-codes')
const { completePaymentRequests } = require('../processing/complete-payment-requests')
const { removeHoldByFrn } = require('../auto-hold')
const { removeAutoHold } = require('../auto-hold')
const { sendProcessingRouteEvent } = require('../event')
const { AWAITING_LEDGER_CHECK } = require('../constants/hold-categories-names')

Expand Down Expand Up @@ -34,7 +34,7 @@ const updateRequestsAwaitingManualLedgerCheck = async (manualLedgerCheckResult)
})

await completePaymentRequests(scheduleId, updatedPaymentRequests)
await removeHoldByFrn(checkPaymentRequest.schemeId, checkPaymentRequest.frn, AWAITING_LEDGER_CHECK)
await removeAutoHold(checkPaymentRequest, AWAITING_LEDGER_CHECK)

for (const paymentRequestItem of updatedPaymentRequests) {
await sendProcessingRouteEvent(paymentRequestItem, 'manual-ledger', 'response')
Expand Down
Loading