summaryrefslogtreecommitdiff
path: root/src/services/uploads.service.ts
blob: 3235f4c2f7145e2be5a9c17f7ecd2433c80eb5d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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 dir = './documents';
const blobStorage = fs(dir);
const uploads = blobService({ Model: blobStorage });

// Async wrapper for pdfreader
const parsePdfItems = async (fileName: string): Promise<any[]> => {
  return new Promise((resolve, reject) => {
    const reader = new PdfReader();
    const items: any[] = [];

    reader.parseFileItems(fileName, (err: Error, item: any) => {
      if (err) reject(err);
      else if (item) items.push(item);
      else resolve(items);
    });
  });
};


const parseTransfersBill = async (context: HookContext): Promise<HookContext> => {
  const { id } = context.result;
  const fileName = `${dir}/${id}`;

  const items = await parsePdfItems(fileName);
  const hash = _.groupBy(items, 'y')
  const rows = _.map(hash, (elements: any[]) => _.map(elements, 'text'));

  const accountName = rows.find(row => row[0]?.startsWith('Наименование'))?.[0].slice(13);
  const accountCodeRow = rows.find(row => row[0]?.startsWith('Счет клиента'));
  const accountCode = new RegExp(/Счет клиента (\w+)/g).exec(accountCodeRow?.[0])?.[1];

  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.utc(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;
  }, []);


  // At this point we can remove the file
  context.service.remove(id);

  const accountId = await context.app
    .service('accounts')
    .find({ code: accountCode })
    .then((results: any[]) => {
      if (results.length) return results[0];
      return context.app
        .service('accounts')
        .create({ code: accountCode, name: accountName });
    })
    .then((account: any) => account._id);

  context.result = await 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,
      accountId,
    });
  });

  return context;
};


export default (app: Application): void => {
  app.use('/uploads', uploads);

  app.service('uploads').hooks({
    after: {
      create: parseTransfersBill,
    },
  });
};