@treeunfe/danfe 1.0.3

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.
Files changed (29) hide show
  1. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/NFCEGerarDanfe/NFCEGerarDanfe.d.ts +58 -0
  2. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/NFCEGerarDanfe/NFCEGerarDanfe.js +495 -0
  3. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/NFCEGerarDanfe/NFCEGerarDanfe.js.map +1 -0
  4. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/NFEGerarDanfe/NFEGerarDanfe.d.ts +39 -0
  5. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/NFEGerarDanfe/NFEGerarDanfe.js +1097 -0
  6. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/NFEGerarDanfe/NFEGerarDanfe.js.map +1 -0
  7. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/index.d.ts +2 -0
  8. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/index.js +19 -0
  9. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/src/index.js.map +1 -0
  10. package/.rollup.cache/home/runner/work/treeunfe-dfe/treeunfe-dfe/packages/danfe/dist/tsconfig.tsbuildinfo +1 -0
  11. package/.turbo/turbo-build.log +15 -0
  12. package/.turbo/turbo-typecheck.log +4 -0
  13. package/CHANGELOG.md +27 -0
  14. package/LICENSE.txt +221 -0
  15. package/dist/index.cjs +1591 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.mjs +1587 -0
  18. package/dist/index.mjs.map +1 -0
  19. package/dist/src/NFCEGerarDanfe/NFCEGerarDanfe.d.ts +58 -0
  20. package/dist/src/NFEGerarDanfe/NFEGerarDanfe.d.ts +39 -0
  21. package/dist/src/index.d.ts +2 -0
  22. package/dist/tsconfig.tsbuildinfo +1 -0
  23. package/package.json +63 -0
  24. package/rollup.config.js +94 -0
  25. package/src/NFCEGerarDanfe/NFCEGerarDanfe.ts +598 -0
  26. package/src/NFEGerarDanfe/NFEGerarDanfe.ts +1223 -0
  27. package/src/bwip-js.d.ts +16 -0
  28. package/src/index.ts +19 -0
  29. package/tsconfig.json +16 -0
