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

Generate DAX batch #7

Merged
merged 87 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
e01680b
Hold and scheme added
johnwatson484 Jul 29, 2021
27ab39d
Patch vulns
johnwatson484 Jul 29, 2021
4c6377b
Payment request added
johnwatson484 Jul 29, 2021
a58d4fc
Schedule added
johnwatson484 Jul 29, 2021
0c57c3e
Completed payment request added
johnwatson484 Jul 29, 2021
5437de1
Mapping tables added
johnwatson484 Jul 30, 2021
53927ff
Check for due payments
johnwatson484 Jul 30, 2021
1037a27
Fixed model errors
johnwatson484 Jul 30, 2021
c5d4a9f
Merged
johnwatson484 Jul 30, 2021
7bbe4f0
remove unused call
johnwatson484 Jul 30, 2021
962f29f
Version changed
johnwatson484 Jul 30, 2021
8dbf8dd
Check for pending payment requests
johnwatson484 Jul 30, 2021
3729b6c
Add marketing year
johnwatson484 Jul 30, 2021
48819cb
version updated
johnwatson484 Jul 30, 2021
fbb5afe
Add previous payment check
johnwatson484 Jul 30, 2021
d2c7cbc
Merged
johnwatson484 Jul 30, 2021
733f9e2
Add user journey
johnwatson484 Jul 30, 2021
91a003b
Fixed name
johnwatson484 Jul 30, 2021
af61e9d
Refactored
johnwatson484 Jul 30, 2021
3e58a26
Refactored
johnwatson484 Jul 30, 2021
3651bdc
Inteval moved to config
johnwatson484 Jul 30, 2021
81b1b73
Batch size updated
johnwatson484 Jul 30, 2021
9978094
Refactor
johnwatson484 Jul 30, 2021
d2920b0
Fix hold model
johnwatson484 Jul 30, 2021
fe75524
Holds removed from batch
johnwatson484 Jul 30, 2021
06d0c2e
Remove duplicates
johnwatson484 Jul 30, 2021
bacf809
Simplify
johnwatson484 Jul 30, 2021
5631ebe
Comment updated
johnwatson484 Jul 30, 2021
a1bc349
Save payment requests
johnwatson484 Jul 30, 2021
efe3225
Fix typo
johnwatson484 Jul 30, 2021
11aba71
Fix get
johnwatson484 Jul 30, 2021
3dcaa23
Handle pending
johnwatson484 Jul 30, 2021
477830e
Account code mapped
johnwatson484 Jul 30, 2021
fd798f7
Test updated
johnwatson484 Jul 30, 2021
3c47296
Tests added
johnwatson484 Jul 30, 2021
038e871
Add tests
johnwatson484 Jul 31, 2021
a638b03
Test for duplicates
johnwatson484 Jul 31, 2021
1c8fbba
Add cap test
johnwatson484 Jul 31, 2021
c790585
Test published
johnwatson484 Jul 31, 2021
c1d0c1f
Update tests
johnwatson484 Jul 31, 2021
22b153d
Prevent data loss
johnwatson484 Jul 31, 2021
2872f9f
Previous test added
johnwatson484 Jul 31, 2021
d52d8a6
Completed tests added
johnwatson484 Jul 31, 2021
e8f93cb
Add completed tests
johnwatson484 Jul 31, 2021
37552a1
Tests added
johnwatson484 Jul 31, 2021
10c2740
Remove comment
johnwatson484 Jul 31, 2021
780d671
Generate DAX file
johnwatson484 Jul 31, 2021
cbd44cf
Generate DAX file
johnwatson484 Jul 31, 2021
f001ac0
Batch allocation added
johnwatson484 Aug 1, 2021
c9a81c0
Increase generation
johnwatson484 Aug 1, 2021
c8b786e
Fix loop
johnwatson484 Aug 1, 2021
cefa068
Flow updated
johnwatson484 Aug 1, 2021
d2fe97e
Filename added
johnwatson484 Aug 1, 2021
7434279
Batch writing added
johnwatson484 Aug 1, 2021
2c33f3b
Test structure added
johnwatson484 Aug 1, 2021
039390f
Filename tests added
johnwatson484 Aug 1, 2021
80db1ca
Rename migration
johnwatson484 Aug 2, 2021
ebaf3d8
Merged
johnwatson484 Aug 2, 2021
5b3899a
Rename test
johnwatson484 Aug 2, 2021
c2a8354
Allocation tests added
johnwatson484 Aug 2, 2021
a910161
git ignore updated
johnwatson484 Aug 2, 2021
ebadb5a
Complete batch tests added
johnwatson484 Aug 2, 2021
a74dd84
Fix typo
johnwatson484 Aug 2, 2021
19b2ccf
Get batch tests added
johnwatson484 Aug 2, 2021
0e6e08b
Tests updated
johnwatson484 Aug 2, 2021
eb4d354
Content test added
johnwatson484 Aug 2, 2021
bf3c886
Removed CSV
johnwatson484 Aug 2, 2021
db7bc2a
Bump version
johnwatson484 Aug 2, 2021
1582a31
Add paymentRequestNumber
johnwatson484 Aug 3, 2021
57d3428
Merged
johnwatson484 Aug 3, 2021
0f27cfe
Version bump
johnwatson484 Aug 3, 2021
f2d65df
Version bump
johnwatson484 Aug 3, 2021
19b0ca8
Fix test date
johnwatson484 Aug 3, 2021
f6d2c60
Change order
johnwatson484 Aug 3, 2021
ada7045
test
johnwatson484 Aug 3, 2021
54c7552
Fix lint
johnwatson484 Aug 3, 2021
74287e0
Update test
johnwatson484 Aug 3, 2021
313a9b6
Dup detection updated
johnwatson484 Aug 3, 2021
a2e0f01
Publish log
johnwatson484 Aug 3, 2021
1017e46
Schema updated
johnwatson484 Aug 3, 2021
02b02bd
Fix data items
johnwatson484 Aug 3, 2021
ecf10ac
Fix data type
johnwatson484 Aug 3, 2021
eecefe6
Fix batch date
johnwatson484 Aug 3, 2021
ef93365
Convert to number
johnwatson484 Aug 3, 2021
d50eb42
Fix tests
johnwatson484 Aug 3, 2021
1d77d03
Fix name
johnwatson484 Aug 3, 2021
373f047
Fix test
johnwatson484 Aug 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ helm/**/*.lock
*.tgz
.vscode
.env
*.csv
98 changes: 98 additions & 0 deletions app/batching/allocate-to-batches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
const db = require('../data')
const config = require('../config')

