diff options
| -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" | 
