Skip to content

Putting it together

Time to use everything. The job: a despatch note arrives on a share. Its order number has been read by Captur. Gather every other document with that order number into one folder named after the customer.

This is a cut-down version of a script that runs for a real customer. It has the same shape.

Before writing code, write the steps in plain words:

  1. Run when a document is indexed, or when someone corrects its fields.
  2. Stop unless the document is a despatch note with a usable order number.
  3. Work out the customer and the folder name.
  4. Find every document carrying that order number.
  5. Create the customer folder if it isn’t there.
  6. Copy each document in, unless it’s already there.
  7. Say what happened.

Each step is a few lines. Steps 1 and 2 are the guards from A safe event script in Reacting to events.

Everything that might differ between customers or shares goes in config parameters (see Settings that belong to the automation in Reacting to events), each with a default:

set errorblock to "onError"
block onError
printline "Error (line {{mash.error.line}}): {{mash.error.message}}"
end
set refField to mash.config.get("REF_FIELD") ?? "Order No"
set typeField to mash.config.get("DOCTYPE_FIELD") ?? "Document Type"
set triggerType to mash.config.get("TRIGGER_DOC_TYPE") ?? "Despatch Note"
set custField to mash.config.get("CUSTOMER_FIELD") ?? "Customer Name"
set destShare to mash.config.get("DEST_SHARE")
if not destShare
fail "DEST_SHARE is not set on this automation."
end

The error handler comes first, so any later failure prints a line you can find in the log. DEST_SHARE has no default because there’s no sensible one, so the script fails loudly rather than guessing.

set doc to mash.event.file
if not doc
return
end
if doc.is_dir
return
end
set docType to mash.str(doc.fields[typeField] ?? "").trim().toString()
if mash.str(docType).lower() is not mash.str(triggerType).lower()
return
end
set reference to mash.str(doc.fields[refField] ?? "").trim().toString()
if mash.str(reference).length() is less than 3
printline "{{doc.name}} has no usable order number yet, nothing to gather."
return
end

Every return here is a quiet exit. Both triggers fire for every document on the share. Without the type check, each certificate would start its own gather.

The ?? "" matters. On the first Indexed event Captur hasn’t finished, so the fields are empty.

The name is built from text a scanner read off a document, so treat it as untrustworthy. Slashes, colons and the like are not welcome in a folder name. Here is the logic on its own, which you can run:

block folderNameFor customer reference
set name to mash.str(customer).trim().toString()
if name is ""
set name to "Unknown Customer"
end
set full to "{{name}} - {{reference}}"
set full to mash.str(full).replaceMatches('#[\\/:*?"<>|]#', "-").toString()
return mash.str(full).trim().toString()
end
printline folderNameFor("Acme Ltd", "SO-20417")
printline folderNameFor(" ", "SO-20418")
printline folderNameFor("A/B: Traders", "SO-1")
Acme Ltd - SO-20417
Unknown Customer - SO-20418
A-B- Traders - SO-1

Pulling the logic into a block means you can test it without a share, using the editor’s Run button, before the script ever sees a real document. The same trick works for the decision about which documents to skip:

set docs to [
{name: "despatch.pdf", type: "Despatch Note", ref: "SO-1"},
{name: "cert.pdf", type: "Certificate", ref: "SO-1"},
{name: "other.pdf", type: "Despatch Note", ref: ""}
]
each docs as d
set ref to mash.str(d["ref"] ?? "").trim().toString()
if d["type"] is not "Despatch Note"
next
end
if mash.str(ref).length() is less than 3
printline "{{d['name']}}: no usable reference yet"
next
end
printline "{{d['name']}}: gather {{ref}}"
end
despatch.pdf: gather SO-1
other.pdf: no usable reference yet
set filters to {}
set filters[refField] to reference
set related to mash.fields.findFiles(filters)
if count(related) is 0
return
end
set parent to mash.files.get(destShare, "")
set target to parent.make(folderNameFor(customer, reference), true)
set filed to 0
set skipped to 0
each related as hit
if mash.files.exists(destShare, target.path ~ "/" ~ hit["name"])
increment skipped by 1
else
set source to mash.files.get(hit["share_id"], hit["path"])
source.duplicate(target)
increment filed by 1
end
end
printline "Order {{reference}}: {{filed}} filed, {{skipped}} already there."

Three details here make the script safe to leave running:

  • make(name, true) reuses the folder if it exists, so every despatch note can run it.
  • It copies (duplicate) rather than moves. Copying can be undone. Moving can’t.
  • It skips a document that’s already in the folder. The copies keep their order number, so they get indexed too and match the same search, and without this check the script would copy forever.

Put the pieces together, with customer read the same way as reference and folderNameFor defined above the guards. This is the shape every event script in this style has: settings, guards, the work, a summary line.

set errorblock to "onError"
block onError
printline "Error (line {{mash.error.line}}): {{mash.error.message}}"
end
block folderNameFor customer reference
set name to mash.str(customer).trim().toString()
if name is ""
set name to "Unknown Customer"
end
set full to "{{name}} - {{reference}}"
set full to mash.str(full).replaceMatches('#[\\/:*?"<>|]#', "-").toString()
return mash.str(full).trim().toString()
end
set refField to mash.config.get("REF_FIELD") ?? "Order No"
set typeField to mash.config.get("DOCTYPE_FIELD") ?? "Document Type"
set triggerType to mash.config.get("TRIGGER_DOC_TYPE") ?? "Despatch Note"
set custField to mash.config.get("CUSTOMER_FIELD") ?? "Customer Name"
set destShare to mash.config.get("DEST_SHARE")
if not destShare
fail "DEST_SHARE is not set on this automation."
end
set doc to mash.event.file
if not doc
return
end
if doc.is_dir
return
end
set docType to mash.str(doc.fields[typeField] ?? "").trim().toString()
if mash.str(docType).lower() is not mash.str(triggerType).lower()
return
end
set reference to mash.str(doc.fields[refField] ?? "").trim().toString()
if mash.str(reference).length() is less than 3
printline "{{doc.name}} has no usable order number yet, nothing to gather."
return
end
set customer to mash.str(doc.fields[custField] ?? "").trim().toString()
set filters to {}
set filters[refField] to reference
set related to mash.fields.findFiles(filters)
if count(related) is 0
return
end
set parent to mash.files.get(destShare, "")
set target to parent.make(folderNameFor(customer, reference), true)
set filed to 0
set skipped to 0
each related as hit
if mash.files.exists(destShare, target.path ~ "/" ~ hit["name"])
increment skipped by 1
else
set source to mash.files.get(hit["share_id"], hit["path"])
source.duplicate(target)
increment filed by 1
end
end
printline "Order {{reference}}: {{filed}} filed, {{skipped}} already there."

To use it: save it, add it to Captur Extracted (for fields Captur fills) and Fields Changed (for fields a person corrects) under Settings > MaSH > Events, filter it to the share documents arrive on, choose its Configuration and set DEST_SHARE. Then drop a test despatch note onto the share and watch Logs.

Change the script to file a certificate too, not only a despatch note. Hint: the check on docType is the only line that has to change, because the search by order number already finds everything. Think about what skipped protects you from before you do it.

Next, where to go from here.