344 lines
9.6 KiB
TypeScript
344 lines
9.6 KiB
TypeScript
import { type ReadableStream } from 'node:stream/web'
|
|
import knex from './knex.ts'
|
|
|
|
import split, { type Decoder } from './split.ts'
|
|
import {
|
|
parseDim,
|
|
parseIB,
|
|
parseKonto,
|
|
parseObjekt,
|
|
parseRAR,
|
|
parseSRU,
|
|
parseTrans,
|
|
parseUB,
|
|
parseVer,
|
|
} from './parse_line.ts'
|
|
|
|
const defaultDecoder = {
|
|
decode(chunk: Uint8Array) {
|
|
return Array.from(chunk, (uint) => {
|
|
switch (uint) {
|
|
case 132:
|
|
return 'ä'
|
|
case 134:
|
|
return 'å'
|
|
case 148:
|
|
return 'ö'
|
|
case 142:
|
|
return 'Ä'
|
|
case 143:
|
|
return 'Å'
|
|
case 153:
|
|
return 'Ö'
|
|
default:
|
|
return String.fromCharCode(uint)
|
|
}
|
|
}).join('')
|
|
},
|
|
}
|
|
|
|
export default async function parseStream(stream: ReadableStream, decoder: Decoder = defaultDecoder) {
|
|
const journals = new Map()
|
|
|
|
let currentEntry: { id: number; description: string }
|
|
let currentInvoiceId: number
|
|
let currentYear = null
|
|
const details: Record<string, string> = {}
|
|
|
|
const trx = await knex.transaction()
|
|
|
|
async function getJournalId(identifier: string) {
|
|
if (journals.has(identifier)) {
|
|
return journals.get(identifier)
|
|
}
|
|
|
|
let journal = await trx('journal').first('*').where('identifier', identifier)
|
|
|
|
if (!journal) {
|
|
journal = (await trx('journal').insert({ identifier }).returning('*'))[0]
|
|
}
|
|
|
|
journals.set(identifier, journal.id)
|
|
|
|
return journal.id
|
|
}
|
|
|
|
for await (let line of stream.pipeThrough(split(null, { decoder }))) {
|
|
line = line.trim()
|
|
|
|
if (line[0] !== '#') {
|
|
continue
|
|
}
|
|
|
|
const splitLine = line.split(/\s+/)
|
|
const lineType = splitLine[0]
|
|
|
|
switch (lineType) {
|
|
case '#DIM': {
|
|
const { number, name } = parseDim(line)
|
|
|
|
const existingDimension = await trx('dimension').first('*').where('number', number)
|
|
|
|
if (!existingDimension) {
|
|
await trx('dimension').insert({ number, name })
|
|
} else if (existingDimension.name !== name) {
|
|
await trx('dimension').update({ name }).where('number', number)
|
|
}
|
|
|
|
break
|
|
}
|
|
case '#IB': {
|
|
const { yearNumber, accountNumber, balance, quantity } = parseIB(line)
|
|
|
|
if (yearNumber !== 0) continue
|
|
|
|
const existingAccountBalance = await trx('accountBalance')
|
|
.first('*')
|
|
.where({ financialYearId: currentYear.id, accountNumber })
|
|
|
|
if (!existingAccountBalance) {
|
|
await trx('accountBalance').insert({
|
|
financialYearId: currentYear.id,
|
|
accountNumber,
|
|
in: balance,
|
|
inQuantity: quantity,
|
|
})
|
|
} else {
|
|
await trx('accountBalance')
|
|
.update({
|
|
in: balance,
|
|
inQuantity: quantity,
|
|
})
|
|
.where({
|
|
financialYearId: currentYear.id,
|
|
accountNumber,
|
|
})
|
|
}
|
|
|
|
break
|
|
}
|
|
case '#KONTO': {
|
|
const { number, description } = parseKonto(line)
|
|
|
|
const existingAccount = await trx('account')
|
|
.first('*')
|
|
.where('number', number)
|
|
.orderBy('financialYearId', 'desc')
|
|
|
|
if (!existingAccount) {
|
|
await trx('account').insert({
|
|
financialYearId: currentYear!.id,
|
|
number,
|
|
description,
|
|
})
|
|
} else if (existingAccount.description !== description) {
|
|
await trx('account').update({ description }).where('id', existingAccount.id)
|
|
}
|
|
|
|
break
|
|
}
|
|
case '#OBJEKT': {
|
|
const { dimensionNumber, number, name } = parseObjekt(line)
|
|
|
|
const dimension = await trx('dimension').first('*').where('number', dimensionNumber)
|
|
|
|
if (!dimension) throw new Error(`Dimension "${dimensionNumber}" does not exist`)
|
|
|
|
const existingObject = await trx('object').first('*').where({ dimensionId: dimension.id, number })
|
|
|
|
if (!existingObject) {
|
|
await trx('object').insert({ dimensionId: dimension.id, number, name })
|
|
} else if (existingObject.name !== name) {
|
|
await trx('object').update({ name }).where({ dimensionId: dimension.id, number })
|
|
}
|
|
|
|
break
|
|
}
|
|
case '#RAR': {
|
|
const { yearNumber, startDate, endDate } = parseRAR(line)
|
|
|
|
if (yearNumber !== 0) continue
|
|
|
|
currentYear = (
|
|
await trx('financial_year')
|
|
.insert({ year: startDate.slice(0, 4), startDate, endDate })
|
|
.returning('*')
|
|
)[0]
|
|
|
|
break
|
|
}
|
|
case '#SRU': {
|
|
const { number, sru } = parseSRU(line)
|
|
|
|
const existingAccount = await trx('account')
|
|
.first('*')
|
|
.where('number', number)
|
|
.orderBy('financialYearId', 'desc')
|
|
|
|
if (existingAccount) {
|
|
if (existingAccount.sru !== sru) {
|
|
await trx('account').update({ sru: sru }).where('id', existingAccount.id)
|
|
}
|
|
} else {
|
|
await trx('account').insert({
|
|
financialYearId: currentYear!.id,
|
|
number,
|
|
description: existingAccount.description,
|
|
sru,
|
|
})
|
|
}
|
|
|
|
break
|
|
}
|
|
case '#TRANS': {
|
|
const { objectList, ...transaction } = parseTrans(line)
|
|
// Faktura 6364710056 172-2 - SBAB Bank AB
|
|
// Faktura 2312 172-6 - Bredablick Frvaltning AB
|
|
const rFisken = /Faktura.*172-(\d*)\s+-\s+(.+)/
|
|
|
|
const result = transaction.description?.match(rFisken)
|
|
|
|
let invoiceId: number
|
|
|
|
if (result) {
|
|
const [, fiskenNumber, supplierName] = result
|
|
// invoiceId = (await trx('invoice').first('id').where({ fiskenNumber})).id
|
|
invoiceId = (await trx('invoice').first('id').where({ fiskenNumber }))?.id
|
|
|
|
if (!invoiceId) {
|
|
let supplierId = (await trx('supplier').first('id').where('name', supplierName))?.id
|
|
|
|
if (!supplierId) {
|
|
supplierId = (await trx('supplier').insert({ name: supplierName, supplierTypeId: 1 }).returning('id'))[0]
|
|
?.id
|
|
}
|
|
|
|
invoiceId = (
|
|
await trx('invoice').insert({ financialYearId: currentYear.id, fiskenNumber, supplierId }).returning('id')
|
|
)[0]?.id
|
|
}
|
|
|
|
if (transaction.accountNumber === 2441 && currentEntry.description?.includes('Fakturajournal')) {
|
|
await trx('invoice').update('amount', Math.abs(transaction.amount)).where('id', invoiceId)
|
|
}
|
|
}
|
|
|
|
if (invoiceId && currentInvoiceId) {
|
|
throw new Error('invoiceId and currentInvoiceId')
|
|
}
|
|
|
|
const transactionId = (
|
|
await trx('transaction')
|
|
.insert({
|
|
entryId: currentEntry.id,
|
|
...transaction,
|
|
invoiceId: invoiceId || currentInvoiceId,
|
|
})
|
|
.returning('id')
|
|
)[0].id
|
|
|
|
if (objectList) {
|
|
for (const [dimensionNumber, objectNumber] of objectList) {
|
|
const objectId = (
|
|
await trx('object')
|
|
.first('object.id')
|
|
.innerJoin('dimension', 'object.dimension_id', 'dimension.id')
|
|
.where({
|
|
'object.number': objectNumber,
|
|
'dimension.number': dimensionNumber,
|
|
})
|
|
)?.id
|
|
|
|
if (!objectId) {
|
|
throw new Error(`Object {${dimensionNumber} ${objectNumber}} does not exist!`)
|
|
}
|
|
|
|
await trx('transactionsToObjects').insert({
|
|
transactionId,
|
|
objectId,
|
|
})
|
|
}
|
|
}
|
|
|
|
break
|
|
}
|
|
case '#UB': {
|
|
const { yearNumber, accountNumber, balance, quantity } = parseUB(line)
|
|
|
|
if (yearNumber !== 0) continue
|
|
|
|
const existingAccountBalance = await trx('accountBalance')
|
|
.first('*')
|
|
.where({ financialYearId: currentYear.id, accountNumber })
|
|
|
|
if (!existingAccountBalance) {
|
|
await trx('accountBalance').insert({
|
|
financialYearId: currentYear.id,
|
|
accountNumber,
|
|
out: balance,
|
|
outQuantity: quantity,
|
|
})
|
|
} else {
|
|
await trx('accountBalance')
|
|
.update({
|
|
out: balance,
|
|
outQuantity: quantity,
|
|
})
|
|
.where({
|
|
financialYearId: currentYear.id,
|
|
accountNumber,
|
|
})
|
|
}
|
|
|
|
break
|
|
}
|
|
case '#VER': {
|
|
const { journal, ...rest } = parseVer(line)
|
|
|
|
let journalId = await getJournalId(journal)
|
|
|
|
// Fisken - Fakturajournal 1248
|
|
// Fisken - Utbetalningsjournal 1056
|
|
|
|
// Levfakt EURO FINANS AB (5) / Levfakt Per Holmberg (6)
|
|
// Levbet EURO FINANS AB (5) / Levbet Per Holmberg (6)
|
|
const rPhm = /^Lev(?:fakt|bet)\s+[^(]*\((\d+)\)$/
|
|
|
|
const result = rest.description.match(rPhm)
|
|
|
|
if (result) {
|
|
currentInvoiceId = (
|
|
await trx('invoice')
|
|
.select('invoice.*', 'supplier.name')
|
|
.innerJoin('supplier', 'invoice.supplierId', 'supplier.id')
|
|
.where('phmNumber', result[1])
|
|
)[0]?.id
|
|
} else {
|
|
currentInvoiceId = null
|
|
}
|
|
|
|
currentEntry = (
|
|
await trx('entry')
|
|
.insert({
|
|
journalId,
|
|
financialYearId: currentYear!.id,
|
|
...rest,
|
|
})
|
|
.returning(['id', 'description'])
|
|
)[0]
|
|
console.log(currentEntry)
|
|
|
|
break
|
|
}
|
|
default:
|
|
details[lineType] = splitLine.slice(1).join(' ')
|
|
}
|
|
}
|
|
|
|
await trx.commit()
|
|
|
|
console.dir(details)
|
|
|
|
console.info(`DONE!: ${currentYear.startDate} - ${currentYear.endDate}`)
|
|
}
|