n8n-nodes-contract-lite 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "n8n-nodes-contract-lite",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Lightweight contract PDF generator for n8n",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
7
10
  "scripts": {
8
- "build": "tsc ",
9
- "prepare": "npm run build"
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
10
13
  },
11
14
  "keywords": [
12
15
  "n8n-node",
@@ -23,7 +26,7 @@
23
26
  },
24
27
  "n8n": {
25
28
  "nodes": [
26
- "dist/src/ContractGenerator.node.js"
29
+ "dist/ContractGenerator.node.js"
27
30
  ]
28
31
  },
29
32
  "devDependencies": {
@@ -32,4 +35,4 @@
32
35
  "n8n-workflow": "^1.120.6",
33
36
  "typescript": "^5.9.3"
34
37
  }
35
- }
38
+ }
package/index.ts DELETED
@@ -1,3 +0,0 @@
1
- import { ContractGenerator } from './src/ContractGenerator.node';
2
-
3
- export { ContractGenerator };
@@ -1,16 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const srcDir = path.join(__dirname, '../src');
5
- const distDir = path.join(__dirname, '../dist/src');
6
-
7
- if (!fs.existsSync(distDir)) {
8
- fs.mkdirSync(distDir, { recursive: true });
9
- }
10
-
11
- fs.readdirSync(srcDir).forEach(file => {
12
- if (file.endsWith('.ttf')) {
13
- fs.copyFileSync(path.join(srcDir, file), path.join(distDir, file));
14
- console.log(`Copied ${file} to dist/src/`);
15
- }
16
- });
@@ -1,103 +0,0 @@
1
- import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
- import { DocumentEngine } from './DocumentEngine';
3
-
4
- export class ContractGenerator implements INodeType {
5
- description: INodeTypeDescription = {
6
- displayName: 'Document Generator Pro (RU)',
7
- name: 'documentGeneratorPro',
8
- icon: 'fa:file-signature',
9
- group: ['transform'],
10
- version: 1,
11
- description: 'Премиальный генератор документов с защитой',
12
- defaults: { name: 'Генератор документов' },
13
- inputs: ['main'],
14
- outputs: ['main'],
15
- properties: [
16
- {
17
- displayName: 'Тип шаблона',
18
- name: 'template',
19
- type: 'options',
20
- options: [
21
- { name: 'Счет (Invoice)', value: 'invoice' },
22
- { name: 'Договор (Contract)', value: 'contract' },
23
- { name: 'Накладная (Waybill)', value: 'waybill' },
24
- { name: 'Акт (Act)', value: 'act' },
25
- ],
26
- default: 'invoice',
27
- },
28
- { displayName: 'Название компании', name: 'companyName', type: 'string', default: 'ООО КНК ГРУПП' },
29
- { displayName: 'Имя клиента', name: 'clientName', type: 'string', default: 'Иван Иванов' },
30
-
31
- // Настройки штампа
32
- { displayName: 'Показать штамп', name: 'showStamp', type: 'boolean', default: true },
33
- {
34
- displayName: 'Текст штампа',
35
- name: 'status',
36
- type: 'string',
37
- default: 'ОПЛАЧЕНО',
38
- displayOptions: { show: { showStamp: [true] } }
39
- },
40
-
41
- // Контент
42
- {
43
- displayName: 'Текст документа',
44
- name: 'content',
45
- type: 'string',
46
- typeOptions: { rows: 5 },
47
- default: 'Настоящий документ подтверждает...',
48
- displayOptions: { show: { template: ['contract', 'act'] } }
49
- },
50
- {
51
- displayName: 'Данные таблицы (JSON)',
52
- name: 'details',
53
- type: 'json',
54
- default: '[]',
55
- description: 'Пример: [{"item": "Услуга", "qty": 1, "price": 100, "total": 100}]',
56
- displayOptions: { hide: { template: ['contract'] } }
57
- },
58
-
59
- // Стили
60
- { displayName: 'Цвет бренда', name: 'headerColor', type: 'color', default: '#1a2b3c' },
61
- ],
62
- };
63
-
64
- async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
65
- const items = this.getInputData();
66
- const returnData: INodeExecutionData[] = [];
67
-
68
- for (let i = 0; i < items.length; i++) {
69
- const template = this.getNodeParameter('template', i) as string;
70
-
71
- // Вызываем статичный метод через имя класса
72
- const title = ContractGenerator.getTitle(template);
73
-
74
- const data = {
75
- companyName: this.getNodeParameter('companyName', i),
76
- clientName: this.getNodeParameter('clientName', i),
77
- showStamp: this.getNodeParameter('showStamp', i),
78
- status: this.getNodeParameter('showStamp', i) ? this.getNodeParameter('status', i) : '',
79
- content: this.getNodeParameter('content', i, ''),
80
- details: this.getNodeParameter('details', i, '[]'),
81
- headerColor: this.getNodeParameter('headerColor', i),
82
- title: title,
83
- };
84
-
85
- const buffer = await DocumentEngine.createPDF(data, template);
86
- const binaryData = await this.helpers.prepareBinaryData(buffer, `${template}_${Date.now()}.pdf`);
87
-
88
- returnData.push({ json: { success: true, timestamp: new Date() }, binary: { data: binaryData } });
89
- }
90
- return [returnData];
91
- }
92
-
93
- // Добавили static
94
- private static getTitle(template: string): string {
95
- const titles: any = {
96
- invoice: 'Счет на оплату',
97
- contract: 'Договор поставки',
98
- act: 'Акт выполненных работ',
99
- waybill: 'Товарная накладная'
100
- };
101
- return titles[template] || 'Документ';
102
- }
103
- }
@@ -1,141 +0,0 @@
1
- import PDFDocument from 'pdfkit';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import crypto from 'crypto';
5
-
6
- export class DocumentEngine {
7
- static async createPDF(data: any, template: string): Promise<Buffer> {
8
- return new Promise((resolve, reject) => {
9
- const doc = new PDFDocument({ size: 'A4', margin: 40, bufferPages: true });
10
- const chunks: Buffer[] = [];
11
- doc.on('data', chunk => chunks.push(chunk));
12
- doc.on('end', () => resolve(Buffer.concat(chunks)));
13
- doc.on('error', reject);
14
-
15
- const fontPath = path.join(__dirname, 'Roboto-Regular.ttf');
16
- if (fs.existsSync(fontPath)) doc.registerFont('Main', fontPath);
17
- doc.font('Main');
18
-
19
- const accent = data.headerColor || '#1a2b3c';
20
-
21
- // 1. Фоновый паттерн (Фишка: микро-сетка)
22
- this.drawPattern(doc);
23
-
24
- // 2. Премиум Шапка
25
- this.drawHeader(doc, data, accent);
26
-
27
- // 3. Инфо-карточки
28
- this.drawCards(doc, data, accent);
29
-
30
- // 4. Штамп
31
- if (data.showStamp && data.status) {
32
- this.drawStamp(doc, data.status, accent);
33
- }
34
-
35
- // 5. Контент
36
- let details = typeof data.details === 'string' ? JSON.parse(data.details || '[]') : data.details;
37
- if (!Array.isArray(details)) details = [];
38
-
39
- doc.y = 280;
40
- switch (template) {
41
- case 'invoice':
42
- this.drawTable(doc, ['Наименование', 'Кол-во', 'Цена', 'Сумма'], details, accent);
43
- break;
44
- case 'contract':
45
- doc.fillColor(accent).fontSize(14).text('ПРЕДМЕТ ДОГОВОРА').moveDown();
46
- doc.fillColor('#444').fontSize(11).text(data.content, { align: 'justify', lineGap: 4 });
47
- break;
48
- case 'act':
49
- doc.fillColor('#333').fontSize(11).text(data.content).moveDown();
50
- this.drawTable(doc, ['Услуга', 'Период', 'Статус', 'Итого'], details, accent);
51
- break;
52
- case 'waybill':
53
- this.drawTable(doc, ['Товар', 'Артикул', 'Ед.изм', 'Кол-во'], details, accent);
54
- break;
55
- }
56
-
57
- // 6. Подписи и Футер
58
- this.drawSignatures(doc, data, accent);
59
- this.drawFooter(doc, accent);
60
-
61
- doc.end();
62
- });
63
- }
64
-
65
- private static drawPattern(doc: any) {
66
- doc.save().strokeColor('#000').lineWidth(0.1).strokeOpacity(0.05);
67
- for(let i=0; i<842; i+=25) {
68
- doc.moveTo(0, i).lineTo(595, i).stroke();
69
- }
70
- doc.restore();
71
- }
72
-
73
- private static drawHeader(doc: any, data: any, color: string) {
74
- doc.rect(0, 0, 15, 140).fill(color); // Стильная полоса слева
75
- doc.rect(40, 40, 520, 80).fillOpacity(0.03).fill(color).fillOpacity(1);
76
-
77
- doc.fillColor(color).fontSize(22).text(data.companyName.toUpperCase(), 60, 60);
78
- doc.fillColor('#666').fontSize(10).text(data.title, 60, 88);
79
-
80
- const docId = crypto.randomBytes(4).toString('hex').toUpperCase();
81
- doc.fillColor(color).fontSize(9).text('ID ДОКУМЕНТА', 400, 60, { align: 'right', width: 140 });
82
- doc.fontSize(12).text(docId, 400, 72, { align: 'right', width: 140 });
83
- doc.fontSize(9).fillColor('#999').text(new Date().toLocaleDateString('ru-RU'), 400, 88, { align: 'right', width: 140 });
84
- }
85
-
86
- private static drawCards(doc: any, data: any, color: string) {
87
- const card = (x: number, y: number, label: string, text: string) => {
88
- doc.rect(x, y, 250, 50).fill('#fcfcfc').strokeColor('#eee').lineWidth(1).stroke();
89
- doc.fillColor(color).fontSize(7).text(label, x + 10, y + 10);
90
- doc.fillColor('#333').fontSize(10).text(text, x + 10, y + 25);
91
- };
92
- card(40, 160, 'ОТПРАВИТЕЛЬ', data.companyName);
93
- card(310, 160, 'ПОЛУЧАТЕЛЬ', data.clientName);
94
- }
95
-
96
- private static drawStamp(doc: any, text: string, color: string) {
97
- doc.save();
98
- doc.rotate(-15, { origin: [500, 250] });
99
- doc.rect(430, 220, 120, 35).lineWidth(2).strokeColor(color).stroke();
100
- doc.fillColor(color).fontSize(14).text(text.toUpperCase(), 430, 232, { width: 120, align: 'center' });
101
- doc.restore();
102
- }
103
-
104
- private static drawTable(doc: any, headers: string[], rows: any[], color: string) {
105
- let y = doc.y + 20;
106
- doc.rect(40, y, 520, 22).fill(color);
107
- doc.fillColor('#fff').fontSize(8);
108
- headers.forEach((h, i) => doc.text(h, 50 + (i * 130), y + 7));
109
-
110
- y += 30;
111
- doc.fillColor('#333').fontSize(9);
112
- rows.forEach((row: any) => {
113
- const vals = Object.values(row);
114
- vals.forEach((v: any, i) => doc.text(String(v), 50 + (i * 130), y));
115
- y += 20;
116
- doc.moveTo(40, y - 5).lineTo(560, y - 5).strokeColor('#eee').stroke();
117
- if (y > 700) { doc.addPage(); y = 50; }
118
- });
119
- doc.y = y;
120
- }
121
-
122
- private static drawSignatures(doc: any, data: any, color: string) {
123
- const y = 680;
124
- doc.moveTo(40, y).lineTo(560, y).strokeColor('#eee').stroke();
125
- doc.fontSize(8).fillColor('#999').text('М.П. ПОДПИСЬ ОТПРАВИТЕЛЯ', 40, y + 15);
126
- doc.text('ПОДПИСЬ ПОЛУЧАТЕЛЯ', 310, y + 15);
127
-
128
- doc.fillColor('#333').fontSize(10).text(data.companyName, 40, y + 45);
129
- doc.text(data.clientName, 310, y + 45);
130
- doc.moveTo(40, y + 40).lineTo(200, y + 40).strokeColor('#ccc').stroke();
131
- doc.moveTo(310, y + 40).lineTo(470, y + 40).stroke();
132
- }
133
-
134
- private static drawFooter(doc: any, color: string) {
135
- const y = 790;
136
- const hash = crypto.randomBytes(16).toString('hex');
137
- doc.fontSize(6).fillColor('#ccc').text(`SECURE HASH: ${hash}`, 40, y);
138
- doc.fontSize(8).fillColor('#999').text('Документ сформирован автоматически и защищен цифровым идентификатором.', 40, y + 10);
139
- doc.rect(530, y - 5, 30, 30).fill(color);
140
- }
141
- }
Binary file
package/src/types.ts DELETED
@@ -1,8 +0,0 @@
1
- export interface IDocumentData {
2
- title: string;
3
- companyName: string;
4
- headerColor: string;
5
- content: string; // Основной текст (можно передавать готовый от ИИ)
6
- details: Array<{ label: string; value: string }>; // Таблица параметров
7
- footer: string;
8
- }
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "CommonJS",
5
- "declaration": true,
6
- "outDir": "dist",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true
10
- },
11
- "include": ["src/**/*.ts", "index.ts"]
12
- }