Checks for a duplicate before filing anything: the same document at the same site is archived, and a PO number already in the system is tagged PO Duplicate for a human instead of being filed twice. Successfully filed mail is tagged PO Ingested and moved out of the inbox; PO Needs Review and PO Duplicate stay in the inbox for review and are skipped on later runs.
595 lines
19 KiB
JavaScript
595 lines
19 KiB
JavaScript
/**
|
|
* MEGA - PO Email Intake (Microsoft Outlook)
|
|
* ------------------------------------------
|
|
* n8n Workflow SDK source. Rebuild with the n8n MCP `create_workflow_from_code` tool
|
|
* (validate first), or rebuild by hand in the n8n UI using
|
|
* docs/n8n/po-email-intake.md as the wiring reference.
|
|
*
|
|
* Live workflow: https://wfdash.n8n.apphub.co.za/workflow/gpKjNsXoqNUzsMb4
|
|
*
|
|
* What it does: polls ai@megainfra.co.za for purchase-order emails, downloads the PO
|
|
* attachment, and files it into the same PocketBase pending queue the browser upload
|
|
* button uses, so the existing "MEGA - Purchase Order Ingest" workflow OCRs and
|
|
* finalises it.
|
|
*
|
|
* Duplicate protection runs BEFORE a row is created:
|
|
* - same file hash at the same site -> the identical document is already filed, the
|
|
* email is archived and nothing is filed twice;
|
|
* - PO number already in the system (read from the attachment filename or subject,
|
|
* e.g. MEGCPO9524.pdf) -> tagged "PO Duplicate" and left in the inbox for a human,
|
|
* because a differing file can be a legitimate revision.
|
|
* The app also checks both again after OCR (process-batch) and flags po_number_conflict.
|
|
*
|
|
* Mail handling once filed:
|
|
* - success -> tagged "PO Ingested" and moved out of the inbox (Archive)
|
|
* - number in use -> tagged "PO Duplicate", stays in the inbox for review
|
|
* - no site found -> tagged "PO Needs Review", stays in the inbox for review
|
|
* - filing failed -> tagged "PO Needs Review", stays in the inbox for review
|
|
* Messages carrying any of those three labels are skipped on later runs.
|
|
*
|
|
* Credentials referenced (none of them contain an inline secret):
|
|
* - "Microsoft Outlook (MEGA PO)" (microsoftOutlookOAuth2Api) -> n8n's hosted Microsoft
|
|
* OAuth: create it in n8n and sign in as ai@megainfra.co.za. No app registration.
|
|
* Swap to microsoftEntraServicePrincipalApi for unattended app-only access.
|
|
* - "MEGAINFRASTRUCTURE" (pocketbaseHttpApi) -> already exists on this instance.
|
|
*
|
|
* Set the Microsoft credential on these eight nodes after creating it:
|
|
* List Inbox POs, List Attachments, Download Attachment, Archive Message,
|
|
* Archive Message (already filed), Label PO Filed, Flag Needs Review,
|
|
* Flag Needs Review (no site), Flag Needs Review (number in use)
|
|
*/
|
|
|
|
import { workflow, node, trigger, ifElse, splitInBatches, nextBatch, newCredential, expr } from '@n8n/workflow-sdk';
|
|
|
|
const GRAPH_CRED = { microsoftOutlookOAuth2Api: newCredential('Microsoft Outlook (MEGA PO)') };
|
|
const PB_CRED = { pocketbaseHttpApi: newCredential('MEGAINFRASTRUCTURE') };
|
|
|
|
const schedule = trigger({
|
|
type: 'n8n-nodes-base.scheduleTrigger',
|
|
version: 1.3,
|
|
config: {
|
|
name: 'Every 5 minutes',
|
|
parameters: { rule: { interval: [{ field: 'minutes', minutesInterval: 5 }] } }
|
|
}
|
|
});
|
|
|
|
const readSites = node({
|
|
type: 'n8n-nodes-pocketbase.pocketbaseHttp',
|
|
version: 1,
|
|
config: {
|
|
name: 'Read Sites',
|
|
parameters: {
|
|
resource: 'sites',
|
|
operation: 'search',
|
|
parameters: { allElements: true, elementsPerPage: 50, fields: ['name', 'project_code'] }
|
|
},
|
|
credentials: PB_CRED
|
|
}
|
|
});
|
|
|
|
const collectSites = node({
|
|
type: 'n8n-nodes-base.code',
|
|
version: 2,
|
|
config: {
|
|
name: 'Collect Sites',
|
|
parameters: {
|
|
mode: 'runOnceForAllItems',
|
|
jsCode: `const items = $input.all();
|
|
const sites = [];
|
|
for (const it of items) {
|
|
const j = it && it.json ? it.json : {};
|
|
if (j && j.id) sites.push({ id: j.id, name: j.name || '', project_code: j.project_code || '' });
|
|
}
|
|
return [{ json: { sites: sites } }];`
|
|
}
|
|
}
|
|
});
|
|
|
|
const listInbox = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'List Inbox POs',
|
|
parameters: {
|
|
resource: 'message',
|
|
operation: 'getAll',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
returnAll: false,
|
|
limit: 25,
|
|
output: 'simple',
|
|
filtersUI: {
|
|
values: {
|
|
filterBy: 'filters',
|
|
filters: { hasAttachments: true, readStatus: 'both', foldersToInclude: ['inbox'] }
|
|
}
|
|
}
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const selectMsgs = node({
|
|
type: 'n8n-nodes-base.code',
|
|
version: 2,
|
|
config: {
|
|
name: 'Select PO Messages',
|
|
parameters: {
|
|
mode: 'runOnceForAllItems',
|
|
jsCode: `const items = $input.all();
|
|
let sites = [];
|
|
try { sites = ($('Collect Sites').item.json.sites) || []; } catch (e) { sites = []; }
|
|
const POISH = /(\\bp\\/?o\\b|\\bpo\\b|purchase\\s*order|\\border\\b)/i;
|
|
const SKIP = ['PO Needs Review', 'PO Ingested', 'PO Duplicate'];
|
|
function norm(s) { return String(s || '').toUpperCase().replace(/\\s+/g, ' ').trim(); }
|
|
function matchSite(text) {
|
|
const t = norm(text);
|
|
if (!t) return null;
|
|
const flat = t.replace(/[^A-Z0-9]/g, '');
|
|
for (const s of sites) { const c = norm(s.project_code); if (c && t === c) return s; }
|
|
for (const s of sites) { const c = norm(s.project_code); if (c && c.length >= 3 && t.indexOf(c) >= 0) return s; }
|
|
for (const s of sites) { const n = norm(s.name); if (n && n.length >= 4 && t.indexOf(n) >= 0) return s; }
|
|
for (const s of sites) { const c = norm(s.project_code).replace(/[^A-Z0-9]/g, ''); if (c && c.length >= 3 && flat.indexOf(c) >= 0) return s; }
|
|
return null;
|
|
}
|
|
const out = [];
|
|
for (const it of items) {
|
|
const m = it && it.json ? it.json : {};
|
|
if (!m.id || m.hasAttachments !== true) continue;
|
|
const cats = Array.isArray(m.categories) ? m.categories : [];
|
|
let skip = false;
|
|
for (const c of SKIP) { if (cats.indexOf(c) >= 0) skip = true; }
|
|
if (skip) continue;
|
|
const subject = m.subject || '';
|
|
const preview = m.bodyPreview || '';
|
|
const from = (m.from && m.from.emailAddress && m.from.emailAddress.address) || '';
|
|
const hay = subject + ' ' + preview + ' ' + from;
|
|
const site = matchSite(hay);
|
|
const poish = POISH.test(hay);
|
|
if (!site && !poish) continue;
|
|
out.push({ json: {
|
|
messageId: m.id,
|
|
subject: subject,
|
|
bodyPreview: preview,
|
|
from: from,
|
|
received: m.receivedDateTime || '',
|
|
ready: !!site,
|
|
siteId: site ? site.id : '',
|
|
siteCode: site ? site.project_code : '',
|
|
siteName: site ? site.name : ''
|
|
} });
|
|
}
|
|
return out;`
|
|
}
|
|
}
|
|
});
|
|
|
|
const loop = splitInBatches({
|
|
version: 3,
|
|
config: { name: 'Loop Messages', parameters: { batchSize: 1 } }
|
|
});
|
|
|
|
const siteResolved = ifElse({
|
|
version: 2.2,
|
|
config: {
|
|
name: 'Site Resolved?',
|
|
parameters: {
|
|
conditions: {
|
|
options: { caseSensitive: true, leftValue: '', typeValidation: 'strict' },
|
|
conditions: [{ leftValue: expr('{{ $json.ready }}'), operator: { type: 'boolean', operation: 'equals' }, rightValue: true }],
|
|
combinator: 'and'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const listAtts = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'List Attachments',
|
|
parameters: {
|
|
resource: 'messageAttachment',
|
|
operation: 'getAll',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $json.messageId }}' }
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const selectAtts = node({
|
|
type: 'n8n-nodes-base.code',
|
|
version: 2,
|
|
config: {
|
|
name: 'Select PO Attachments',
|
|
parameters: {
|
|
mode: 'runOnceForAllItems',
|
|
jsCode: `const items = $input.all();
|
|
const DOC_TYPES = ['application/pdf', 'image/jpeg', 'image/png', 'image/webp'];
|
|
let messageId = '';
|
|
let subject = '';
|
|
try {
|
|
const mm = $('Select PO Messages').item.json || {};
|
|
messageId = mm.messageId || '';
|
|
subject = mm.subject || '';
|
|
} catch (e) { messageId = ''; subject = ''; }
|
|
if (!messageId) { try { messageId = $('Loop Messages').item.json.messageId || ''; } catch (e) { messageId = ''; } }
|
|
const out = [];
|
|
for (const it of items) {
|
|
const a = it && it.json ? it.json : {};
|
|
if (!a.id) continue;
|
|
if (a.isInline === true) continue;
|
|
const ct = String(a.contentType || '').toLowerCase();
|
|
const name = String(a.name || '');
|
|
if (DOC_TYPES.indexOf(ct) < 0) continue;
|
|
let numberHint = '';
|
|
const hit = (name + ' ' + subject).match(/MEG[A-Z]*PO[-_ ]?\\d{2,}/i);
|
|
if (hit) numberHint = hit[0].replace(/[^A-Za-z0-9]/g, '').toUpperCase();
|
|
out.push({ json: {
|
|
messageId: messageId,
|
|
attachmentId: a.id,
|
|
filename: name,
|
|
contentType: ct,
|
|
size: a.size || 0,
|
|
numberHint: numberHint
|
|
} });
|
|
}
|
|
return out;`
|
|
}
|
|
}
|
|
});
|
|
|
|
const download = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'Download Attachment',
|
|
parameters: {
|
|
resource: 'messageAttachment',
|
|
operation: 'download',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $json.messageId }}' },
|
|
attachmentId: { __rl: true, mode: 'id', value: '={{ $json.attachmentId }}' },
|
|
binaryPropertyName: 'data'
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const capture = node({
|
|
type: 'n8n-nodes-base.code',
|
|
version: 2,
|
|
config: {
|
|
name: 'Capture File',
|
|
parameters: {
|
|
mode: 'runOnceForAllItems',
|
|
jsCode: `const items = $input.all();
|
|
const out = [];
|
|
for (const item of items) {
|
|
// Site and message metadata live on the message node; attachment details on the
|
|
// attachment node. Reading site info from the attachment node leaves siteId empty
|
|
// and PocketBase rejects the row (site is a required relation).
|
|
let msgMeta = {};
|
|
try { msgMeta = $('Select PO Messages').item.json || {}; } catch (e) { msgMeta = {}; }
|
|
if (!msgMeta.messageId) { try { msgMeta = $('Loop Messages').item.json || {}; } catch (e) { msgMeta = {}; } }
|
|
let attMeta = {};
|
|
try { attMeta = $('Select PO Attachments').item.json || {}; } catch (e) { attMeta = {}; }
|
|
const bin = (item.binary && item.binary.data) || {};
|
|
const b64 = typeof bin.data === 'string' ? bin.data : '';
|
|
let fileHash = '';
|
|
try {
|
|
const crypto = require('crypto');
|
|
if (b64) fileHash = crypto.createHash('sha256').update(Buffer.from(b64, 'base64')).digest('hex');
|
|
} catch (e) { fileHash = ''; }
|
|
const stamp = Date.now().toString(36).toUpperCase();
|
|
out.push({
|
|
json: {
|
|
messageId: msgMeta.messageId || attMeta.messageId || '',
|
|
attachmentId: attMeta.attachmentId || '',
|
|
filename: bin.fileName || attMeta.filename || 'purchase-order.pdf',
|
|
contentType: bin.mimeType || attMeta.contentType || 'application/pdf',
|
|
fileHash: fileHash,
|
|
siteId: msgMeta.siteId || '',
|
|
siteCode: msgMeta.siteCode || '',
|
|
numberHint: attMeta.numberHint || '',
|
|
purchaseOrderNumber: fileHash ? ('PENDING-' + fileHash.slice(0, 12).toUpperCase()) : ('PENDING-' + stamp),
|
|
emailMeta: {
|
|
source: 'email',
|
|
message_id: msgMeta.messageId || attMeta.messageId || '',
|
|
subject: msgMeta.subject || '',
|
|
from: msgMeta.from || '',
|
|
received: msgMeta.received || '',
|
|
site_code: msgMeta.siteCode || '',
|
|
file_name: bin.fileName || attMeta.filename || ''
|
|
}
|
|
},
|
|
binary: item.binary
|
|
});
|
|
}
|
|
return out;`
|
|
}
|
|
}
|
|
});
|
|
|
|
const dupCheck = node({
|
|
type: 'n8n-nodes-pocketbase.pocketbaseHttp',
|
|
version: 1,
|
|
config: {
|
|
name: 'Duplicate Check',
|
|
alwaysOutputData: true,
|
|
parameters: {
|
|
resource: 'purchase_orders',
|
|
operation: 'search',
|
|
parameters: {
|
|
elementsPerPage: 5,
|
|
filter: expr('"(file_hash=\'" + $("Capture File").item.json.fileHash + "\' && site=\'" + $("Capture File").item.json.siteId + "\') || (purchase_order_number=\'" + ($("Capture File").item.json.numberHint || "") + "\')"')
|
|
}
|
|
},
|
|
credentials: PB_CRED
|
|
}
|
|
});
|
|
|
|
const classifyDup = node({
|
|
type: 'n8n-nodes-base.code',
|
|
version: 2,
|
|
config: {
|
|
name: 'Classify Duplicate',
|
|
parameters: {
|
|
mode: 'runOnceForAllItems',
|
|
jsCode: `const rows = $input.all();
|
|
let cap = {};
|
|
try { cap = $('Capture File').item.json || {}; } catch (e) { cap = {}; }
|
|
let verdict = 'none';
|
|
let hitId = '';
|
|
let hitNumber = '';
|
|
for (const r of rows) {
|
|
const j = r && r.json ? r.json : {};
|
|
if (!j.id) continue;
|
|
if (j.file_hash && cap.fileHash && j.file_hash === cap.fileHash) { verdict = 'same_file'; hitId = j.id; hitNumber = j.purchase_order_number || ''; break; }
|
|
if (!hitId) { hitId = j.id; hitNumber = j.purchase_order_number || ''; }
|
|
}
|
|
if (verdict !== 'same_file' && hitId) verdict = 'same_number';
|
|
return [{ json: {
|
|
verdict: verdict,
|
|
duplicateOf: hitId,
|
|
duplicateNumber: hitNumber,
|
|
fileHash: cap.fileHash || '',
|
|
siteId: cap.siteId || '',
|
|
siteCode: cap.siteCode || '',
|
|
numberHint: cap.numberHint || '',
|
|
purchaseOrderNumber: cap.purchaseOrderNumber || '',
|
|
messageId: cap.messageId || '',
|
|
emailMeta: cap.emailMeta || {}
|
|
} }];`
|
|
}
|
|
}
|
|
});
|
|
|
|
const sameFileFiled = ifElse({
|
|
version: 2.2,
|
|
config: {
|
|
name: 'Same File Filed?',
|
|
parameters: {
|
|
conditions: {
|
|
options: { caseSensitive: true, leftValue: '', typeValidation: 'loose' },
|
|
conditions: [{ leftValue: expr('{{ $json.verdict }}'), operator: { type: 'string', operation: 'equals' }, rightValue: 'same_file' }],
|
|
combinator: 'and'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const numberUsed = ifElse({
|
|
version: 2.2,
|
|
config: {
|
|
name: 'Number Already Used?',
|
|
parameters: {
|
|
conditions: {
|
|
options: { caseSensitive: true, leftValue: '', typeValidation: 'loose' },
|
|
conditions: [{ leftValue: expr('{{ $json.verdict }}'), operator: { type: 'string', operation: 'equals' }, rightValue: 'same_number' }],
|
|
combinator: 'and'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const createPo = node({
|
|
type: 'n8n-nodes-pocketbase.pocketbaseHttp',
|
|
version: 1,
|
|
config: {
|
|
name: 'Create PO Row',
|
|
onError: 'continueRegularOutput',
|
|
parameters: {
|
|
resource: 'purchase_orders',
|
|
operation: 'create',
|
|
bodyType: ['fields', 'binaryData'],
|
|
binaryPropertyName: 'data',
|
|
binaryFieldName: 'purchase_order_file',
|
|
fields: {
|
|
assignments: [
|
|
{ id: 'f1', name: 'purchase_order_number', value: expr('{{ $("Capture File").item.json.purchaseOrderNumber }}'), type: 'string' },
|
|
{ id: 'f2', name: 'site', value: expr('{{ $("Capture File").item.json.siteId }}'), type: 'string' },
|
|
{ id: 'f3', name: 'processing_status', value: 'pending', type: 'string' },
|
|
{ id: 'f4', name: 'attempts', value: 0, type: 'number' },
|
|
{ id: 'f5', name: 'is_verified', value: false, type: 'boolean' },
|
|
{ id: 'f6', name: 'file_hash', value: expr('{{ $("Capture File").item.json.fileHash }}'), type: 'string' },
|
|
{ id: 'f7', name: 'date_uploaded', value: expr('{{ $now.toISO() }}'), type: 'string' },
|
|
{ id: 'f8', name: 'raw_ocr_data', value: expr('{{ $("Capture File").item.json.emailMeta }}'), type: 'object' }
|
|
]
|
|
}
|
|
},
|
|
credentials: PB_CRED
|
|
}
|
|
});
|
|
|
|
const checkResult = node({
|
|
type: 'n8n-nodes-base.code',
|
|
version: 2,
|
|
config: {
|
|
name: 'Check Result',
|
|
parameters: {
|
|
mode: 'runOnceForAllItems',
|
|
jsCode: `const j = $input.first() ? $input.first().json : {};
|
|
let outcome = 'failed';
|
|
let detail = '';
|
|
let poId = '';
|
|
if (j && j.id) {
|
|
outcome = 'filed';
|
|
poId = j.id;
|
|
} else {
|
|
const err = j && (j.error || j.message) ? String(j.error || j.message) : JSON.stringify(j).slice(0, 300);
|
|
detail = err;
|
|
}
|
|
return [{ json: { outcome: outcome, detail: detail, poId: poId } }];`
|
|
}
|
|
}
|
|
});
|
|
|
|
const filedOk = ifElse({
|
|
version: 2.2,
|
|
config: {
|
|
name: 'Filed OK?',
|
|
parameters: {
|
|
conditions: {
|
|
options: { caseSensitive: true, leftValue: '', typeValidation: 'loose' },
|
|
conditions: [{ leftValue: expr('{{ $json.outcome }}'), operator: { type: 'string', operation: 'equals' }, rightValue: 'filed' }],
|
|
combinator: 'and'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const labelFiled = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'Label PO Filed',
|
|
parameters: {
|
|
resource: 'message',
|
|
operation: 'update',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $("Capture File").item.json.messageId }}' },
|
|
updateFields: { categories: ['PO Ingested'], isRead: true }
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const archiveNew = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'Archive Message',
|
|
parameters: {
|
|
resource: 'message',
|
|
operation: 'move',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $("Capture File").item.json.messageId }}' },
|
|
folderId: { __rl: true, mode: 'id', value: 'archive' }
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const archiveDup = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'Archive Message (already filed)',
|
|
parameters: {
|
|
resource: 'message',
|
|
operation: 'move',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $("Capture File").item.json.messageId }}' },
|
|
folderId: { __rl: true, mode: 'id', value: 'archive' }
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const flagNumber = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'Flag Needs Review (number in use)',
|
|
parameters: {
|
|
resource: 'message',
|
|
operation: 'update',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $("Capture File").item.json.messageId }}' },
|
|
updateFields: { categories: ['PO Duplicate'], isRead: true }
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const flagReview = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'Flag Needs Review',
|
|
parameters: {
|
|
resource: 'message',
|
|
operation: 'update',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $("Capture File").item.json.messageId }}' },
|
|
updateFields: { categories: ['PO Needs Review'], isRead: true }
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const flagNoSite = node({
|
|
type: 'n8n-nodes-base.microsoftOutlook',
|
|
version: 2,
|
|
config: {
|
|
name: 'Flag Needs Review (no site)',
|
|
parameters: {
|
|
resource: 'message',
|
|
operation: 'update',
|
|
authentication: 'microsoftOutlookOAuth2Api',
|
|
messageId: { __rl: true, mode: 'id', value: '={{ $json.messageId }}' },
|
|
updateFields: { categories: ['PO Needs Review'], isRead: true }
|
|
},
|
|
credentials: GRAPH_CRED
|
|
}
|
|
});
|
|
|
|
const summary = node({
|
|
type: 'n8n-nodes-base.code',
|
|
version: 2,
|
|
config: {
|
|
name: 'Run Summary',
|
|
parameters: {
|
|
mode: 'runOnceForAllItems',
|
|
jsCode: `const items = $input.all();
|
|
return [{ json: { finished: true, processed: items.length } }];`
|
|
}
|
|
}
|
|
});
|
|
|
|
export default workflow('mega-po-email-intake-v3', 'MEGA - PO Email Intake (Microsoft Outlook)')
|
|
.add(schedule)
|
|
.to(readSites)
|
|
.to(collectSites)
|
|
.to(listInbox)
|
|
.to(selectMsgs)
|
|
.to(loop
|
|
.onDone(summary)
|
|
.onEachBatch(siteResolved
|
|
.onTrue(listAtts
|
|
.to(selectAtts)
|
|
.to(download
|
|
.to(capture)
|
|
.to(dupCheck)
|
|
.to(classifyDup)
|
|
.to(sameFileFiled
|
|
.onTrue(archiveDup.to(nextBatch(loop)))
|
|
.onFalse(numberUsed
|
|
.onTrue(flagNumber.to(nextBatch(loop)))
|
|
.onFalse(createPo
|
|
.to(checkResult)
|
|
.to(filedOk
|
|
.onTrue(labelFiled.to(archiveNew.to(nextBatch(loop))))
|
|
.onFalse(flagReview.to(nextBatch(loop)))))))))
|
|
.onFalse(flagNoSite.to(nextBatch(loop)))));
|