进入了我的学术休假季,开始做一些自己喜欢的题目。今天就来扒一扒一个NetSuite落满灰尘的功能--Plug-In。
大家可能听到过一个叫做Email Approval的应用场景,可以让用户在不登录NetSuite系统的情况下,跟NetSuite产生交互。例如,通过邮件回复或者“按钮”来审批PO或费用报告。

这就是一个典型的Plug-In应用。
NetSuite Plug-In的背后是NetSuite架构师的一个想法,就是“面向对象”。这个设计的初衷是,将NetSuite系统中的某些业务逻辑松耦合,让开发者可以对这些业务环节进行修改。例如,我们俗称为“过账引擎” 的Custom GL Plug-In,其基本思路就是让开发者可以干预过账逻辑,从而适应客户业务场景的多变。例如,在销售发票过账环节,系统的标准处理是取Item上的Income科目预设值,以及客户上的AR科目预设值。但是,客户说我能不能在发票上客制一个“收入”字段,让这个发票都计入到这个临时设定的收入,而不是Item上的收入科目。这个需求在不同的财务软件中有不同的名字,在NetSuite中我们是通过Custom GL Plug-In来实现的。
NetSuite Plug-In分为两类,一种叫做Custom Plug-In,另一种叫做Core Plug-In,前者是NetSuite鼓励其他开发商使用的,后者是NetSuite自己用的。
这是一个架构示意图,满满的面向对象的想法。

以上面Email Approval的应用场景来举例,实际上是应用了一个叫做Email Capture的NetSuite Core Plug-In类型,做的一个实例化。

但是,不知为何。NetSuite并没有在更大范围里推广Plug-In。甚至在上面所说的过账引擎中,也是浅尝辄止。
目前NetSuite提供了不多的几个Core Plug-In,见下表:

其中好用的还是前两个,一个用于过账引擎,另一个用于Email侦听。
如上所述,如果我们作为实施顾问来应用NetSuite Core Plug-In的话,基本上是在既有的框架下,填入自己的处理逻辑来实现的。例如,如果是基于Email Capture Plug-In来写自己的应用到话。就是遵从其Interface Definition就可以了。也有示例代码啥的,把自己的代码抄进去就行了。

如下是一个典型的邮件侦听应用,用于进行特定邮箱邮件的收集汇总。
转载自网文:
NetSuite Email Capture Example – NetSuite Experiences
- function process(email) {
-
- const IS_PRODUCTION = true;
-
- const valid_types = [
- 'APPCACHE',
- 'AUTOCAD',
- 'BMPIMAGE',
- 'CERTIFICATE',
- 'CONFIG',
- 'CSV',
- 'EXCEL',
- 'FLASH',
- 'FREEMARKER',
- 'GIFIMAGE',
- 'GZIP',
- 'HTMLDOC',
- 'ICON',
- 'JAVASCRIPT',
- 'JPGIMAGE',
- 'JSON',
- 'MESSAGERFC',
- 'MP3',
- 'MPEGMOVIE',
- 'MSPROJECT',
- 'PDF',
- 'PJPGIMAGE',
- 'PLAINTEXT',
- 'PNGIMAGE',
- 'POSTSCRIPT',
- 'POWERPOINT',
- 'QUICKTIME',
- 'RTF',
- 'SCSS',
- 'SMS',
- 'STYLESHEET',
- 'SVG',
- 'TAR',
- 'TIFFIMAGE',
- 'VISIO',
- 'WEBAPPPAGE',
- 'WEBAPPSCRIPT',
- 'WORD',
- 'XMLDOC',
- 'XSD',
- 'ZIP',
- ]
-
- var fromAddress = email.getFrom();
- nlapiLogExecution('DEBUG', 'Email - from: ' + fromAddress.getName() + ', ' + fromAddress.getEmail());
- nlapiLogExecution('DEBUG', 'subject - ' + email.getSubject());
-
- var newRec = nlapiCreateRecord('customrecord_xxxxxxxxxxxxxxxxxxxxxxxxxxxx');
- newRec.setFieldValue('custrecord_xxxxxxxxxx', fromAddress.getName());
- newRec.setFieldValue('custrecord_xxxxxxxxxx', fromAddress.getEmail());
- newRec.setFieldValue('custrecord_xxxxxxxxxx', email.getSubject());
- newRec.setFieldValue('custrecord_xxxxxxxxxx', email.getTextBody());
- var newRec_id = nlapiSubmitRecord(newRec, true);
-
- var attachments = email.getAttachments();
-
- // This variable is here in case you wanted to add notes about attachments.
- // it is currently unused.
- var processing_notes = '';
-
- for (var indexAtt in attachments) {
- var attachment = attachments[indexAtt];
- nlapiLogExecution('DEBUG', 'Attachment: ' + attachment.getName() + ', ' + attachment.getType());
-
- // If the file name does not have an extension, skip it
- var fileName = attachment.getName();
- if (fileName.indexOf('.') <= 0) continue;
-
- // add a unique suffix to the file name, but leave the extension as-is
- var fileArray = fileName.split('.');
- var newName = '';
- for (var i = 0; i < fileArray.length; i++) {
- if (i == fileArray.length - 1) {
- newName += ('_' + (new Date().valueOf()).toString());
- }
- newName += ('.' + fileArray[i]);
- }
-
- // Lookup the file type to see if it is supported, else save as PLAINTEXT
- // This really won't affect being able to open and download the file.
- // It only affects filtering files by type.
- var file_type = attachment.getType().toUpperCase();
- if (valid_types.filter(function (p) { return p == file_type }).length == 0) {
- file_type = 'PLAINTEXT'; // Import nonrecognized file types as Other Binary File
- }
-
- var file = nlapiCreateFile(newName, file_type, attachment.getValue());
-
- // Save the file under a selected folder in the file cabinet
- file.setFolder(1111111111); //Internal ID of folder to hold imported attachments
- var file_id = nlapiSubmitFile(file);
-
- // Attach the file to a custom record type
- nlapiAttachRecord('file', file_id, 'customrecord_xxxxxxxxxxxxxxxxxxxxxxxxxxxx', newRec_id);
- }
-
- // here is where you'd include notes about attachments, perhaps those that were erroniously
- // saved under the PLAINTEXT type.
- nlapiSubmitField('customrecord_xxxxxxxxxxxxxxxxxxxxxxxxxxxx', newRec_id, 'custrecord_xxxxxxxxxx', processing_notes);
- }
举一反三,大家应该可以用这个作为例子,写一下你自己的邮件侦听应用了。邮件审批、邮件新建拜访日志、邮件触发工作流等等。
以上是对NetSuite Plug-In的概述,Show me the code. Have fun!