const allocateToBatches = async (created = new Date()) => {
const transaction = await db.sequelize.transaction()
try {
const apPaymentRequests = await getPendingPaymentRequests('AP', transaction)
const arPaymentRequests = await getPendingPaymentRequests('AR', transaction)
if (apPaymentRequests.length) {
await allocateToBatch(apPaymentRequests, 'AP', created, transaction)
}
if (arPaymentRequests.length) {
await allocateToBatch(arPaymentRequests, 'AR', created, transaction)
}
await transaction.commit()
} catch (error) {
await transaction.rollback()
throw (error)
}
}

const getPendingPaymentRequests = async (ledger, transaction) => {
return db.scheme.findAll({
transaction,
include: [{
model: db.completedPaymentRequest,
as: 'completedPaymentRequests',
required: true,
include: [{
model: db.completedInvoiceLine,
as: 'invoiceLines',
required: true
}],
where: {
ledger,
batchId: null
},
order: ['completedPaymentRequestId'],
limit: config.batchSize
}]
})
}

const allocateToBatch = async (schemes, ledger, created, transaction) => {
for (const scheme of schemes) {
if (scheme.completedPaymentRequests.length) {
const sequence = await getAndIncrementSequence(scheme.schemeId, ledger, transaction)
const batch = await createNewBatch(scheme.schemeId, ledger, sequence, created, transaction)
await updatePaymentRequests(scheme, batch.batchId, transaction)
}
}
}

const getAndIncrementSequence = async (schemeId, ledger, transaction) => {
const sequence = await getSequence(schemeId, transaction)
let nextSequence
if (ledger === 'AP') {
nextSequence = sequence.nextAP
sequence.nextAP = sequence.nextAP + 1
await updateSequence(sequence, transaction)
return nextSequence
}
nextSequence = sequence.nextAR
sequence.nextAR = sequence.nextAR + 1
await updateSequence(sequence, transaction)
return nextSequence
}

