Import From Paperless
Summary: Imports a database from the Mariner Paperless app (formerly ReceiptWallet).
Requires: EagleFiler
Install Location: ~/Library/Scripts/Applications/EagleFiler/
Last Modified: 2026-01-15
Description
The Paperless app from Mariner Software (formerly called ReceiptWallet) has been discontinued. This script lets you migrate the files and metadata from your Paperless database into EagleFiler. When you run the script, it will ask you to select the .paperless package for your database. It will then import all the files into the frontmost EagleFiler library window:
- The Category and Subcategory will be preserved as folders.
- Any items that were in the trash will be preserved in a separate Trash folder.
- Collections are imported as EagleFiler tags. If you have multiple collections with the same name, of if you’re using a version of EagleFiler older than 1.9.20, the tag name may have a number appended to make it unique.
- The title is imported to EagleFiler’s Title field.
- The original filename is preserved.
- The date is imported as EagleFiler’s Date Created.
- The import date is imported as EagleFiler’s Date Added.
- The tags are imported as EagleFiler tags.
- The notes are imported as EagleFiler notes.
- The dollar amount, shipping, tax, and tip are imported into the EagleFiler note.
- Additional custom fields are imported into the EagleFiler note.
Installation Instructions · Download in Compiled Format · Download in Text Format
Script
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
property _tagSeparator : "|"
set _package to choose file of type {"paperless"} with prompt "Select the Mariner Paperless package to import into EagleFiler."
set _databasePath to _package's POSIX path & "/DocumentWallet.documentwalletsql"
my importPackage(_package)
on importPackage(_package)
set _folderName to my getBasename(_package's POSIX path)
tell application "EagleFiler"
tell library document 1
set _folderRecord to add folder name _folderName
set _tagName to _folderRecord's name & " Collections"
set _topTag to tag _tagName
end tell
end tell
set _ids to getDocumentIDs()
repeat with _id in _ids
my importID(_id, _folderRecord, _topTag)
end repeat
tell application "EagleFiler"
repeat with _tag in _topTag's tags
my removeCollectionIDs(_tag)
end repeat
end tell
end importPackage
on getDocumentIDs()
paragraphs of my sqlite("SELECT Z_PK FROM ZRECEIPT ORDER BY Z_PK")
end getDocumentIDs
on importID(_id, _topFolderRecord, _topTag)
if my isInTrash(_id) then
set _topFolderRecord to my getFolder(_topFolderRecord, "Trash")
end if
set _category to my getCategoryName(_id)
set _subcategory to my getSubcategoryName(_id)
set _folderRecord to _topFolderRecord
if _category is not "" then
set _folderRecord to my getFolder(_topFolderRecord, _category)
end if
if _subcategory is not "" then
set _folderRecord to my getFolder(_folderRecord, _subcategory)
end if
tell application "EagleFiler"
global _package
tell _folderRecord's library document
set _path to _package's POSIX path & "/" & my getAttribute(_id, "ZPATH")
try
set _tagNames to my getTagNames(_id) & my getCollectionNames(_id, _topTag)
set {_record} to import files {_path} container _folderRecord tag names _tagNames note my getNote(_id) with allowing duplicates
set _record's added date to my getDate(_id, "ZIMPORTDATE")
set _record's creation date to my getDate(_id, "ZDATE")
set _record's title to my getAttribute(_id, "ZMERCHANT")
set _record's basename to my getBasename(my getAttribute(_id, "ZORIGINALFILENAME"))
on error _error
with timeout of 60 * 60 * 24 seconds
display dialog "Error importing: " & _path & return & return & _error
end timeout
end try
end tell
end tell
end importID
on isInTrash(_id)
set _value to my getAttribute(_id, "ZINTRASHVALUE")
if _value is "" then return false
return _value as number as boolean
end isInTrash
on getAttribute(_id, _name)
my sqlite("SELECT " & _name & " FROM ZRECEIPT WHERE Z_PK=" & _id)
end getAttribute
on getCategoryName(_id)
my sqlite("SELECT ZNAME FROM ZCATEGORY INNER JOIN ZRECEIPT ON ZCATEGORY.Z_PK = ZRECEIPT.ZCATEGORY WHERE ZRECEIPT.Z_PK=" & _id)
end getCategoryName
on getSubcategoryName(_id)
my sqlite("SELECT ZNAME FROM ZSUBCATEGORY INNER JOIN ZRECEIPT ON ZSUBCATEGORY.Z_PK = ZRECEIPT.ZSUBCATEGORY WHERE ZRECEIPT.Z_PK=" & _id)
end getSubcategoryName
on getDate(_id, _name)
set _timeInterval to my getAttribute(_id, _name) as number
set _nsDate to current application's NSDate's dateWithTimeIntervalSinceReferenceDate:_timeInterval
return _nsDate as date
end getDate
on getTagNames(_id)
set _sql to "SELECT ZNAME FROM ZTAG WHERE Z_PK IN (SELECT Z_18TAGS FROM Z_14TAGS INNER JOIN ZRECEIPT ON Z_14TAGS.Z_14RECEIPTS1 = ZRECEIPT.Z_PK WHERE ZRECEIPT.Z_PK=" & _id & ")"
return paragraphs of my sqlite(_sql)
end getTagNames
on getCollectionIDs(_id)
set _sql to "SELECT Z_3COLLECTIONS FROM Z_3RECEIPTS INNER JOIN ZRECEIPT ON Z_3RECEIPTS.Z_14RECEIPTS = ZRECEIPT.Z_PK WHERE ZRECEIPT.Z_PK=" & _id
return paragraphs of my sqlite(_sql)
end getCollectionIDs
on isBasicCollection(_id)
my sqlite("SELECT ZTYPE=1 FROM ZCOLLECTION WHERE Z_PK=" & _id) as number as boolean
end isBasicCollection
on getCollectionName(_id)
my sqlite("SELECT ZNAME FROM ZCOLLECTION WHERE Z_PK=" & _id)
end getCollectionName
on getCollectionParent(_id)
my sqlite("SELECT ZPARENT FROM ZCOLLECTION WHERE Z_PK=" & _id)
end getCollectionParent
on getCollectionNames(_id, _topTag)
set _result to {}
repeat with _id in my getCollectionIDs(_id)
if my isBasicCollection(_id) then
set _tagName to my tagNameForCollection(_id, _topTag)
copy _tagName to end of _result
end if
end repeat
return _result
end getCollectionNames
on tagNameForCollection(_id, _topTag)
tell application "EagleFiler"
tell _topTag's library document
set _parentCollection to my getCollectionParent(_id)
if _parentCollection is "" then
set _parentTag to _topTag
else
set _parentTag to tag (my tagNameForCollection(_parentCollection, _topTag))
end if
set _tagName to my getCollectionName(_id) & _tagSeparator & _id
set _tag to tag _tagName
set _tag's container to _parentTag
return _tag's name
end tell
end tell
end tagNameForCollection
on removeCollectionIDs(_tag)
tell application "EagleFiler"
set {_newName, _prefix} to my split(_tag's name, _tagSeparator)
try
set _tag's name to _newName
on error
-- This requires EagleFiler 1.9.20 or later.
-- If there's a duplicate name, it won't be changed and the ID number will remain appended.
end try
repeat with _child in _tag's tags
my removeCollectionIDs(_child)
end repeat
end tell
end removeCollectionIDs
on getNote(_id)
set _note to ""
set _note to my appendField(_note, "Amount", _id, "ZAMOUNTASSTRING")
set _note to my appendField(_note, "Shipping", _id, "ZSHIPPINGAMOUNTASSTRING")
set _note to my appendField(_note, "Shipping", _id, "ZSHIPPINGAMOUNTSTRING")
set _note to my appendField(_note, "Tax", _id, "ZTAXAMOUNT2ASSTRING")
set _note to my appendField(_note, "Tax", _id, "ZTAXAMOUNTASSTRING")
set _note to my appendField(_note, "Tax", _id, "ZTAXAMOUNTSTRING2")
set _note to my appendField(_note, "Tip", _id, "ZTIPAMOUNTASSTRING")
set _note to my appendField(_note, "Tip", _id, "ZTIPAMOUNTSTRING")
set _note to my appendField(_note, "Custom 1", _id, "ZCUSTOM1")
set _note to my appendField(_note, "Custom 2", _id, "ZCUSTOM2")
set _note to my appendField(_note, "Custom 3", _id, "ZCUSTOM3")
set _note to my appendField(_note, "Custom 4", _id, "ZCUSTOM4")
set _note to my appendField(_note, "Custom 5", _id, "ZCUSTOM5")
set _note to my appendField(_note, "Custom 6", _id, "ZCUSTOM6")
set _note to my appendField(_note, "Notes", _id, "ZNOTES")
set _note to my appendField(_note, "OCR", _id, "ZOCRRESULT")
end getNote
on appendField(_string, _title, _id, _column)
set _value to my getAttribute(_id, _column)
if _value is "" then return _string
if _value is "$0.00" and _column contains "AMOUNT" then return _string
if _title is not "" then
set _string to _string & _title & ": "
end if
set _string to _string & _value
set _string to _string & return
return _string
end appendField
on sqlite(_sql)
global _databasePath
set _script to "sqlite3 " & _databasePath's quoted form & " " & _sql's quoted form
try
return do shell script _script
on error _error
error _error & return & "SQL:" & _sql
end try
end sqlite
on getFolder(_folderRecord, _name)
tell application "EagleFiler"
set _name to my makeFileNameValid(_name)
try
return library record _name of _folderRecord
on error
tell _folderRecord's library document
return add folder name _name container _folderRecord
end tell
end try
end tell
end getFolder
on makeFileNameValid(_name)
set _name to my replace(_name, ":", ":") -- "FULLWIDTH COLON" U+FF1A
set _name to my replace(_name, "/", ":")
set _limit to 255
set _extension to my pathExtension(_name)
set _basename to my getBasename(_name)
set _basenameLimit to _limit - ((length of _extension) + 1)
set _basename to my prefix(_basename, _basenameLimit)
if _extension is "" then return _basename
set _result to _basename & "." & _extension
set _result to my replace(_name, ":", "/")
return _result
end makeFileNameValid
on getBasename(_path) -- Terminology conflict without the "get"
set _nsString to current application's NSString's stringWithString:_path
return _nsString's lastPathComponent's stringByDeletingPathExtension as Unicode text
end getBasename
on pathExtension(_path)
set _nsString to current application's NSString's stringWithString:_path
return _nsString's lastPathComponent's pathExtension as Unicode text
end pathExtension
on replace(_string, _source, _replacement)
return my join(my split(_string, _source), _replacement)
end replace
on join(_list, _sep)
set _temp to AppleScript's text item delimiters
set AppleScript's text item delimiters to _sep
set _result to _list as string
set AppleScript's text item delimiters to _temp
return _result
end join
on split(_string, _sep)
set _temp to AppleScript's text item delimiters
set AppleScript's text item delimiters to _sep
set _result to text items of _string
set AppleScript's text item delimiters to _temp
return _result
end split
on prefix(_string, _length)
if length of _string ≤ _length then return _string
my join(characters 1 thru _string of _basename, "")
end prefix