einvoice-cli 1.0.0
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/README.md +139 -0
- package/bin/cli.js +52 -0
- package/index.js +23 -0
- package/lib/BaseInvoiceService.js +158 -0
- package/lib/ErrorHandler.js +98 -0
- package/lib/Invoice.js +108 -0
- package/lib/InvoiceValidator.js +422 -0
- package/lib/OfdInvoiceExtractor.js +170 -0
- package/lib/PDFTextPositionAnalyzer.js +366 -0
- package/lib/PdfFinancialInvoiceService.js +134 -0
- package/lib/PdfFullElectronicInvoiceService.js +325 -0
- package/lib/PdfInvoiceExtractor.js +124 -0
- package/lib/PdfRegularInvoiceService.js +786 -0
- package/lib/RegexPatterns.js +202 -0
- package/lib/StringUtils.js +70 -0
- package/lib/extractor.js +24 -0
- package/package.json +31 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
const { Invoice, Detail } = require('./Invoice');
|
|
2
|
+
const RegexPatterns = require('./RegexPatterns');
|
|
3
|
+
const InvoiceValidator = require('./InvoiceValidator');
|
|
4
|
+
const BaseInvoiceService = require('./BaseInvoiceService');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* PDF 全电发票服务 - 优化版
|
|
8
|
+
* 整合:正则表达式优化 + 字段验证
|
|
9
|
+
*/
|
|
10
|
+
class PdfFullElectronicInvoiceService {
|
|
11
|
+
static extract(fullText, allText, pageWidth, items) {
|
|
12
|
+
return BaseInvoiceService.safeExtract(() => {
|
|
13
|
+
const invoice = new Invoice();
|
|
14
|
+
|
|
15
|
+
this.extractBasicFields(invoice, allText);
|
|
16
|
+
this.extractAmountInfo(invoice, allText, fullText);
|
|
17
|
+
BaseInvoiceService.extractPersonInfo(invoice, allText);
|
|
18
|
+
this.extractDetails(invoice, fullText, allText, items);
|
|
19
|
+
|
|
20
|
+
BaseInvoiceService.validateInvoice(invoice);
|
|
21
|
+
|
|
22
|
+
return invoice;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static extractBasicFields(invoice, allText) {
|
|
27
|
+
// 使用基类的通用方法
|
|
28
|
+
BaseInvoiceService.extractBasicFields(invoice, allText);
|
|
29
|
+
|
|
30
|
+
// 全电发票特定的字段提取
|
|
31
|
+
const electronicsNumberMatch = allText.match(RegexPatterns.UTILITY_PATTERNS.electronicsNumber);
|
|
32
|
+
if (electronicsNumberMatch) {
|
|
33
|
+
invoice.number = electronicsNumberMatch[1];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 提取日期(使用基类方法)
|
|
37
|
+
const date = BaseInvoiceService.extractDate(allText);
|
|
38
|
+
if (date) {
|
|
39
|
+
invoice.date = date;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 提取购销方名称(使用基类方法)
|
|
43
|
+
const partyNames = BaseInvoiceService.extractPartyNames(allText);
|
|
44
|
+
if (partyNames.buyerName) {
|
|
45
|
+
invoice.buyerName = partyNames.buyerName;
|
|
46
|
+
}
|
|
47
|
+
if (partyNames.sellerName) {
|
|
48
|
+
invoice.sellerName = partyNames.sellerName;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 提取纳税人识别号(使用基类方法)
|
|
52
|
+
const taxIds = BaseInvoiceService.extractTaxIds(allText);
|
|
53
|
+
if (taxIds.length > 0) {
|
|
54
|
+
invoice.buyerCode = taxIds[0];
|
|
55
|
+
}
|
|
56
|
+
if (taxIds.length > 1) {
|
|
57
|
+
invoice.sellerCode = taxIds[1];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.detectInvoiceType(invoice, allText);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static detectInvoiceType(invoice, allText) {
|
|
64
|
+
// 优先匹配完整的发票类型(注意:allText是规范化后的文本,括号可能被替换)
|
|
65
|
+
const fullMatch = allText.match(/电子发票\s*(增值税专用发票)/) ||
|
|
66
|
+
allText.match(/电子发票(增值税专用发票)/) ||
|
|
67
|
+
allText.match(/电子发票\s*\(增值税专用发票\)/) ||
|
|
68
|
+
allText.match(/电子发票\(增值税专用发票\)/) ||
|
|
69
|
+
allText.match(/电子发票\s*(普通发票)/) ||
|
|
70
|
+
allText.match(/电子发票(普通发票)/) ||
|
|
71
|
+
allText.match(/电子发票\s*\(普通发票\)/) ||
|
|
72
|
+
allText.match(/电子发票\(普通发票\)/);
|
|
73
|
+
|
|
74
|
+
if (fullMatch) {
|
|
75
|
+
// 提取完整的发票类型
|
|
76
|
+
const matchText = fullMatch[0];
|
|
77
|
+
if (matchText.includes('增值税专用发票')) {
|
|
78
|
+
invoice.title = '电子发票(增值税专用发票)';
|
|
79
|
+
invoice.type = '专用发票';
|
|
80
|
+
} else if (matchText.includes('普通发票')) {
|
|
81
|
+
invoice.title = '电子发票(普通发票)';
|
|
82
|
+
invoice.type = '普通发票';
|
|
83
|
+
}
|
|
84
|
+
} else if (allText.includes('电子发票')) {
|
|
85
|
+
invoice.title = '电子发票';
|
|
86
|
+
invoice.type = '电子发票';
|
|
87
|
+
} else if (allText.includes('普通发票')) {
|
|
88
|
+
invoice.title = '普通发票';
|
|
89
|
+
invoice.type = '普通发票';
|
|
90
|
+
} else if (allText.includes('增值税专用发票')) {
|
|
91
|
+
invoice.title = '增值税专用发票';
|
|
92
|
+
invoice.type = '专用发票';
|
|
93
|
+
} else if (allText.includes('通行费')) {
|
|
94
|
+
invoice.title = '通行费发票';
|
|
95
|
+
invoice.type = '通行费';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static extractAmountInfo(invoice, allText, fullText) {
|
|
100
|
+
// 首先尝试从"合计"行提取金额和税额
|
|
101
|
+
const amountMatch = fullText.match(/合\s*计\s*¥?\s*(\d+\.\d+)\s+¥?\s*(\d+\.\d+)/);
|
|
102
|
+
if (amountMatch) {
|
|
103
|
+
invoice.amount = amountMatch[1];
|
|
104
|
+
invoice.taxAmount = amountMatch[2];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 如果没找到,尝试其他模式
|
|
108
|
+
if (!invoice.amount) {
|
|
109
|
+
const amountMatch2 = RegexPatterns.tryPatterns(allText, [
|
|
110
|
+
RegexPatterns.AMOUNT_FIELDS.amount,
|
|
111
|
+
RegexPatterns.AMOUNT_FIELDS.amountWithSpace,
|
|
112
|
+
]);
|
|
113
|
+
if (amountMatch2) {
|
|
114
|
+
const parsed = this.parseAmountString(amountMatch2.match[0]);
|
|
115
|
+
if (parsed.amount) {
|
|
116
|
+
invoice.amount = parsed.amount;
|
|
117
|
+
}
|
|
118
|
+
if (parsed.taxAmount) {
|
|
119
|
+
invoice.taxAmount = parsed.taxAmount;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 提取价税合计
|
|
125
|
+
const totalMatch = allText.match(
|
|
126
|
+
/价税合计\s*\(?大写\)?\s*([壹贰叁肆伍陆柒捌玖拾佰仟万亿圆整]+)\s*\(?小写\)?\s*¥?\s*(\d+\.\d+)\s*(?=[合计]|$)/
|
|
127
|
+
);
|
|
128
|
+
if (totalMatch) {
|
|
129
|
+
invoice.totalAmountString = totalMatch[1];
|
|
130
|
+
invoice.totalAmount = totalMatch[2];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 如果还没找到价税合计,尝试更宽松的模式
|
|
134
|
+
if (!invoice.totalAmount) {
|
|
135
|
+
const totalMatch2 = fullText.match(/价税合计.*?¥?\s*(\d+\.\d+)/);
|
|
136
|
+
if (totalMatch2) {
|
|
137
|
+
invoice.totalAmount = totalMatch2[1];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 如果还没找到大写金额,尝试提取
|
|
142
|
+
if (!invoice.totalAmountString) {
|
|
143
|
+
const chineseMatch = allText.match(/价税合计.*?([壹贰叁肆伍陆柒捌玖拾佰仟万亿圆整]+)/);
|
|
144
|
+
if (chineseMatch) {
|
|
145
|
+
invoice.totalAmountString = chineseMatch[1];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 验证金额一致性
|
|
150
|
+
if (invoice.amount && invoice.taxAmount && invoice.totalAmount) {
|
|
151
|
+
const amount = parseFloat(invoice.amount);
|
|
152
|
+
const taxAmount = parseFloat(invoice.taxAmount);
|
|
153
|
+
const totalAmount = parseFloat(invoice.totalAmount);
|
|
154
|
+
|
|
155
|
+
if (!isNaN(amount) && !isNaN(taxAmount) && !isNaN(totalAmount)) {
|
|
156
|
+
const diff = Math.abs(totalAmount - (amount + taxAmount));
|
|
157
|
+
if (diff > 0.01) {
|
|
158
|
+
invoice.amountWarning = `金额不匹配: ${amount} + ${taxAmount} ≠ ${totalAmount}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static parseAmountString(amountStr) {
|
|
165
|
+
const parts = amountStr.match(/(\d+\.?\d*)/g) || [];
|
|
166
|
+
return {
|
|
167
|
+
amount: parts[0] || '',
|
|
168
|
+
taxAmount: parts[1] || '',
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
static extractDetails(invoice, fullText, allText, items) {
|
|
174
|
+
const details = [];
|
|
175
|
+
const lines = fullText.split('\n');
|
|
176
|
+
|
|
177
|
+
let taxRateLineIdx = -1;
|
|
178
|
+
let mergeLineIdx = -1;
|
|
179
|
+
|
|
180
|
+
for (let i = 0; i < lines.length; i++) {
|
|
181
|
+
const line = lines[i];
|
|
182
|
+
if (line.includes('税率') || line.includes('合计') || line.includes('销售额')) {
|
|
183
|
+
if (line.includes('税率')) {
|
|
184
|
+
taxRateLineIdx = i;
|
|
185
|
+
}
|
|
186
|
+
if (line.includes('合计') && i > taxRateLineIdx) {
|
|
187
|
+
mergeLineIdx = i;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (taxRateLineIdx > 0 && mergeLineIdx > taxRateLineIdx) {
|
|
194
|
+
for (let i = taxRateLineIdx + 1; i < mergeLineIdx; i++) {
|
|
195
|
+
const line = lines[i].trim();
|
|
196
|
+
if (!line || this.isLineIgnorable(line)) continue;
|
|
197
|
+
|
|
198
|
+
if (this.isDetailLine(line)) {
|
|
199
|
+
const detail = this.parseDetailLine(line);
|
|
200
|
+
if (detail && detail.amount) {
|
|
201
|
+
details.push(detail);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
invoice.details = details;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static isLineIgnorable(line) {
|
|
211
|
+
return (
|
|
212
|
+
line.length < 2 ||
|
|
213
|
+
/^[-\s=*]*$/.test(line) ||
|
|
214
|
+
/^\s*$/.test(line) ||
|
|
215
|
+
line.startsWith('商品')
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
static isDetailLine(line) {
|
|
220
|
+
// 排除包含"合计"的行
|
|
221
|
+
if (line.includes('合计') || line.includes('合 计')) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return /\d+%/.test(line) || /免税|不征税|出口零税/.test(line) || /\d+\.\d+/.test(line);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
static parseDetailLine(line) {
|
|
228
|
+
const detail = new Detail();
|
|
229
|
+
|
|
230
|
+
line = line.replace(/ /g, ' ').replace(/\s+/g, ' ').trim();
|
|
231
|
+
const tokens = line.split(/\s+/);
|
|
232
|
+
|
|
233
|
+
if (tokens.length < 2) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 特殊处理:如果行以*开头,可能是项目名称包含*
|
|
238
|
+
if (tokens[0] === '*' && tokens.length >= 4) {
|
|
239
|
+
// 尝试重建项目名称
|
|
240
|
+
let nameParts = [];
|
|
241
|
+
let i = 0;
|
|
242
|
+
|
|
243
|
+
// 收集直到遇到数字的部分作为项目名称
|
|
244
|
+
while (i < tokens.length && !/^\d+(\.\d+)?$/.test(tokens[i])) {
|
|
245
|
+
nameParts.push(tokens[i]);
|
|
246
|
+
i++;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (nameParts.length > 0) {
|
|
250
|
+
detail.name = nameParts.join('');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 剩下的tokens从i开始是数字部分
|
|
254
|
+
const numberTokens = tokens.slice(i);
|
|
255
|
+
|
|
256
|
+
// 查找税率位置
|
|
257
|
+
const taxRateIdx = numberTokens.findIndex((t) => /\d+%|免税|不征税/.test(t));
|
|
258
|
+
|
|
259
|
+
// 金额应该是税率前面的一个数字
|
|
260
|
+
if (taxRateIdx > 0) {
|
|
261
|
+
detail.amount = numberTokens[taxRateIdx - 1];
|
|
262
|
+
|
|
263
|
+
// 税率
|
|
264
|
+
if (taxRateIdx >= 0) {
|
|
265
|
+
const taxRateStr = numberTokens[taxRateIdx];
|
|
266
|
+
detail.taxRate = RegexPatterns.extractTaxRate(taxRateStr);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 税额是税率后面的数字
|
|
270
|
+
if (taxRateIdx + 1 < numberTokens.length) {
|
|
271
|
+
const nextToken = numberTokens[taxRateIdx + 1];
|
|
272
|
+
if (/^\d+(\.\d+)?$/.test(nextToken)) {
|
|
273
|
+
detail.taxAmount = nextToken;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return Object.keys(detail).length > 1 ? detail : null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 原来的逻辑(用于非*开头的行)
|
|
282
|
+
const amountIdx = tokens.findIndex((t) => /^\d+(\.\d+)?$/.test(t));
|
|
283
|
+
const taxRateIdx = tokens.findIndex((t) => /\d+%|免税|不征税/.test(t));
|
|
284
|
+
|
|
285
|
+
if (amountIdx >= 0) {
|
|
286
|
+
detail.amount = tokens[amountIdx];
|
|
287
|
+
|
|
288
|
+
let numCount = 0;
|
|
289
|
+
for (let i = 0; i < amountIdx; i++) {
|
|
290
|
+
if (/^-?\d+(\.\d+)?$/.test(tokens[i])) {
|
|
291
|
+
if (numCount === 0) {
|
|
292
|
+
detail.count = tokens[i];
|
|
293
|
+
} else if (numCount === 1) {
|
|
294
|
+
detail.price = tokens[i];
|
|
295
|
+
}
|
|
296
|
+
numCount++;
|
|
297
|
+
} else {
|
|
298
|
+
if (!detail.name) {
|
|
299
|
+
detail.name = tokens[i];
|
|
300
|
+
} else if (!detail.model) {
|
|
301
|
+
detail.model = tokens[i];
|
|
302
|
+
} else if (!detail.unit) {
|
|
303
|
+
detail.unit = tokens[i];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (taxRateIdx >= 0) {
|
|
309
|
+
const taxRateStr = tokens[taxRateIdx];
|
|
310
|
+
detail.taxRate = RegexPatterns.extractTaxRate(taxRateStr);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (taxRateIdx >= 0 && taxRateIdx + 1 < tokens.length) {
|
|
314
|
+
const nextToken = tokens[taxRateIdx + 1];
|
|
315
|
+
if (/^\d+(\.\d+)?$/.test(nextToken)) {
|
|
316
|
+
detail.taxAmount = nextToken;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return Object.keys(detail).length > 1 ? detail : null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
module.exports = PdfFullElectronicInvoiceService;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const pdfjs = require('pdfjs-dist/build/pdf');
|
|
3
|
+
const { Invoice } = require('./Invoice');
|
|
4
|
+
const PdfRegularInvoiceService = require('./PdfRegularInvoiceService');
|
|
5
|
+
const PdfFullElectronicInvoiceService = require('./PdfFullElectronicInvoiceService');
|
|
6
|
+
const PdfFinancialInvoiceService = require('./PdfFinancialInvoiceService');
|
|
7
|
+
const StringUtils = require('./StringUtils');
|
|
8
|
+
const ErrorHandler = require('./ErrorHandler');
|
|
9
|
+
|
|
10
|
+
// 设置 worker - Node.js 环境
|
|
11
|
+
const path = require('path');
|
|
12
|
+
pdfjs.GlobalWorkerOptions.workerSrc = path.join(
|
|
13
|
+
path.dirname(require.resolve('pdfjs-dist/package.json')),
|
|
14
|
+
'build/pdf.worker.js'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* PDF 发票提取器
|
|
19
|
+
*/
|
|
20
|
+
class PdfInvoiceExtractor {
|
|
21
|
+
static async extract(filePath) {
|
|
22
|
+
try {
|
|
23
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
24
|
+
// 转换 Buffer 为 Uint8Array
|
|
25
|
+
const pdfData = new Uint8Array(fileBuffer);
|
|
26
|
+
|
|
27
|
+
// 配置 PDF.js 参数
|
|
28
|
+
const pdfjsOptions = {
|
|
29
|
+
data: pdfData,
|
|
30
|
+
cMapUrl: path.join(
|
|
31
|
+
path.dirname(require.resolve('pdfjs-dist/package.json')),
|
|
32
|
+
'cmaps/'
|
|
33
|
+
),
|
|
34
|
+
cMapPacked: true,
|
|
35
|
+
standardFontDataUrl: path.join(
|
|
36
|
+
path.dirname(require.resolve('pdfjs-dist/package.json')),
|
|
37
|
+
'standard_fonts/'
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const pdf = await pdfjs.getDocument(pdfjsOptions).promise;
|
|
42
|
+
|
|
43
|
+
// 只处理第一页
|
|
44
|
+
const page = await pdf.getPage(1);
|
|
45
|
+
const textContent = await page.getTextContent();
|
|
46
|
+
const pageWidth = Math.round(page.view[2]);
|
|
47
|
+
|
|
48
|
+
// 获取所有文本 - 按行分组重建文本结构
|
|
49
|
+
let fullText = '';
|
|
50
|
+
const items = [];
|
|
51
|
+
|
|
52
|
+
// 按Y坐标排序文本项(从上到下)
|
|
53
|
+
const sortedItems = textContent.items
|
|
54
|
+
.filter(item => item.str && item.str.trim())
|
|
55
|
+
.sort((a, b) => b.transform[5] - a.transform[5]); // Y坐标从大到小
|
|
56
|
+
|
|
57
|
+
// 按行分组(Y坐标相近的视为同一行)
|
|
58
|
+
const lines = [];
|
|
59
|
+
let currentLine = [];
|
|
60
|
+
let lastY = null;
|
|
61
|
+
|
|
62
|
+
for (const item of sortedItems) {
|
|
63
|
+
const y = Math.round(item.transform[5]);
|
|
64
|
+
if (lastY === null || Math.abs(y - lastY) > 5) { // 5像素容差
|
|
65
|
+
if (currentLine.length > 0) {
|
|
66
|
+
lines.push(currentLine);
|
|
67
|
+
}
|
|
68
|
+
currentLine = [item];
|
|
69
|
+
lastY = y;
|
|
70
|
+
} else {
|
|
71
|
+
currentLine.push(item);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (currentLine.length > 0) {
|
|
75
|
+
lines.push(currentLine);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 在每行内按X坐标排序并拼接
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
line.sort((a, b) => a.transform[4] - b.transform[4]); // X坐标从小到大
|
|
81
|
+
|
|
82
|
+
let lineText = '';
|
|
83
|
+
for (const item of line) {
|
|
84
|
+
if (lineText && !lineText.endsWith(' ') && !item.str.startsWith(' ')) {
|
|
85
|
+
lineText += ' ';
|
|
86
|
+
}
|
|
87
|
+
lineText += item.str;
|
|
88
|
+
|
|
89
|
+
items.push({
|
|
90
|
+
text: item.str,
|
|
91
|
+
x: item.transform[4],
|
|
92
|
+
y: item.transform[5],
|
|
93
|
+
width: item.width,
|
|
94
|
+
height: item.height,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (fullText && !fullText.endsWith('\n')) {
|
|
99
|
+
fullText += '\n';
|
|
100
|
+
}
|
|
101
|
+
fullText += lineText;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 规范化文本
|
|
105
|
+
let allText = StringUtils.normalize(fullText)
|
|
106
|
+
.replace(/(/g, '(')
|
|
107
|
+
.replace(/)/g, ')')
|
|
108
|
+
.replace(/¥/g, '¥');
|
|
109
|
+
|
|
110
|
+
// 判断发票类型
|
|
111
|
+
if (allText.includes('福建省社会团体会员费统一收据') || allText.includes('财政票据')) {
|
|
112
|
+
return PdfFinancialInvoiceService.extract(fullText, allText, pageWidth, items);
|
|
113
|
+
} else if (allText.includes('电子发票')) {
|
|
114
|
+
return PdfFullElectronicInvoiceService.extract(fullText, allText, pageWidth, items);
|
|
115
|
+
} else {
|
|
116
|
+
return PdfRegularInvoiceService.extract(fullText, allText, pageWidth, items);
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
return ErrorHandler.createErrorInvoice(error, 'pdf');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = PdfInvoiceExtractor;
|