Index
前言
合合信息是一家致力于OCR识别技术与企业大数据的IT公司,利用它提供的API接口我们可以很方便的为kintone添加图像识别功能并拓展出更多的应用场景,如:名片扫描、合同上传、发票报销......
本文告诉大家如何在发票报销App中添加识别功能。
效果图如下:
应用的自定义开发
我们先来创建一个发票报销的App。
Step1: 设置字段
字段名 | 字段类型 | code | 备注 |
无 | 空白栏 | fileSel | 存放自定义的文件上传按钮 |
发票明细 | 表格 | table | 显示发票详细信息 |
购买方名称 | 单行文本框 | 购买方名称 | |
发票类型 | 单行文本框 | 发票类型 | |
发票号码 | 单行文本框 | 发票号码 | |
价税合计小写 | 数值 | 价税合计小写 | |
价税合计大写 | 单行文本框 | 价税合计大写 | |
附件 | 附件 | 附件 | 用于保存电子发票文件 |
无 | 空白栏 | preview | 预览电子发票 |
Step2: 导入公用库
下面是我们要用到的库:
※ 这里不对这两个库做具体介绍,如果还不熟悉它们,请先参阅相关文档。
※ 本文只针对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
Step5: 验证
注意事项
※ 不要在js中暴露appKey和appSecret
※ 本文代码仅供参考
※ 我们不为本示例代码提供技术支持。