const getSequence = async (schemeId, transaction) => {
return db.sequence.findByPk(schemeId, { transaction })
}

const updateSequence = async (sequence, transaction) => {
await db.sequence.update({
nextAP: sequence.nextAP,
nextAR: sequence.nextAR
}, {
where: { schemeId: sequence.schemeId },
transaction
})
}

const createNewBatch = async (schemeId, ledger, sequence, created, transaction) => {
return db.batch.create({ schemeId, ledger, sequence, created }, { transaction })
}

const updatePaymentRequests = async (scheme, batchId, transaction) => {
for (const paymentRequest of scheme.completedPaymentRequests) {
await db.completedPaymentRequest.update({ batchId }, {
where: {
completedPaymentRequestId: paymentRequest.completedPaymentRequestId
},
transaction
})
}
}

module.exports = allocateToBatches
18 changes: 18 additions & 0 deletions app/batching/complete-batch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const db = require('../data')

const completeBatch = async (batchId) => {
const transaction = await db.sequelize.transaction()
try {
const batch = await db.batch.findByPk(batchId, { transaction })
// Check if completed already in case of duplicate processing
if (batch.published === null) {
await db.batch.update({ published: new Date() }, { where: { batchId }, transaction })
}
await transaction.commit()
} catch (error) {
await transaction.rollback()
throw (error)
}
}

module.exports = completeBatch
19 changes: 19 additions & 0 deletions app/batching/generate-batches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const allocateToBatches = require('./allocate-to-batches')
const getBatches = require('./get-batches')
const getFileName = require('./get-filename')
const getContent = require('./get-content')
const publishBatch = require('./publish-batch')
const completeBatch = require('./complete-batch')

const generateBatches = async () => {
await allocateToBatches()
const batches = await getBatches()
for (const batch of batches) {
const filename = getFileName(batch)
const content = getContent(batch)
await publishBatch(batch, filename, content)
await completeBatch(batch.batchId)
}
}

module.exports = generateBatches
64 changes: 64 additions & 0 deletions app/batching/get-batches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const db = require('../data')
const moment = require('moment')
const config = require('../config')

const getBatches = async (started = new Date()) => {
const transaction = await db.sequelize.transaction()
try {
const batches = await getPendingBatches(started, transaction)
await updateStarted(batches, started, transaction)
await transaction.commit()
return batches
} catch (error) {
await transaction.rollback()
throw (error)
}
}

const getPendingBatches = async (started, transaction) => {
return db.batch.findAll({
transaction,
limit: config.batchCap,
order: ['sequence'],
include: [{
model: db.scheme,
as: 'scheme',
required: true,
include: [{
model: db.batchProperties,
as: 'batchProperties',
required: true
}]
}, {
model: db.completedPaymentRequest,
as: 'paymentRequests',
required: true,
include: [{
model: db.completedInvoiceLine,
as: 'invoiceLines',
required: true
}]
}],
where: {
published: null,
[db.Sequelize.Op.or]: [{
started: null
}, {
started: { [db.Sequelize.Op.lte]: moment(started).subtract(5, 'minutes').toDate() }
}]
}
})
}

const updateStarted = async (batches, started, transaction) => {
for (const batch of batches) {
await db.batch.update({ started }, {
where: {
batchId: batch.batchId
},
transaction
})
}
}

module.exports = getBatches
105 changes: 105 additions & 0 deletions app/batching/get-content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const moment = require('moment')
const { convertToPounds } = require('../currency-convert')

const getContent = (batch) => {
const rows = []
for (const paymentRequest of batch.paymentRequests) {
const vendorGroups = getVendorGroups(paymentRequest.invoiceLines)
for (const vendorGroup of vendorGroups) {
const vendor = getVendorLine(paymentRequest, vendorGroup, batch)
rows.push(vendor)
for (const invoiceLine of vendorGroup.invoiceLines) {
const ledger = getLedgerLine(invoiceLine, paymentRequest)
rows.push(ledger)
}
}
}

return rows
}

