diff options
author | eug-vs <eug-vs@keemail.me> | 2021-03-20 17:01:41 +0300 |
---|---|---|
committer | eug-vs <eug-vs@keemail.me> | 2021-03-20 17:01:41 +0300 |
commit | f0fe802df9fd7e98c0333a80922f6008cab7abab (patch) | |
tree | 9d4ec9e822621c4693e015edf17811c340e8a1be | |
parent | 2434c687bbc8f5b42b380a75e48cf1879a508674 (diff) | |
download | commercel-api-f0fe802df9fd7e98c0333a80922f6008cab7abab.tar.gz |
feat: parse pdf bills into transfers
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/models/contractor/contractor.schema.ts | 5 | ||||
-rw-r--r-- | src/services/uploads.service.ts | 79 | ||||
-rw-r--r-- | src/types.ts | 1 | ||||
-rw-r--r-- | yarn.lock | 42 |
5 files changed, 123 insertions, 5 deletions
diff --git a/package.json b/package.json index 5701420..5baa782 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "fs-blob-store": "^6.0.0", "moment": "^2.29.1", "mongoose": "^5.12.0", + "pdfreader": "^1.2.8", "ts-node": "^9.1.1", "typescript": "^4.2.3" } diff --git a/src/models/contractor/contractor.schema.ts b/src/models/contractor/contractor.schema.ts index e3f7de6..d5ac115 100644 --- a/src/models/contractor/contractor.schema.ts +++ b/src/models/contractor/contractor.schema.ts @@ -13,6 +13,9 @@ export const contractorSchema = new Schema({ fullName: String, vatId: String, type: String, - debt: Number + debt: { + type: Number, + default: 0 + } }, { timestamps: true }); 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<HookContext> => { + 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, + }, + }); }; diff --git a/src/types.ts b/src/types.ts index 745d7f2..fd1e7a2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,4 @@ declare module 'fs-blob-store'; declare module 'feathers-blob'; +declare module 'pdfreader'; @@ -1099,7 +1099,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.20: +lodash@^4.17.13, lodash@^4.17.20: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -1165,6 +1165,11 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -1283,6 +1288,14 @@ once@^1.3.0, once@^1.4.0: dependencies: wrappy "1" +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + pako@~1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -1313,6 +1326,23 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +pdf2json@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pdf2json/-/pdf2json-1.2.0.tgz#fdb395fad1d18248756cfbc758066a2f15b903bf" + integrity sha512-Z/m+OFOe13Nn2SHQNSINZ6Mh2b8t2bK3whL3L6b5Av1wqDvotYvpMg1Zi8aEPV37jF0jG0yQ83c8XuuNbIsn6Q== + dependencies: + async "^3.2.0" + lodash "^4.17.13" + optimist "^0.6.1" + xmldom "^0.3.0" + +pdfreader@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/pdfreader/-/pdfreader-1.2.8.tgz#9f82ef695ca0bc4524f79fd5ca55f014205706b7" + integrity sha512-LD9NdIaY1vc8kUcuFpJPHJF4nbyc1HHL3TehyiEw9KC7nN175pOfmdRUkjUGW28xr2f+ADLLGLl7ZyDXL7EBRw== + dependencies: + pdf2json "1.2.0" + printj@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" @@ -1732,6 +1762,11 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -1747,6 +1782,11 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmldom@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a" + integrity sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g== + xmlhttprequest-ssl@~1.5.4: version "1.5.5" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" |