From f0fe802df9fd7e98c0333a80922f6008cab7abab Mon Sep 17 00:00:00 2001 From: eug-vs Date: Sat, 20 Mar 2021 17:01:41 +0300 Subject: feat: parse pdf bills into transfers --- src/services/uploads.service.ts | 79 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-) (limited to 'src/services/uploads.service.ts') diff --git a/src/services/uploads.service.ts b/src/services/uploads.service.ts index 520079f..1d39a66 100644 --- a/src/services/uploads.service.ts +++ b/src/services/uploads.service.ts @@ -1,13 +1,86 @@ import { Application } from '@feathersjs/express'; +import { HookContext } from '@feathersjs/feathers'; import blobService from 'feathers-blob'; import fs from 'fs-blob-store'; +import moment from 'moment'; import Bluebird from 'bluebird'; +import { PdfReader } from 'pdfreader' import _ from 'lodash'; -const blobStorage = fs('./uploads'); - +const dir = './documents'; +const blobStorage = fs(dir); const uploads = blobService({ Model: blobStorage }); + +const parseTransfersBill = async (context: HookContext): Promise => { + const { id } = context.result; + const fileName = `${dir}/${id}`; + + const reader = new PdfReader(); + const items: any[] = []; + + reader.parseFileItems(fileName, (err: Error, item: any) => { + if (item) items.push(item); + else { // Finished parsing. TODO: wrap into async + const hash = _.groupBy(items, 'y') + const rows = _.map(hash, (elements: any[]) => _.map(elements, 'text')); + + const transfers = rows.reduce((acc: any, cols: string[]) => { + if (cols[0]?.startsWith('УНП')) { + acc[acc.length - 1].vatId = cols[2]; + acc[acc.length - 1].name = cols[3]; + } else if (cols.length === 7) { + const [dateString, doc, op, code, account, debet, credit] = cols; + try { + const date = moment(dateString, 'DD.MM.YYYY').toISOString(); + acc.push({ date, doc, op, code, account, debet, credit }); + } catch (e) { + console.log(`Skipping row because not a date: ${dateString}`); + } + } + + return acc; + }, []); + + return Bluebird.mapSeries(transfers, async (transfer: any) => { + const { date, vatId, name } = transfer; + const debet = parseFloat(transfer.debet.replace(/ /g, '')); + const credit = parseFloat(transfer.credit.replace(/ /g, '')); + + const operation = debet ? 'out' : 'in'; + const amount = debet || credit; + + const contractorId = await context.app + .service('contractors') + .find({ query: { vatId } }) + .then((results: any[]) => { + if (results.length) return results[0]; + return context.app + .service('contractors') + .create({ vatId, name }); + }) + .then((contractor: any) => contractor._id); + + return context.app.service('transfers').create({ + date, + operation, + amount, + contractorId, + }); + }).then(() => context.service.remove(id)); + } + }); + + return context; +}; + + export default (app: Application): void => { - app.use('/uploads', uploads) + app.use('/uploads', uploads); + + app.service('uploads').hooks({ + after: { + create: parseTransfersBill, + }, + }); }; -- cgit v1.2.3