const getVendorGroups = (invoiceLines) => {
return [...invoiceLines.reduce((x, y) => {
// group by scheme and fund, so create key representing the combination
const key = `${y.schemeCode}-${y.fundCode}`

// if key doesn't exist then first instance so create new group
const item = x.get(key) || Object.assign({}, { fundCode: y.fundCode, schemeCode: y.schemeCode, value: 0, invoiceLines: [] })
item.value += Number(y.value)
item.invoiceLines.push(y)

return x.set(key, item)
}, new Map()).values()]
}

const getVendorLine = (paymentRequest, vendorGroup, batch) => {
return [
'Vendor',
paymentRequest.frn,
'',
vendorGroup.fundCode,
vendorGroup.schemeCode,
paymentRequest.marketingYear,
paymentRequest.deliveryBody,
paymentRequest.invoiceNumber,
convertToPounds(vendorGroup.value),
paymentRequest.currency,
'legacy',
'',
paymentRequest.contractNumber,
'',
1,
'',
'',
'',
'',
`BACS_${paymentRequest.currency}`,
batch.scheme.batchProperties.source,
'',
'',
'',
moment(paymentRequest.dueDate).format('DD[/]MM[/]YYYY'),
paymentRequest.currency,
'',
'',
paymentRequest.schedule,
'END'
]
}

const getLedgerLine = (invoiceLine, paymentRequest) => {
return [
'Ledger',
invoiceLine.accountCode,
'',
invoiceLine.fundCode,
invoiceLine.schemeCode,
paymentRequest.marketingYear,
paymentRequest.deliveryBody,
paymentRequest.invoiceNumber,
convertToPounds(invoiceLine.value),
paymentRequest.currency,
'legacy',
'',
'',
'',
'',
'',
'',
invoiceLine.description,
'',
'',
'',
'',
'',
'',
'',
'',
'',
paymentRequest.agreementNumber,
'',
'END'
]
}

module.exports = getContent
7 changes: 7 additions & 0 deletions app/batching/get-filename.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const moment = require('moment')

const getFileName = (batch) => {
return `${batch.scheme.batchProperties.prefix}${batch.sequence.toString().padStart(4, '0')}_${batch.ledger}_${moment().format('YYYYMMDDHHmmss')}${batch.scheme.batchProperties.suffix}.csv`
}

module.exports = getFileName
16 changes: 16 additions & 0 deletions app/batching/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const generateBatches = require('./generate-batches')
const config = require('../config')

const start = async () => {
try {
await generateBatches()
} catch (err) {
console.error(err)
} finally {
setTimeout(start, config.batchGenerationInterval)
}
}

module.exports = {
start
}
7 changes: 7 additions & 0 deletions app/batching/publish-batch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const publishBatch = async (batch, filename, content) => {
console.info(`Publishing ${batch.scheme.name} ${batch.ledger} batch ${batch.sequence}`)
console.log(`Filename: ${filename}`)
console.log(`content: ${JSON.stringify(content)}`)
}

module.exports = publishBatch
10 changes: 8 additions & 2 deletions app/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ const { development, production, test } = require('./constants').environments
const schema = Joi.object({
env: Joi.string().valid(development, test, production).default(development),
paymentProcessingInterval: Joi.number().default(1000),
processingBatchSize: Joi.number().default(1000)
processingCap: Joi.number().default(1000),
batchGenerationInterval: Joi.number().default(5000),
batchSize: Joi.number().default(10000),
batchCap: Joi.number().default(100)
})

// Build config
const config = {
env: process.env.NODE_ENV,
paymentProcessingInterval: process.env.PROCESSING_INTERVAL,
processingBatchSize: process.env.PROCESSING_BATCH_SIZE
processingCap: process.env.PROCESSING_CAP,
batchGenerationInterval: process.env.BATCH_INTERVAL,
batchSize: process.env.BATCH_SIZE,
batchCap: process.env.BATCH_CAP
}

// Validate config
Expand Down
15 changes: 15 additions & 0 deletions app/currency-convert/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const convertToPence = (valueInPounds) => {
const currencyArray = valueInPounds.toString().split('.')
const pounds = currencyArray[0]
const pence = (currencyArray[1] || '00').padEnd(2, '0')
return Number(pounds + pence)
}

const convertToPounds = (valueInPence) => {
return (valueInPence / 100).toFixed(2)
}

module.exports = {
convertToPence,
convertToPounds
}
Loading