kintone+计算机视觉---发票识别

cybozu发表于:2020年05月21日 15:24:24更新于:2020年05月28日 18:11:26

Index

前言

合合信息是一家致力于OCR识别技术与企业大数据的IT公司,利用它提供的API接口我们可以很方便的为kintone添加图像识别功能并拓展出更多的应用场景,如:名片扫描、合同上传、发票报销......


本文告诉大家如何在发票报销App中添加识别功能。


效果图如下:

0015ec62bbf0a86a59cc1af639f9c8b


应用的自定义开发

我们先来创建一个发票报销的App。

Step1: 设置字段

0015ecf7fde42c4691899a7a0dc4f4b

字段名字段类型code备注
空白栏fileSel存放自定义的文件上传按钮
发票明细表格table显示发票详细信息
购买方名称单行文本框购买方名称
发票类型单行文本框发票类型
发票号码单行文本框发票号码
价税合计小写数值价税合计小写
价税合计大写单行文本框价税合计大写
附件附件附件用于保存电子发票文件
空白栏preview预览电子发票


Step2: 导入公用库

0015ec62bbfcf1c47b6365ca0302ec4

下面是我们要用到的库:

kintone JS SDK

kintone UI Component


※ 这里不对这两个库做具体介绍,如果还不熟悉它们,请先参阅相关文档。

※ 本文只针对PC端如何实现做简要说明,需要在mobile端实现方案的请自行修改。


Step3: 创建JavaScript文件

ocr.js
  • kintone不支持附件字段的监听事件,我们需要自定义一个上传的按钮

            kintone.events.on(['app.record.create.show', 'app.record.edit.show'], onShow);
            function onShow(event) {
                const attachment = new kintoneUIComponent.Attachment();
                ......
                kintone.app.record.getSpaceElement('fileSel').append(attachment.render());
                attachment.on('filesAdd', function (files) {
                ......
                })
                return event;
            }
  • 从合合AI的simplecode中我们了解到,它只能支持pdf和图片,所以要对可选文件加上限制。

                const input = attachment.element.getElementsByTagName('input')[0];
                input.accept = 'application/pdf,image/*';
  • 由于需要调用外部API上传文件,建议使用kintone.Promise来做异步处理

                    const file = files[files.length - 1];
                    kintone.Promise.all([uploadFile(file.name, file), getData(file)]).then(function (result) {
                        if (result[1][1] == 200) {
                            const data = JSON.parse(result[1][0]);
                            data.code == 200 && data.message == 'success' && updateDisplayData(result[0].fileKey, data);
                        }
                    }).catch(function (error) {
                        console.log(error);
                    });
  • 文件上传

            const client = new KintoneRestAPIClient()
            function uploadFile(name, blob) {
                const file = {
                    name: name,
                    data: blob
                };
                return client.file.uploadFile({
                    file: file
                });
            }
  • 将文件用Base64编码

           function readData(file) {
                return new kintone.Promise(function (resolve, reject) {
                    const fr = new FileReader();
                    fr.onerror = reject;
                    fr.onload = function () {
                        resolve(fr.result);
                    }
                    fr.readAsDataURL(file);
                });
            }
  • 实现发票识别

            const appKey = '.....'; // your app_key;
            const appSecret = '......';// your app_secret;
            const url = '.....';
            
            function inv(fileData) {
                const idx = fileData.indexOf(',');
                const img = fileData.substr(idx + 1, fileData.length - idx);
                return kintone.proxy(url + appKey, 'POST', {
                    'content-type': 'application/json',
                }, {
                    app_secret: appSecret,
                    image_data: img
                });
            }
  • 封装上述函数

           function getData(file) {
                return readData(file).then(function (fileData) {
                    preview(fileData);
                    return inv(fileData);
                })
            }
  • 更新显示字段

        ※ 附件字段是无法重写的字段,我们需要临时保存fileKey

            const keyStore = new Array()
            function updateDisplayData(key, data) {
                const rec = kintone.app.record.get();
                const table = rec.record.table.value;
                const isFirst = table.length == 1 && !isNotEmpty(table[0].value);
                const row = isFirst ? table[0].value : { '购买方名称': { type: 'SINGLE_LINE_TEXT', value: '' }, '发票类型': { type: 'SINGLE_LINE_TEXT', value: '' }, '价税合计小写': { type: 'NUMBER', value: '' }, '价税合计大写': { type: 'SINGLE_LINE_TEXT', value: '' }, '发票号码': { type: 'SINGLE_LINE_TEXT', value: '' }, '附件': { type: 'FILE', value: [] } };
                data.result.ocr_data_list.forEach(function (v) {
                    const k = v.description.replace(/\/|、|\(|\)/gi, '_');
                    row.hasOwnProperty(k) && (row[k].value = v.value);
                });
                keyStore[row.发票号码.value] = key;
                !isFirst && table.push({ id: null, value: row });
                kintone.app.record.set(rec);
            }
  • 在submit.success事件中更新到附件字段

            kintone.events.on(['app.record.create.submit.success', 'app.record.edit.submit.success'], onSubmitSuccess);
            
            function onSubmitSuccess(event) {
                if (keyStore.length > 0) {
                    const table = event.record.table.value;
                    for (let k in table) {
                        const row = table[k].value;
                        if (keyStore.hasOwnProperty(row.发票号码.value) && row.附件.value.length == 0) {
                            row.附件.value.push({ fileKey: keyStore[row.发票号码.value] });
                        }
                    }
                    const json = {
                        app: event.appId,
                        id: event.recordId,
                        record: {
                            table: {
                                value: table
                            }
                        }
                    };
                    return client.record.updateRecord(json);
                }
            }

Step4: 导入自定义开发文件到kintone

0015ec62bbc75f37714c26d8a2a3444

Step5: 验证

0015ec6378c0fb61a297f8cead8d35b


注意事项

  • ※ 不要在js中暴露appKey和appSecret

  • ※ 本文代码仅供参考

  • ※ 我们不为本示例代码提供技术支持。