@@ -0,0 +1,1223 @@
1
+ /*
2
+ * This file is part of Treeunfe DFe.
3
+ *
4
+ * Treeunfe DFe is free software: you can redistribute it and/or modify
5
+ * it under the terms of the GNU General Public License as published by
6
+ * the Free Software Foundation, either version 3 of the License, or
7
+ * (at your option) any later version.
8
+ *
9
+ * Treeunfe DFe is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with Treeunfe DFe. If not, see <https://www.gnu.org/licenses/>.
16
+ */
17
+ import { ValidaCPFCNPJ } from '@treeunfe/shared';
18
+ import bwipjs from 'bwip-js';
19
+ import { format } from 'date-fns';
20
+ import fs from 'fs';
21
+ import PDFDocument from 'pdfkit';
22
+ import { Dest, DetProd, Emit, ICMS, Ide, InfAdic, IPI, NFEGerarDanfeProps, ProtNFe, Total, Transp, Vol } from '@treeunfe/types';
23
+
24
+
25
+ class NFEGerarDanfe {
26
+ data: NFEGerarDanfeProps['data'];
27
+ chave: string;
28
+ enviada: boolean;
29
+ outputPath: string;
30
+ documento: InstanceType<typeof ValidaCPFCNPJ>;
31
+ protNFe: ProtNFe | undefined;
32
+ det: DetProd | DetProd[];
33
+ ide: Ide;
34
+ dest: Dest | undefined;
35
+ emit: Emit;
36
+ total: Total;
37
+ transp: Transp;
38
+ infAdic: InfAdic | undefined;
39
+ exibirMarcaDaguaDanfe?: boolean;
40
+ doc: InstanceType<typeof PDFDocument>;
41
+ barcodeBuffer: Buffer | null = null;
42
+
43
+ constructor(props: NFEGerarDanfeProps) {
44
+ const { data, chave, outputPath } = props;
45
+
46
+ this.data = data;
47
+ this.chave = chave.trim();
48
+ this.outputPath = outputPath;
49
+ this.enviada = false;
50
+ this.documento = new ValidaCPFCNPJ();
51
+ this.protNFe = data.protNFe;
52
+
53
+ const nfeData = Array.isArray(data.NFe) ? data.NFe[0] : data.NFe;
54
+ const { det, ide, emit, dest, total, transp, infAdic } = nfeData.infNFe;
55
+
56
+ this.det = det;
57
+ this.ide = ide;
58
+ this.emit = emit;
59
+ this.total = total;
60
+ this.transp = transp;
61
+ this.infAdic = infAdic;
62
+ if (dest) this.dest = dest;
63
+
64
+ if (this.protNFe?.infProt.nProt) {
65
+ this.enviada = true;
66
+ }
67
+
68
+ // Área útil ignorando margem à direita (22.68) e esquerda (5.67) = 566.93
69
+ this.doc = new PDFDocument({
70
+ margins: { top: 22, right: 22.68, bottom: 12, left: 5.67 },
71
+ size: 'a4', // 595.28 * 841.89
72
+ bufferPages: true,
73
+ layout: 'portrait',
74
+ font: 'Times-Roman',
75
+ });
76
+ }
77
+
78
+ async generateBarcode(data: string): Promise<Buffer | null> {
79
+ try {
80
+ const pngBuffer = await bwipjs.toBuffer({
81
+ bcid: 'code128',
82
+ text: data,
83
+ scaleX: 4,
84
+ height: 14,
85
+ includetext: false,
86
+ });
87
+ this.barcodeBuffer = pngBuffer; // Armazena o buffer na instância
88
+ return pngBuffer; // Retorna o buffer
89
+ } catch (err) {
90
+ console.error('Erro ao gerar código de barras:', err);
91
+ this.barcodeBuffer = null;
92
+ return null;
93
+ }
94
+ }
95
+
96
+ setLineStyle(lineWidth: number, strokeColor: string) {
97
+ this.doc.lineWidth(lineWidth).strokeColor(strokeColor).fill('black');
98
+ }
99
+
100
+ drawHeader(isFirstPage: boolean) {
101
+ if (isFirstPage) {
102
+ this._buildGuia();
103
+ this._buildSeparator();
104
+ this._buildHeader(52);
105
+ } else {
106
+ this._buildHeader(0);
107
+ }
108
+ // this.doc.moveDown();
109
+ }
110
+
111
+ drawFooter() {
112
+ this._buildFooter();
113
+ }
114
+
115
+ _buildGuia() {
116
+ const { top, left } = this.doc.page.margins;
117
+ this.setLineStyle(0.75, '#1c1c1c');
118
+
119
+ /** TOP LEFT */
120
+ this.doc.rect(left, top, 470, 21).stroke();
121
+ this.doc.fontSize(5).text(`RECEBEMOS DE ${this.emit.xNome} OS PRODUTOS / SERVIÇOS CONSTANTES DA NOTA FISCAL INDICADO AO LADO`, 10, 26, {
122
+ characterSpacing: 0.5
123
+ });
124
+ this.doc.fontSize(6).text(`EMISSÃO: ${format(new Date(this.ide.dhEmi), 'dd-MM-yyyy')} - DEST. / REM.: ${this.dest?.xNome || ''} - VALOR TOTAL: R$ ${parseFloat(String(this.total.ICMSTot.vNF)).toFixed(2)}`, 10, 33.5, {
125
+ characterSpacing: 0.5
126
+ });
127
+ /** RIGHT */
128
+ this.doc.rect(left + 470, top, 96.93, 42).stroke();
129
+ this.doc.fontSize(11).text(`NF-e`, 480, 27, {
130
+ characterSpacing: 1.5,
131
+ align: 'center'
132
+ });
133
+ this.doc.fontSize(8.8).font('Times-Bold').text(`Nº ${String(this.ide.nNF).padStart(2, '0')}`, 480, 40.5, {
134
+ characterSpacing: 1.5,
135
+ align: 'center',
136
+ });
137
+ this.doc.fontSize(8.5).font('Times-Roman').text(`SÉRIE ${this.ide.serie.padStart(3, '0')}`, 480, 53, {
138
+ characterSpacing: 1.5,
139
+ align: 'center',
140
+ });
141
+ /** BOTTON LEFT */
142
+ this.doc.rect(left, top + 21, 75, 21).stroke();
143
+ this.doc.fontSize(5).text(`DATA DE RECEBIMENTO`, 10, 46.5, {
144
+ characterSpacing: 0.5,
145
+ });
146
+ /** BOTTON RIGHT */
147
+ this.doc.rect(left + 75, top + 21, 395, 21).stroke();
148
+ this.doc.fontSize(5).text(`IDENTIFICAÇÃO E ASSINATURA DO RECEBEDOR`, 75 + 10, 46.5, {
149
+ characterSpacing: 0.5
150
+ });
151
+ }
152
+
153
+ _buildSeparator() {
154
+ const { left, right } = this.doc.page.margins;
155
+ const pageWidth = 595.28; // Largura da página A4 em pontos
156
+
157
+ // Calcule a largura da linha, considerando as margens esquerda e direita
158
+ const lineWidth = pageWidth - left - right;
159
+
160
+ // Desenhe a linha
161
+ this.doc.moveTo(left, 69) // Início da linha
162
+ .lineTo(left + lineWidth, 69) // Fim da linha
163
+ .lineWidth(1) // Espessura da linha
164
+ .dash(1.5, { space: 1.5 }) // Estilo pontilhado
165
+ .strokeColor('black') // Cor da linha
166
+ .stroke(); // Desenhar a linha
167
+
168
+ this.doc.undash();
169
+ }
170
+
171
+ _buildHeader(pos: number) {
172
+ const { top, left } = this.doc.page.margins;
173
+ const page = this.doc.bufferedPageRange();
174
+ const CNPJCPF = this.emit.CNPJCPF?.toString()
175
+ const CNPJ = this.emit.CNPJ?.toString()
176
+ const CPF = this.emit.CPF?.toString()
177
+ const documento = this.documento.mascaraCnpjCpf(CNPJCPF || CNPJ || CPF || '')
178
+ this.setLineStyle(0.75, '#1c1c1c');
179
+
180
+ const topIdentificacao_1 = top + pos;
181
+ const identificationJoined = `\nCEP: ${this.emit.enderEmit.CEP} - ${this.emit.enderEmit.xBairro} - ${this.emit.enderEmit.UF}\nTEL: ${this.emit.enderEmit.fone} - FAX: ${this.emit.enderEmit.fone}`
182
+
183
+ /** IDENTIFICACAO EMITENTE */
184
+ const _buildIdentificacaoEmit = () => {
185
+ this.doc.rect(left, topIdentificacao_1, 197.5, 98).stroke();
186
+ this.doc.fontSize(5).text(`IDENTIFICAÇÃO DO EMITENTE`, 10, topIdentificacao_1 + 3.5, {
187
+ characterSpacing: 0.5,
188
+ });
189
+ this.doc.fontSize(8).font('Times-Bold').text(this.emit.xNome, 35, topIdentificacao_1 + 14, {
190
+ characterSpacing: 1,
191
+ width: 170,
192
+ lineBreak: true,
193
+ lineGap: 4
194
+ });
195
+ this.doc.fontSize(7).font('Times-Roman').text(`${this.emit.enderEmit.xLgr}, ${this.emit.enderEmit.nro} ${this.emit.xFant ? `- ${this.emit.xFant}` : ''}`, 10, topIdentificacao_1 + 44, {
196
+ characterSpacing: 1,
197
+ width: 181,
198
+ lineBreak: true,
199
+ lineGap: 2,
200
+ continued: true
201
+ }).text(identificationJoined);
202
+ }
203
+
204
+ /** IDENTIFICACAO NFe */
205
+ const _buildIdentificacaoDanfe = () => {
206
+ this.doc.rect(left + 197.5, topIdentificacao_1, 112.7, 98).stroke();
207
+ this.doc.fontSize(12).font('Times-Bold').text(`DANFE`, 203, topIdentificacao_1 + 8, {
208
+ characterSpacing: 1.5,
209
+ width: 112.7,
210
+ align: 'center'
211
+ });
212
+ this.doc.fontSize(6).text(`DOCUMENTO AUXILIAR DA NOTA FISCAL ELETRÔNICA`, 215, topIdentificacao_1 + 20, {
213
+ characterSpacing: 0.5,
214
+ width: 95,
215
+ align: 'left',
216
+ lineGap: 2,
217
+ });
218
+ this.doc.fontSize(8).font('Times-Roman').text(`0 - ENTRADA`, 217, topIdentificacao_1 + 40, {
219
+ characterSpacing: 0.5,
220
+ align: 'left',
221
+ });
222
+ this.doc.fontSize(8).text(`1 - SAÍDA`, 217, topIdentificacao_1 + 50, {
223
+ characterSpacing: 0.5,
224
+ align: 'left',
225
+ });
226
+ this.doc.rect(left + 275, topIdentificacao_1 + 38, 18, 18).stroke();
227
+ this.doc.fontSize(14).font('Times-Bold').text(String(this.ide.tpNF), left + 280, topIdentificacao_1 + 42.5);
228
+ this.doc.fontSize(8.8).text(`Nº ${String(this.ide.nNF).padStart(2, '0')}`, 208, topIdentificacao_1 + 65, {
229
+ characterSpacing: 1,
230
+ });
231
+ this.doc.fontSize(10).font('Times-Roman').text(`fl. ${page.start + 1}/${page.count}`, 240, topIdentificacao_1 + 65, {
232
+ characterSpacing: 1.5,
233
+ width: 112.7,
234
+ align: 'center'
235
+ });
236
+ this.doc.fontSize(8.8).text(`SÉRIE ${this.ide.serie.padStart(3, '0')}`, 208, topIdentificacao_1 + 78, {
237
+ characterSpacing: 1.5,
238
+ width: 112.7,
239
+ align: 'center'
240
+ });
241
+ }
242
+
243
+ /** IDENTIFICACAO NFe (Barcode e Chave) */
244
+ const _buildIdentificacaoNFe = () => {
245
+ this.doc.rect(left + 310.2, topIdentificacao_1, 256.73, 35).stroke();
246
+ if (this.barcodeBuffer) { // Verifica se o buffer do barcode existe
247
+ this.doc.image(this.barcodeBuffer, left + 323.5, topIdentificacao_1 + 3, { width: 230, height: 30 });
248
+ } else {
249
+ this.doc.fontSize(8).fillColor('red').text('Erro ao carregar barcode', left + 316, topIdentificacao_1 + 12, {
250
+ width: 256.73, align: 'center'
251
+ });
252
+ }
253
+
254
+ if (Number(this.ide.tpAmb) !== 2 && !this.enviada && !this.data.forceTransmitida) {
255
+ this.doc.fontSize(14).font('Times-Bold').fillColor('red').text('NF-E NÃO ENVIADA PARA SEFAZ', left + 316, topIdentificacao_1 + 12, {
256
+ characterSpacing: 1,
257
+ width: 256.73,
258
+ });
259
+ }
260
+ this.setLineStyle(0.75, '#1c1c1c')
261
+ this.doc.rect(left + 310.2, topIdentificacao_1 + 35, 256.73, 23).stroke();
262
+ this.doc.fontSize(5).font('Times-Roman').text(`CHAVE DE ACESSO`, left + 314, topIdentificacao_1 + 38, {
263
+ characterSpacing: 0.5,
264
+ });
265
+ this.doc.fontSize(6).text(this.chave, left + 310.2, topIdentificacao_1 + 47, {
266
+ characterSpacing: 1,
267
+ width: 256.73,
268
+ align: 'center'
269
+ });
270
+
271
+ this.doc.rect(left + 310.2, topIdentificacao_1 + 58, 256.73, 40).stroke();
272
+ if (Number(this.ide.tpAmb) === 2 || this.enviada) {
273
+ this.doc.fontSize(6.5).text('Consulta de autenticidade no portal nacional da NF-e', left + 310.2, topIdentificacao_1 + 65, {
274
+ characterSpacing: 0.5,
275
+ width: 256.73,
276
+ align: 'center'
277
+ });
278
+ this.doc.fontSize(6.5).text('www.nfe.fazenda.gov.br/portal', left + 310.2, topIdentificacao_1 + 75, {
279
+ characterSpacing: 0.5,
280
+ width: 256.73,
281
+ align: 'center'
282
+ });
283
+ this.doc.fontSize(6.5).text('ou no site da Sefaz Autorizadora', left + 310.2, topIdentificacao_1 + 85, {
284
+ characterSpacing: 0.5,
285
+ width: 256.73,
286
+ align: 'center'
287
+ });
288
+ }
289
+
290
+ this.doc.rect(left, topIdentificacao_1 + 98, 310.2, 23).stroke();
291
+ this.doc.fontSize(5).text('NATUREZA DE OPERAÇÃO', left + 4, topIdentificacao_1 + 102, {
292
+ characterSpacing: 0.5,
293
+ });
294
+ this.doc.fontSize(8).text(this.ide.natOp, left + 4, topIdentificacao_1 + 112, {
295
+ characterSpacing: 1,
296
+ });
297
+
298
+ this.doc.rect(left + 310.2, topIdentificacao_1 + 98, 256.73, 23).stroke();
299
+ if (Number(this.ide.tpAmb) === 2 || this.enviada) {
300
+ let dhRecbto = '';
301
+ if (this.protNFe?.infProt.dhRecbto) {
302
+ dhRecbto = format(new Date(this.protNFe?.infProt.dhRecbto), 'dd/MM/yyyy HH:mm:ss')
303
+ }
304
+ const xProtNfe = `${String(this.protNFe?.infProt.nProt || '')} ${dhRecbto}`;
305
+ this.doc.fontSize(5).text('PROTOCOLO DE AUTORIZAÇÃO DE USO', left + 314.2, topIdentificacao_1 + 102, {
306
+ characterSpacing: 0.5,
307
+ });
308
+ this.doc.fontSize(8).text(xProtNfe, left + 314.2, topIdentificacao_1 + 112, {
309
+ characterSpacing: 1,
310
+ });
311
+ }
312
+ }
313
+
314
+ /** IDENTIFICACAO PESSOA */
315
+ const _buildIdentificacaoPessoa = () => {
316
+ this.doc.rect(left, topIdentificacao_1 + 121, 189.5, 23).stroke();
317
+ this.doc.fontSize(5).text('INSCRIÇÃO ESTADUAL', left + 4, topIdentificacao_1 + 125, {
318
+ characterSpacing: 0.5,
319
+ });
320
+ this.doc.fontSize(8).text(this.emit.IE, left + 5, topIdentificacao_1 + 135, {
321
+ characterSpacing: 1,
322
+ });
323
+
324
+ this.doc.rect(left + 189.5, topIdentificacao_1 + 121, 187.93, 23).stroke();
325
+ this.doc.fontSize(5).text('INSCRIÇÃO ESTADUAL DO SUBST. TRIB.', left + 193.5, topIdentificacao_1 + 125, {
326
+ characterSpacing: 0.5,
327
+ });
328
+ this.doc.fontSize(8).text(String(this.emit.IEST || ''), left + 194.5, topIdentificacao_1 + 135, {
329
+ characterSpacing: 1,
330
+ });
331
+
332
+ this.doc.rect(left + 377.43, topIdentificacao_1 + 121, 189.5, 23).stroke();
333
+ this.doc.fontSize(5).text('CNPJ / CPF', left + 381.43, topIdentificacao_1 + 125, {
334
+ characterSpacing: 0.5,
335
+ });
336
+ this.doc.fontSize(8).text(documento, left + 386.43, topIdentificacao_1 + 135, {
337
+ characterSpacing: 1,
338
+ });
339
+ }
340
+
341
+ _buildIdentificacaoEmit();
342
+ _buildIdentificacaoDanfe();
343
+ _buildIdentificacaoNFe();
344
+ _buildIdentificacaoPessoa();
345
+ }
346
+
347
+ _buildDestinatario() {
348
+ const { top, left } = this.doc.page.margins;
349
+ const docDest = this.documento.mascaraCnpjCpf(this.dest?.CNPJCPF || this.dest?.CNPJ || this.dest?.CPF || '')
350
+ this.setLineStyle(0.75, '#1c1c1c');
351
+ const topDestinatario = top + 90;
352
+
353
+ const _buildDestPessoa = () => {
354
+ this.doc.rect(left, topDestinatario + 120, 398, 23).stroke();
355
+ let xNome = 'NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL';
356
+ if (Number(this.ide.tpAmb) !== 2) {
357
+ xNome = String(this.dest?.xNome || '');
358
+ }
359
+ this.doc.fontSize(5).font('Times-Roman').text('NOME / RAZÃO SOCIAL', left + 4, topDestinatario + 125, {
360
+ characterSpacing: 0.5,
361
+ });
362
+ this.doc.fontSize(8).text(xNome, left + 5, topDestinatario + 135, {
363
+ characterSpacing: 1,
364
+ });
365
+
366
+ this.doc.rect(left + 398, topDestinatario + 120, 94, 23).stroke();
367
+ this.doc.fontSize(5).text('CNPJ / CPF', left + 402, topDestinatario + 125, {
368
+ characterSpacing: 0.5,
369
+ });
370
+ this.doc.fontSize(8).text(docDest, left + 403, topDestinatario + 135, {
371
+ characterSpacing: 1,
372
+ });
373
+
374
+ this.doc.rect(left + 492, topDestinatario + 120, 74.3, 23).stroke();
375
+ this.doc.fontSize(5).text('DATA DA EMISSÃO', left + 496, topDestinatario + 125, {
376
+ characterSpacing: 0.5,
377
+ });
378
+ this.doc.fontSize(8).text(format(new Date(this.ide.dhEmi), 'dd-MM-yyyy'), left + 497, topDestinatario + 135, {
379
+ characterSpacing: 1,
380
+ });
381
+ }
382
+ const _buildDestLogradouro = () => {
383
+ this.doc.rect(left, topDestinatario + 143, 320, 23).stroke();
384
+ this.doc.fontSize(5).text('ENDEREÇO', left + 4, topDestinatario + 148, {
385
+ characterSpacing: 0.5,
386
+ });
387
+ this.doc.fontSize(8).text(`${this.dest?.enderDest?.xLgr || ''}, ${this.dest?.enderDest?.nro || ''}`, left + 5, topDestinatario + 158, {
388
+ characterSpacing: 1,
389
+ });
390
+
391
+ this.doc.rect(left + 320, topDestinatario + 143, 118.5, 23).stroke();
392
+ this.doc.fontSize(5).text('BAIRRO / DISTRITO', left + 324, topDestinatario + 148, {
393
+ characterSpacing: 0.5,
394
+ });
395
+
396
+ const bairroText = String(this.dest?.enderDest?.xBairro || '');
397
+ const bairroCharLimit = 22;
398
+ const bairroMaxWidthForText = 110; // Largura útil para o texto dentro da célula
399
+ const defaultBairroFontSize = 8;
400
+ const reducedBairroFontSize = 5.5;
401
+ const defaultBairroCharSpacing = 1;
402
+ const reducedBairroCharSpacing = 0.25;
403
+
404
+ let fontSizeToUse = defaultBairroFontSize;
405
+ let charSpacingToUse = defaultBairroCharSpacing;
406
+
407
+ if (bairroText.length > bairroCharLimit) {
408
+ fontSizeToUse = reducedBairroFontSize;
409
+ charSpacingToUse = reducedBairroCharSpacing;
410
+ }
411
+
412
+ // Cálculo de Y para tentar alinhar (pode precisar de ajuste fino)
413
+ const textLineHeight = this.doc.currentLineHeight(); // Altura da linha com fontSizeToUse
414
+ const cellContentYBase = topDestinatario + 158; // Linha de base original
415
+ const cellHeightForText = 23 - 5 - 5; // Altura da célula - padding label - padding inferior
416
+ let yPosForBairro = cellContentYBase;
417
+ // Ajuste simples para tentar centralizar um pouco se a linha for mais baixa que o espaço
418
+ if (textLineHeight < cellHeightForText) {
419
+ yPosForBairro = cellContentYBase - ((cellHeightForText - textLineHeight) / 2) + (textLineHeight * 0.3); // ajuste
420
+ }
421
+ // Ajuste fino para alinhar com os outros campos na mesma linha Y
422
+ yPosForBairro = topDestinatario + 158; // Reset para a mesma linha Y dos outros campos
423
+
424
+ this.doc.font('Times-Roman').fontSize(fontSizeToUse);
425
+
426
+ this.doc.text(bairroText, left + 320 + 3, yPosForBairro, { // Padding X de 3pt
427
+ characterSpacing: charSpacingToUse,
428
+ width: bairroMaxWidthForText,
429
+ lineBreak: false,
430
+ ellipsis: true,
431
+ });
432
+
433
+ this.doc.font('Times-Roman').fontSize(8);
434
+
435
+ this.doc.rect(left + 438.5, topDestinatario + 143, 53.5, 23).stroke();
436
+ this.doc.fontSize(5).text('CEP', left + 442.5, topDestinatario + 148, {
437
+ characterSpacing: 0.5,
438
+ });
439
+ this.doc.fontSize(8).text(String(this.dest?.enderDest?.CEP || ''), left + 443.5, topDestinatario + 158, {
440
+ characterSpacing: 1,
441
+ });
442
+
443
+ this.doc.rect(left + 492, topDestinatario + 143, 74.3, 23).stroke();
444
+ this.doc.fontSize(5).text('DATA SAÍDA / ENTRADA', left + 496, topDestinatario + 148, {
445
+ characterSpacing: 0.5,
446
+ });
447
+ this.doc.fontSize(8).text(format(new Date(), 'dd-MM-yyyy'), left + 497, topDestinatario + 158, {
448
+ characterSpacing: 1,
449
+ });
450
+ }
451
+ const _buildDestEndereco = () => {
452
+ this.doc.rect(left, topDestinatario + 166, 246.5, 23).stroke();
453
+ this.doc.fontSize(5).text('MUNICÍPIO', left + 4, topDestinatario + 171, {
454
+ characterSpacing: 0.5,
455
+ });
456
+ this.doc.fontSize(8).text(String(this.dest?.enderDest?.xMun || ''), left + 5, topDestinatario + 181, {
457
+ characterSpacing: 1,
458
+ });
459
+
460
+ this.doc.rect(left + 246.5, topDestinatario + 166, 113, 23).stroke();
461
+ this.doc.fontSize(5).text('FONE / FAX', left + 250.5, topDestinatario + 171, {
462
+ characterSpacing: 0.5,
463
+ });
464
+ this.doc.fontSize(8).text(this.dest?.enderDest?.fone || '', left + 250.5, topDestinatario + 181, {
465
+ characterSpacing: 1,
466
+ });
467
+
468
+ this.doc.rect(left + 359.5, topDestinatario + 166, 40, 23).stroke();
469
+ this.doc.fontSize(5).text('UF', left + 363.5, topDestinatario + 171, {
470
+ characterSpacing: 0.5,
471
+ });
472
+ this.doc.fontSize(8).text(String(this.dest?.enderDest?.UF || ''), left + 363.5, topDestinatario + 181, {
473
+ characterSpacing: 1,
474
+ });
475
+
476
+ this.doc.rect(left + 399.5, topDestinatario + 166, 92.5, 23).stroke();
477
+ this.doc.fontSize(5).text('INSCRIÇÃO ESTADUAL', left + 403.5, topDestinatario + 171, {
478
+ characterSpacing: 0.5,
479
+ });
480
+ this.doc.fontSize(8).text(String(this.dest?.indIEDest || ''), left + 403.5, topDestinatario + 181, {
481
+ characterSpacing: 1,
482
+ });
483
+
484
+ this.doc.rect(left + 492, topDestinatario + 166, 74.3, 23).stroke();
485
+ this.doc.fontSize(5).text('HORA DA SAÍDA', left + 496, topDestinatario + 171, {
486
+ characterSpacing: 0.5,
487
+ });
488
+ this.doc.fontSize(8).text(format(new Date(), 'HH:mm'), left + 497, topDestinatario + 181, {
489
+ characterSpacing: 1,
490
+ });
491
+ }
492
+
493
+ this.doc.fontSize(6).font('Times-Bold').text('DESTINATÁRIO / REMETENTE', left, topDestinatario + 114, {
494
+ characterSpacing: 0.5,
495
+ });
496
+ _buildDestPessoa();
497
+ _buildDestLogradouro();
498
+ _buildDestEndereco();
499
+
500
+ }
501
+
502
+ _builCalculoImposto() {
503
+ const { top, left } = this.doc.page.margins;
504
+ this.setLineStyle(0.75, '#1c1c1c');
505
+ const topDestinatario = top + 173;
506
+
507
+ const _buildCalcImposto = () => {
508
+ /** LINHA 1 */
509
+ this.doc.rect(left, topDestinatario + 120, 86, 23).stroke();
510
+ this.doc.fontSize(5).font('Times-Roman').text('BASE DE CÁLCULO DO ICMS', left + 4, topDestinatario + 125, {
511
+ characterSpacing: 0.5,
512
+ });
513
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vBC)).toFixed(2), left - 8, topDestinatario + 135, {
514
+ characterSpacing: 1,
515
+ align: 'right',
516
+ width: 86
517
+ });
518
+ this.doc.rect(left + 86, topDestinatario + 120, 79, 23).stroke();
519
+ this.doc.fontSize(5).text('VALOR DO ICMS', left + 90, topDestinatario + 125, {
520
+ characterSpacing: 0.5,
521
+ });
522
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vICMS)).toFixed(2), left + 86 - 16, topDestinatario + 135, {
523
+ characterSpacing: 1,
524
+ align: 'right',
525
+ width: 86
526
+ });
527
+ this.doc.rect(left + 165, topDestinatario + 120, 79, 23).stroke();
528
+ this.doc.fontSize(5).text('BASE CÁLC. ICMS SUBST', left + 169, topDestinatario + 125, {
529
+ characterSpacing: 0.5,
530
+ });
531
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vBCST)).toFixed(2), left + 165 - 16, topDestinatario + 135, {
532
+ characterSpacing: 1,
533
+ align: 'right',
534
+ width: 86
535
+ });
536
+ this.doc.rect(left + 244, topDestinatario + 120, 79, 23).stroke();
537
+ this.doc.fontSize(5).text('VALOR DO ICMS SUBST.', left + 248, topDestinatario + 125, {
538
+ characterSpacing: 0.5,
539
+ });
540
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vST)).toFixed(2), left + 244 - 16, topDestinatario + 135, {
541
+ characterSpacing: 1,
542
+ align: 'right',
543
+ width: 86
544
+ });
545
+ this.doc.rect(left + 323, topDestinatario + 120, 91, 23).stroke();
546
+ this.doc.fontSize(5).text('VALOR APROX. DOS TRIBUTOS', left + 327, topDestinatario + 125, {
547
+ characterSpacing: 0.5,
548
+ });
549
+ this.doc.fontSize(8).text(parseFloat(this.total.ICMSTot.vTotTrib || '0').toFixed(2), left + 323 - 6, topDestinatario + 135, {
550
+ characterSpacing: 1,
551
+ align: 'right',
552
+ width: 86
553
+ });
554
+ this.doc.rect(left + 414, topDestinatario + 120, 152.93, 23).fillAndStroke('#DDDDDD', '#1c1c1c');
555
+ this.setLineStyle(0.75, '#1c1c1c');
556
+ this.doc.fontSize(5).text('VALOR TOTAL DOS PRODUTOS', left + 418, topDestinatario + 125, {
557
+ characterSpacing: 0.5,
558
+ });
559
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vProd)).toFixed(2), left + 480 - 8, topDestinatario + 135, {
560
+ characterSpacing: 1,
561
+ align: 'right',
562
+ width: 86
563
+ });
564
+
565
+ /** LINHA 2 */
566
+ this.doc.rect(left, topDestinatario + 143, 86, 23).stroke();
567
+ this.doc.fontSize(5).text('VALOR DO FRETE', left + 4, topDestinatario + 148, {
568
+ characterSpacing: 0.5,
569
+ });
570
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vFrete)).toFixed(2), left - 8, topDestinatario + 158, {
571
+ characterSpacing: 1,
572
+ align: 'right',
573
+ width: 86
574
+ });
575
+
576
+ this.doc.rect(left + 86, topDestinatario + 143, 79, 23).stroke();
577
+ this.doc.fontSize(5).text('VALOR DO SEGURO', left + 90, topDestinatario + 148, {
578
+ characterSpacing: 0.5,
579
+ });
580
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vSeg)).toFixed(2), left + 86 - 16, topDestinatario + 158, {
581
+ characterSpacing: 1,
582
+ align: 'right',
583
+ width: 86
584
+ });
585
+ this.doc.rect(left + 165, topDestinatario + 143, 79, 23).stroke();
586
+ this.doc.fontSize(5).text('DESCONTO', left + 169, topDestinatario + 148, {
587
+ characterSpacing: 0.5,
588
+ });
589
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vDesc)).toFixed(2), left + 165 - 16, topDestinatario + 158, {
590
+ characterSpacing: 1,
591
+ align: 'right',
592
+ width: 86
593
+ });
594
+ this.doc.rect(left + 244, topDestinatario + 143, 79, 23).stroke();
595
+ this.doc.fontSize(5).text('OUTRAS DESP. ACESS.', left + 248, topDestinatario + 148, {
596
+ characterSpacing: 0.5,
597
+ });
598
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vOutro)).toFixed(2), left + 244 - 16, topDestinatario + 158, {
599
+ characterSpacing: 1,
600
+ align: 'right',
601
+ width: 86
602
+ });
603
+ this.doc.rect(left + 323, topDestinatario + 143, 91, 23).stroke();
604
+ this.doc.fontSize(5).text('VALOR DO IPI', left + 327, topDestinatario + 148, {
605
+ characterSpacing: 0.5,
606
+ });
607
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vIPI)).toFixed(2), left + 323 - 6, topDestinatario + 158, {
608
+ characterSpacing: 1,
609
+ align: 'right',
610
+ width: 86
611
+ });
612
+ this.doc.rect(left + 414, topDestinatario + 143, 152.93, 23).fillAndStroke('#DDDDDD', '#1c1c1c');
613
+ this.setLineStyle(0.75, '#1c1c1c');
614
+ this.doc.fontSize(5).text('VALOR TOTAL DA NOTA', left + 418, topDestinatario + 148, {
615
+ characterSpacing: 0.5,
616
+ });
617
+ this.doc.fontSize(8).text(parseFloat(String(this.total.ICMSTot.vNF)).toFixed(2), left + 480 - 8, topDestinatario + 158, {
618
+ characterSpacing: 1,
619
+ align: 'right',
620
+ width: 86
621
+ });
622
+ }
623
+
624
+ this.doc.fontSize(6).font('Times-Bold').text('CÁLCULO DO IMPOSTO', left, topDestinatario + 114, {
625
+ characterSpacing: 0.5,
626
+ });
627
+ _buildCalcImposto();
628
+
629
+ }
630
+
631
+ _builTransporte() {
632
+ const { top, left } = this.doc.page.margins;
633
+ this.setLineStyle(0.75, '#1c1c1c');
634
+ const topDestinatario = top + 233;
635
+
636
+ const getModFrete = () => {
637
+ // * 0=Contratação do Frete por conta do Remetente (CIF)
638
+ // * 1=Contratação do Frete por conta do Destinatário (FOB)
639
+ // * 2=Contratação do Frete por conta de Terceiros
640
+ // * 3=Transporte Próprio por conta do Remetente
641
+ // * 4=Transporte Próprio por conta do Destinatário
642
+ // * 9=Sem Ocorrência de Transporte. (Atualizado na NT 2016/002)
643
+ const modFrete = parseInt(String(this.transp.modFrete));
644
+ switch (modFrete) {
645
+ case 0:
646
+ return `${modFrete} - REMETENTE`;
647
+ case 1:
648
+ return `${modFrete} - DESTINATÁRIO`;
649
+ case 2:
650
+ return `${modFrete} - TERCEIROS`;
651
+ case 3:
652
+ return `${modFrete} - REMETENTE`;
653
+ case 4:
654
+ return `${modFrete} - DESTINATÁRIO`;
655
+ case 9:
656
+ return `${modFrete} - SEM FRETE`;
657
+ default:
658
+ return '';
659
+ }
660
+ }
661
+
662
+ const _buildVolumeTransporte = () => {
663
+ if (this.transp.vol) {
664
+ /** Posição da primeira linha */
665
+ let topTrnasport = topDestinatario + 166;
666
+ let topTrnasportTitle = topDestinatario + 171;
667
+ let topTrnasportValue = topDestinatario + 181;
668
+
669
+ const createVolume = (vol: Vol) => {
670
+ this.doc.rect(left, topTrnasport, 68.5, 23).stroke();
671
+ this.doc.fontSize(5).text('QUANTIDADE', left + 4, topTrnasportTitle, {
672
+ characterSpacing: 0.5,
673
+ });
674
+ this.doc.fontSize(8).text(String(vol?.qVol || ''), left - 8, topTrnasportValue, {
675
+ characterSpacing: 1,
676
+ align: 'right',
677
+ width: 68.5
678
+ });
679
+ this.doc.rect(left + 68.5, topTrnasport, 100.5, 23).stroke();
680
+ this.doc.fontSize(5).text('ESPÉCIE', left + 72.5, topTrnasportTitle, {
681
+ characterSpacing: 0.5,
682
+ });
683
+ this.doc.fontSize(8).text(String(vol?.esp || ''), left + 73.5, topTrnasportValue, {
684
+ characterSpacing: 1,
685
+ });
686
+ this.doc.rect(left + 169, topTrnasport, 100.5, 23).stroke();
687
+ this.doc.fontSize(5).text('MARCA', left + 173, topTrnasportTitle, {
688
+ characterSpacing: 0.5,
689
+ });
690
+ this.doc.fontSize(8).text(String(vol?.marca || ''), left + 174, topTrnasportValue, {
691
+ characterSpacing: 1,
692
+ });
693
+ this.doc.rect(left + 269.5, topTrnasport, 100.5, 23).stroke();
694
+ this.doc.fontSize(5).text('NUMERAÇÃO', left + 273.5, topTrnasportTitle, {
695
+ characterSpacing: 0.5,
696
+ });
697
+ this.doc.fontSize(8).text(String(vol?.nVol || ''), left + 274.5, topTrnasportValue, {
698
+ characterSpacing: 1,
699
+ });
700
+ this.doc.rect(left + 370, topTrnasport, 102.6, 23).stroke();
701
+ this.doc.fontSize(5).text('PESO BRUTO', left + 374, topTrnasportTitle, {
702
+ characterSpacing: 0.5,
703
+ });
704
+ this.doc.fontSize(8).text(String(vol?.nVol || ''), left + 375, topTrnasportValue, {
705
+ characterSpacing: 1,
706
+ });
707
+ //96,43
708
+ this.doc.rect(left + 472.5, topTrnasport, 94.43, 23).stroke();
709
+ this.doc.fontSize(5).text('PESO LÍQUIDO', left + 474.5, topTrnasportTitle, {
710
+ characterSpacing: 0.5,
711
+ });
712
+ this.doc.fontSize(8).text(String(vol?.nVol || ''), left + 475.5, topTrnasportValue, {
713
+ characterSpacing: 1,
714
+ });
715
+ /** Define posição da nova linha */
716
+ topTrnasport = topTrnasport + 23;
717
+ topTrnasportTitle = topTrnasportTitle + 23;
718
+ topTrnasportValue = topTrnasportValue + 23;
719
+ }
720
+
721
+ if (this.transp.vol instanceof Array) {
722
+ for (let vol of this.transp.vol) {
723
+ createVolume(vol);
724
+ }
725
+ } else {
726
+ createVolume(this.transp.vol);
727
+ }
728
+
729
+ }
730
+
731
+ }
732
+
733
+ const _buildCalcImposto = () => {
734
+ const documento = this.documento.mascaraCnpjCpf(this.transp.transporta?.CNPJCPF || this.transp.transporta?.CNPJ || this.transp.transporta?.CPF || '');
735
+ /** LINHA 1 */
736
+ this.doc.rect(left, topDestinatario + 120, 248.5, 23).stroke();
737
+ this.doc.fontSize(5).font('Times-Roman').text('RAZÃO SOCIAL', left + 4, topDestinatario + 125, {
738
+ characterSpacing: 0.5,
739
+ });
740
+ this.doc.fontSize(8).text(this.transp.transporta?.xNome || '', left + 5, topDestinatario + 135, {
741
+ characterSpacing: 1,
742
+ width: 246
743
+ });
744
+ this.doc.rect(left + 248.5, topDestinatario + 120, 90, 23).stroke();
745
+ this.doc.fontSize(5).text('FRETE POR CONTA', left + 252.5, topDestinatario + 125, {
746
+ characterSpacing: 0.5,
747
+ });
748
+ this.doc.fontSize(8).text(getModFrete(), left + 253.5, topDestinatario + 135, {
749
+ characterSpacing: 1,
750
+ });
751
+ this.doc.rect(left + 338.5, topDestinatario + 120, 50, 23).stroke();
752
+ this.doc.fontSize(5).text('CÓDIGO ANTT', left + 342.5, topDestinatario + 125, {
753
+ characterSpacing: 0.5,
754
+ });
755
+ this.doc.fontSize(8).text(this.transp?.veicTransp?.RNTC || '', left + 343.5, topDestinatario + 135, {
756
+ characterSpacing: 1,
757
+ });
758
+ this.doc.rect(left + 388.5, topDestinatario + 120, 62, 23).stroke();
759
+ this.doc.fontSize(5).text('PLACA DO VEÍCULO', left + 391.5, topDestinatario + 125, {
760
+ characterSpacing: 0.5,
761
+ });
762
+ this.doc.fontSize(8).text(this.transp?.veicTransp?.placa || '', left + 393.5, topDestinatario + 135, {
763
+ characterSpacing: 1,
764
+ });
765
+ this.doc.rect(left + 450.5, topDestinatario + 120, 22, 23).stroke();
766
+ this.doc.fontSize(5).text('UF', left + 454.5, topDestinatario + 125, {
767
+ characterSpacing: 0.5,
768
+ });
769
+ this.doc.fontSize(8).text(this.transp?.transporta?.UF || '', left + 455.5, topDestinatario + 135, {
770
+ characterSpacing: 1,
771
+ });
772
+ this.doc.rect(left + 472.5, topDestinatario + 120, 94.43, 23).stroke();
773
+ this.doc.fontSize(5).text('CNPJ / CPF', left + 476.5, topDestinatario + 125, {
774
+ characterSpacing: 0.5,
775
+ });
776
+ this.doc.fontSize(8).text(documento, left + 477.5, topDestinatario + 135, {
777
+ characterSpacing: 1,
778
+ });
779
+
780
+ /** LINHA 2 */
781
+ this.doc.rect(left, topDestinatario + 143, 338.5, 23).stroke();
782
+ this.doc.fontSize(5).text('ENDEREÇO', left + 4, topDestinatario + 148, {
783
+ characterSpacing: 0.5,
784
+ });
785
+ this.doc.fontSize(8).text(this.transp.transporta?.xEnder || '', left + 4, topDestinatario + 158, {
786
+ characterSpacing: 1,
787
+ });
788
+ this.doc.rect(left + 338.5, topDestinatario + 143, 112, 23).stroke();
789
+ this.doc.fontSize(5).text('MUNICÍPIO', left + 342.5, topDestinatario + 148, {
790
+ characterSpacing: 0.5,
791
+ });
792
+ this.doc.fontSize(8).text(this.transp.transporta?.xMun || '', left + 343.5, topDestinatario + 158, {
793
+ characterSpacing: 1,
794
+ });
795
+ this.doc.rect(left + 450.5, topDestinatario + 143, 22, 23).stroke();
796
+ this.doc.fontSize(5).text('UF', left + 454.5, topDestinatario + 148, {
797
+ characterSpacing: 0.5,
798
+ });
799
+ this.doc.fontSize(8).text(this.transp?.transporta?.UF || '', left + 455.5, topDestinatario + 158, {
800
+ characterSpacing: 1,
801
+ });
802
+ this.doc.rect(left + 472.5, topDestinatario + 143, 94.43, 23).stroke();
803
+ this.doc.fontSize(5).text('INSCRIÇÃO ESTADUAL', left + 476.5, topDestinatario + 148, {
804
+ characterSpacing: 0.5,
805
+ });
806
+ this.doc.fontSize(8).text(this.transp?.transporta?.IE || '', left + 476.5, topDestinatario + 158, {
807
+ characterSpacing: 1,
808
+ });
809
+ }
810
+
811
+ this.doc.fontSize(6).font('Times-Bold').text('TRANSPORTADOR / VOLUMES TRANSPORTADOS', left, topDestinatario + 114, {
812
+ characterSpacing: 0.5,
813
+ });
814
+ _buildCalcImposto();
815
+ _buildVolumeTransporte();
816
+
817
+ }
818
+
819
+ _buildProdutos() {
820
+ const { left } = this.doc.page.margins;
821
+ this.setLineStyle(0.75, '#1c1c1c');
822
+
823
+ this.doc.fontSize(6).font('Times-Bold').fillColor('black').text('DADOS DO PRODUTO / SERVIÇOS', left, 452, {
824
+ characterSpacing: 0.5,
825
+ });
826
+
827
+ const tableTop = 458;
828
+ const defaultItemHeight = 15;
829
+ let y = tableTop;
830
+ let currentPage = 0;
831
+
832
+ const header = (top: number) => {
833
+ this.doc.rect(left, top, 40, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
834
+ this.setLineStyle(0.75, '#1c1c1c');
835
+ this.doc.font('Times-Roman').fontSize(5.7).text('CÓDIGO DO PROD. / SERV', left, top + 2.8, {
836
+ width: 40,
837
+ align: 'center',
838
+ });
839
+ this.doc.rect(left + 40, top, 138, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
840
+ this.setLineStyle(0.75, '#1c1c1c');
841
+ this.doc.text('DESCRIÇÃO DO PRODUTO / SERVIÇO', left + 40, top + 5.9, {
842
+ width: 138,
843
+ align: 'center'
844
+ });
845
+ this.doc.rect(left + 178, top, 30, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
846
+ this.setLineStyle(0.75, '#1c1c1c');
847
+ this.doc.text('NCM / SH', left + 178, top + 5.9, {
848
+ width: 30,
849
+ align: 'center'
850
+ });
851
+ this.doc.rect(left + 208, top, 17, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
852
+ this.setLineStyle(0.75, '#1c1c1c');
853
+ this.doc.text('CST', left + 208, top + 5.9, {
854
+ width: 17,
855
+ align: 'center'
856
+ });
857
+ this.doc.rect(left + 225, top, 28, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
858
+ this.setLineStyle(0.75, '#1c1c1c');
859
+ this.doc.text('CFOP', left + 225, top + 5.9, {
860
+ width: 28,
861
+ align: 'center'
862
+ });
863
+ this.doc.rect(left + 253, top, 23, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
864
+ this.setLineStyle(0.75, '#1c1c1c');
865
+ this.doc.text('UNID.', left + 253, top + 5.9, {
866
+ width: 23,
867
+ align: 'center'
868
+ });
869
+ this.doc.rect(left + 276, top, 25, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
870
+ this.setLineStyle(0.75, '#1c1c1c');
871
+ this.doc.text('QUANT.', left + 276, top + 5.9, {
872
+ width: 25,
873
+ align: 'center'
874
+ });
875
+ this.doc.rect(left + 301, top, 37.23, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
876
+ this.setLineStyle(0.75, '#1c1c1c');
877
+ this.doc.text('VALOR UNITÁRIO', left + 301, top + 2.9, {
878
+ width: 37.23,
879
+ align: 'center'
880
+ });
881
+ this.doc.rect(left + 338.23, top, 39.23, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
882
+ this.setLineStyle(0.75, '#1c1c1c');
883
+ this.doc.text('VALOR DESCONTO', left + 338.23, top + 2.9, {
884
+ width: 39.23,
885
+ align: 'center'
886
+ });
887
+ this.doc.rect(left + 377.46, top, 33.23, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
888
+ this.setLineStyle(0.75, '#1c1c1c');
889
+ this.doc.text('VALOR LIQUIDO', left + 377.46, top + 2.9, {
890
+ width: 33.23,
891
+ align: 'center'
892
+ });
893
+ this.doc.rect(left + 410.69, top, 38, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
894
+ this.setLineStyle(0.75, '#1c1c1c');
895
+ this.doc.text('BASE CÁLC. ICMS', left + 413.8, top + 2.9, {
896
+ width: 32,
897
+ align: 'center'
898
+ });
899
+ this.doc.rect(left + 448.69, top, 35, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
900
+ this.setLineStyle(0.75, '#1c1c1c');
901
+ this.doc.text('VALOR I.C.M.S.', left + 448.69, top + 2.9, {
902
+ width: 35,
903
+ align: 'center'
904
+ });
905
+ this.doc.rect(left + 483.69, top, 38, defaultItemHeight).fillAndStroke('#DDDDDD', '#1c1c1c');
906
+ this.setLineStyle(0.75, '#1c1c1c');
907
+ this.doc.text('VALOR I.P.I.', left + 490.69, top + 2.9, {
908
+ width: 25,
909
+ align: 'center'
910
+ });
911
+ this.doc.rect(left + 521.69, top, 45.2, 7.5).fillAndStroke('#DDDDDD', '#1c1c1c');
912
+ this.setLineStyle(0.75, '#1c1c1c');
913
+ this.doc.text('ALÍQUOTAS', left + 521.69, top + 2, {
914
+ width: 45,
915
+ align: 'center'
916
+ });
917
+ this.doc.rect(left + 521.69, top + 7.5, 22.5, 7.5).fillAndStroke('#DDDDDD', '#1c1c1c');
918
+ this.setLineStyle(0.75, '#1c1c1c');
919
+ this.doc.text('ICMS', left + 521.69, top + 9.5, {
920
+ width: 22.5,
921
+ align: 'center'
922
+ });
923
+ this.doc.rect(left + 544.19, top + 7.5, 22.75, 7.5).fillAndStroke('#DDDDDD', '#1c1c1c');
924
+ this.setLineStyle(0.75, '#1c1c1c');
925
+ this.doc.text('IPI', left + 544.19, top + 9.5, {
926
+ width: 22.5,
927
+ align: 'center'
928
+ });
929
+ };
930
+
931
+ const row = (top: number, item: DetProd) => {
932
+ function getCST(ICMS: ICMS): string {
933
+ const chavesICMS: (keyof ICMS)[] = Object.keys(ICMS) as (keyof ICMS)[];
934
+
935
+ const listaIcmsSemCST = [
936
+ 'ICMSSN101',
937
+ 'ICMSSN102',
938
+ 'ICMSSN201',
939
+ 'ICMSSN202',
940
+ 'ICMSSN500',
941
+ 'ICMSSN900'
942
+ ];
943
+
944
+ const icmsSemCST = listaIcmsSemCST.includes(chavesICMS[0]);
945
+
946
+ let CST = '';
947
+ if (chavesICMS.length > 0) {
948
+ const tipoICMS = chavesICMS[0];
949
+ if (!icmsSemCST) {
950
+ CST = (ICMS[tipoICMS] as any).CST;
951
+ }
952
+ }
953
+ return CST;
954
+ }
955
+ function getValoresItem(ICMS: ICMS): {
956
+ vBC: string,
957
+ vICMS: string,
958
+ pICMS: string
959
+ } {
960
+ const chavesICMS: (keyof ICMS)[] = Object.keys(ICMS) as (keyof ICMS)[];
961
+
962
+ const listaIcmsSemvBC = [
963
+ 'ICMS02',
964
+ 'ICMS15',
965
+ 'ICMS30',
966
+ 'ICMS40',
967
+ 'ICMS53',
968
+ 'ICMS60',
969
+ 'ICMS61',
970
+ 'ICMSST',
971
+ 'ICMSSN101',
972
+ 'ICMSSN102',
973
+ 'ICMSSN201',
974
+ 'ICMSSN202',
975
+ 'ICMSSN500'
976
+ ];
977
+ const icmsSemvBC = listaIcmsSemvBC.includes(chavesICMS[0]);
978
+
979
+ let vBC = '0,00';
980
+ let vICMS = '0,00';
981
+ let pICMS = '0,00';
982
+ if (chavesICMS.length > 0) {
983
+ const tipoICMS = chavesICMS[0];
984
+ if (!icmsSemvBC) {
985
+ vBC = (ICMS[tipoICMS] as any).vBC;
986
+ vICMS = (ICMS[tipoICMS] as any).vICMS
987
+ pICMS = (ICMS[tipoICMS] as any).pICMS
988
+ return {
989
+ vBC: parseFloat(vBC).toFixed(2),
990
+ vICMS: parseFloat(vBC).toFixed(2),
991
+ pICMS: parseFloat(pICMS).toFixed(2)
992
+ };
993
+ }
994
+ }
995
+ return { vBC, vICMS, pICMS };
996
+ }
997
+ function getValoresIPI(IPI: IPI | undefined): { vIPI: string, pIPI: string } {
998
+ if (!IPI) {
999
+ return {
1000
+ vIPI: '0,00',
1001
+ pIPI: '0,00'
1002
+ }
1003
+ }
1004
+
1005
+ let vIPI = parseFloat(String(IPI.IPITrib.vIPI)).toFixed(2);
1006
+ let pIPI = parseFloat(String(IPI.IPITrib.pIPI)).toFixed(2);
1007
+ return { vIPI, pIPI }
1008
+ }
1009
+ const CST = getCST(item.imposto.ICMS as ICMS);
1010
+ const { vIPI, pIPI } = getValoresIPI(item.imposto.IPI);
1011
+ const { vBC, vICMS, pICMS } = getValoresItem(item.imposto.ICMS as ICMS);
1012
+
1013
+ const text = item.infAdProd ? `${item.prod.xProd || ''}\n${item.infAdProd}` : item.prod.xProd || '';
1014
+ const textHeight = this.doc.heightOfString(text, {
1015
+ width: 138,
1016
+ align: 'center'
1017
+ });
1018
+
1019
+ const itemHeight = Math.max(defaultItemHeight, textHeight + 10); // 10 is padding
1020
+
1021
+ this.setLineStyle(0.75, '#1c1c1c');
1022
+ this.doc.rect(left, top, 40, itemHeight).stroke();
1023
+ this.doc.font('Times-Roman').fontSize(6).text(item.prod.cProd || '', left + 2, top + 3, {
1024
+ width: 40,
1025
+ });
1026
+
1027
+ this.doc.rect(left + 40, top, 138, itemHeight).stroke();
1028
+ this.doc.text(text, left + 43, top + 4, {
1029
+ width: 137,
1030
+ });
1031
+
1032
+ this.doc.rect(left + 178, top, 30, itemHeight).stroke();
1033
+ this.doc.text(String(item.prod.NCM) || '', left + 178, top + 5.9, {
1034
+ width: 30,
1035
+ align: 'center'
1036
+ });
1037
+
1038
+ this.doc.rect(left + 208, top, 17, itemHeight).stroke();
1039
+ this.doc.text(CST, left + 208, top + 5.9, {
1040
+ width: 17,
1041
+ align: 'center'
1042
+ });
1043
+
1044
+ this.doc.rect(left + 225, top, 28, itemHeight).stroke();
1045
+ this.doc.text(String(item.prod.CFOP || ''), left + 225, top + 5.9, {
1046
+ width: 28,
1047
+ align: 'center'
1048
+ });
1049
+
1050
+ this.doc.rect(left + 253, top, 23, itemHeight).stroke();
1051
+ this.doc.text(item.prod.uCom || item.prod.uTrib || '', left + 256, top + 5.9, {
1052
+ width: 23,
1053
+ });
1054
+
1055
+ const quant = parseFloat(String(item.prod.qCom || item.prod.qTrib)).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 3 });
1056
+ this.doc.rect(left + 276, top, 25, itemHeight).stroke();
1057
+ this.doc.text(String(quant || ''), left + 274.5, top + 5.9, {
1058
+ width: 25,
1059
+ align: 'right'
1060
+ });
1061
+
1062
+ const valUnit = parseFloat(String(item.prod.vUnCom || item.prod.vUnTrib || '0')).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
1063
+ this.doc.rect(left + 301, top, 37.23, itemHeight).stroke();
1064
+ this.doc.text(valUnit, left + 299.5, top + 5.9, {
1065
+ width: 37.23,
1066
+ align: 'right'
1067
+ });
1068
+
1069
+ const valDesc = parseFloat(String(item.prod.vDesc || '0')).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
1070
+ this.doc.rect(left + 338.23, top, 39.23, itemHeight).stroke();
1071
+ this.doc.text(valDesc, left + 336.73, top + 5.9, {
1072
+ width: 39.23,
1073
+ align: 'right'
1074
+ });
1075
+
1076
+ const valLiq = parseFloat(String(item.prod.vProd || '0')).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
1077
+ this.doc.rect(left + 377.46, top, 33.23, itemHeight).stroke();
1078
+ this.doc.text(valLiq, left + 375.96, top + 5.9, {
1079
+ width: 33.23,
1080
+ align: 'right'
1081
+ });
1082
+
1083
+ this.doc.rect(left + 410.69, top, 38, itemHeight).stroke();
1084
+ this.doc.text(vBC, left + 409.19, top + 5.9, {
1085
+ width: 38,
1086
+ align: 'right'
1087
+ });
1088
+
1089
+ this.doc.rect(left + 448.69, top, 35, itemHeight).stroke();
1090
+ this.doc.text(vICMS, left + 447.19, top + 5.9, {
1091
+ width: 35,
1092
+ align: 'right'
1093
+ });
1094
+
1095
+ this.doc.rect(left + 483.69, top, 38, itemHeight).stroke();
1096
+ this.doc.text(vIPI, left + 482.19, top + 5.9, {
1097
+ width: 38,
1098
+ align: 'right'
1099
+ });
1100
+
1101
+ this.doc.rect(left + 521.69, top, 22.5, itemHeight).stroke();
1102
+ this.doc.text(pICMS, left + 520.19, top + 5.9, {
1103
+ width: 22.5,
1104
+ align: 'right'
1105
+ });
1106
+
1107
+ this.doc.rect(left + 544.19, top, 22.75, itemHeight).stroke();
1108
+ this.doc.text(pIPI, left + 542.69, top + 5.9, {
1109
+ width: 22.5,
1110
+ align: 'right'
1111
+ });
1112
+
1113
+ return itemHeight;
1114
+ };
1115
+
1116
+ header(458);
1117
+
1118
+ const createTable = (prod: DetProd) => {
1119
+ if (y + defaultItemHeight > this.doc.page.height - 160) {
1120
+ this.doc.addPage();
1121
+ currentPage++;
1122
+ if (currentPage > 0) {
1123
+ y = 185;
1124
+ this.doc.fontSize(6).font('Times-Bold').fillColor('black').text('DADOS DO PRODUTO / SERVIÇOS', left, 175, {
1125
+ characterSpacing: 0.5,
1126
+ });
1127
+ header(185);
1128
+ }
1129
+ }
1130
+ const itemHeight = row(y + 15, prod);
1131
+ y += itemHeight;
1132
+ }
1133
+
1134
+ // Adicionando itens da tabela
1135
+ if (this.det instanceof Array) {
1136
+ for (let i = 0; i < this.det.length; i++) {
1137
+ const prod = this.det[i];
1138
+ createTable(prod);
1139
+ }
1140
+ } else {
1141
+ createTable(this.det);
1142
+ }
1143
+ }
1144
+
1145
+ _buildFooter() {
1146
+ const { left } = this.doc.page.margins;
1147
+
1148
+ this.setLineStyle(0.75, '#1c1c1c');
1149
+ const topDestinatario = 820.45 - 88.5;
1150
+
1151
+ this.doc.fontSize(6).font('Times-Bold').text('DADOS ADICIONAIS', left, topDestinatario - 5, {
1152
+ characterSpacing: 0.5,
1153
+ });
1154
+ this.doc.rect(left, topDestinatario, 408, 95).stroke();
1155
+ this.doc.fontSize(5).font('Times-Roman').text('INFORMAÇÕES COMPLEMENTARES', 10, topDestinatario + 4.5, {
1156
+ characterSpacing: 0.5
1157
+ });
1158
+ this.doc.fontSize(8).text(this.infAdic?.infCpl || '', 13, topDestinatario + 13, {
1159
+ characterSpacing: 1,
1160
+ width: 400,
1161
+ });
1162
+ this.doc.rect(left + 408, topDestinatario, 158.93, 95).stroke();
1163
+ this.doc.fontSize(5).text('RESERVADO AO FISCO', 408 + 10, topDestinatario + 4.5, {
1164
+ characterSpacing: 0.5
1165
+ });
1166
+ this.doc.fontSize(8).text(String(this.infAdic?.infAdFisco || ''), 13, topDestinatario + 13, {
1167
+ characterSpacing: 1,
1168
+ width: 400,
1169
+ });
1170
+
1171
+ if (this.exibirMarcaDaguaDanfe) {
1172
+ const topPosition = Number(Number(this.ide.tpAmb)) !== 2 ? topDestinatario + 38 : topDestinatario + 58;
1173
+ const leftPosition = Number(Number(this.ide.tpAmb)) !== 2 ? left + 150 : left + 100;
1174
+ this.doc.fontSize(26).font('Times-Bold').fillColor('#c7c7c7').text('Treeunfe DFe', leftPosition, topPosition, {
1175
+ characterSpacing: 0.5,
1176
+ });
1177
+ }
1178
+
1179
+ if (Number(Number(this.ide.tpAmb)) === 2) {
1180
+ this.doc.fontSize(14).font('Times-Bold').fillColor('grey').text('AMBIENTE DE HOMOLOGAÇÃO - NF-E SEM VALOR FISCAL', left + 100, topDestinatario + 45, {
1181
+ characterSpacing: 1
1182
+ });
1183
+ }
1184
+ // .rotate(45, { origin: [0, 0] })
1185
+ }
1186
+
1187
+ async generatePDF(exibirMarcaDaguaDanfe?: boolean) {
1188
+ try {
1189
+ this.exibirMarcaDaguaDanfe = exibirMarcaDaguaDanfe ?? true
1190
+ const chave = this.chave
1191
+
1192
+ await this.generateBarcode(chave);
1193
+
1194
+ this.doc.pipe(fs.createWriteStream(this.outputPath));
1195
+
1196
+ this.drawHeader(true);
1197
+
1198
+ this._buildDestinatario();
1199
+ this._builCalculoImposto();
1200
+ this._builTransporte();
1201
+
1202
+ this.doc.on('pageAdded', () => {
1203
+ this.drawHeader(false);
1204
+ });
1205
+ this.drawFooter();
1206
+ this.doc.on('pageAdded', () => {
1207
+ this.drawFooter();
1208
+ });
1209
+
1210
+ this._buildProdutos();
1211
+ this.doc.end();
1212
+
1213
+ return {
1214
+ message: ` DANFE Gerada em '${this.outputPath}'`,
1215
+ success: true,
1216
+ };
1217
+ } catch (error: any) {
1218
+ throw new Error(`Erro ao gerar DANFE: ${error.message}`);
1219
+ }
1220
+ }
1221
+ }
1222
+
1223
+ export default NFEGerarDanfe;