@jigx/mdk
Version:
Jigx Mobile Development Kit - SDK for building Jigx applications
1,556 lines • 189 kB
JSON
{
"name": "expenses-app-lukas",
"title": "Expenses",
"category": "field-services",
"expressions": {
"hasItems": "=function($items)\n{\n $type($items) = 'array' or $count($items) > 0\n} \n"
},
"tabs": {
"home": {
"jigId": "home",
"icon": "receipt-dollar",
"label": {
"id": "expenses"
}
}
},
"onLoad": {
"type": "action.execute-action",
"options": {
"action": "action-sync-data"
}
},
"databases": {
"default": {
"tables": {
"weather-icons": null,
"logged-time": null,
"timesheets": null,
"expenseItemIcons": null,
"feature-settings": null
},
"databaseId": "default"
}
},
"datasources": {
"data-deleted-expense-receipts": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
"expense-receipts",
"expense-receipts-not-deleted"
],
"query": "SELECT id\nFROM [expense-receipts] AS p\nWHERE NOT EXISTS (\n SELECT 1\n FROM [expense-receipts-not-deleted] AS np\n WHERE p.id = np.id\n)\n"
}
},
"data-expense-receipt-max-modified": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "expense-receipts"
}
],
"query": "SELECT max(json_extract(data, '$.LastModifiedDateTime.value')) as LastModifiedDateTime FROM [expense-receipts]"
}
},
"data-global-expenses": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
"expense-receipts"
],
"jsonProperties": [
"files"
],
"query": "SELECT id, json_extract(data, '$.files') as files\nFROM [expense-receipts] WHERE json_array_length(files) > 0\n"
}
},
"data-loading-progress": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "progress"
}
],
"query": "SELECT \nid\n,'$.stage'\n,'$.percentage'\n,'$.step'\n,'$.max'\nFROM\nprogress\n"
}
},
"data-solution-features": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_DYNAMIC",
"entities": [
"default/feature-settings"
],
"query": "SELECT id, '$.CorporateCard', '$.CostCode', '$.Project' FROM [default/feature-settings] LIMIT 1"
}
},
"data-who-am-i": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "whoami"
}
],
"query": "SELECT \n '$.employeeID',\n '$.fullName'\nFROM \n [whoami]\n"
}
},
"errorqueue": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": [
"_commandQueue"
]
}
],
"query": "SELECT\nid,\njson_extract(payload, '$.functionId') AS functionId,\njson_extract(payload, '$.data.id') AS appointmentLineItemId,\njson_extract(payload, '$.data.LineNbr') AS lineNbr,\njson_extract(payload, '$.correlationId') as correlationId,\njson_extract(payload, '$.entity') as entity,\njson_extract(payload, '$.data') as functionData,\n[payload],\n[type],\n[queue],\n[state],\n[error]\nFROM\n[_commandQueue]\nwhere state = 'failed' \nand error is not null\n"
}
},
"processqueue": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": [
"_commandQueue"
]
}
],
"query": "SELECT\nid,\njson_extract(payload, '$.functionId') AS functionId,\njson_extract(payload, '$.data.id') AS appointmentLineItemId,\njson_extract(payload, '$.data.LineNbr') AS lineNbr,\njson_extract(payload, '$.correlationId') as correlationId,\njson_extract(payload, '$.entity') as entity,\njson_extract(payload, '$.data') as functionData,\n[payload],\n[type],\n[queue],\n[state],\n[error]\nFROM\n[_commandQueue]\nwhere state != 'failed' \n"
}
},
"user-roles": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "userRoles"
}
],
"query": "SELECT id, '$.roleName' FROM [userRoles]"
}
}
},
"functions": {
"rest-get-corp-cards": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{oDataBaseUrl}JIGXEmployeeCorpCard",
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"$format": {
"location": "query",
"type": "string",
"value": "json",
"required": false
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
}
},
"outputTransform": "$.value.{\n \"CorporateCardID\" : CorporateCardID,\n \"EmployeeID\" : $trim(EmployeeID),\n \"EmployeeName\" : EmployeeName,\n \"CardNumber\" : CardNumber\n }",
"error": [
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 500",
"icon": "on-error-sad",
"title": "Something Went Wrong",
"description": "\"It seems like something went wrong when trying to get your user information.\nPlease screenshot and email to markus@jigx.com\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 503",
"icon": "on-error-sad",
"title": "Server is temporarily unavailable",
"description": "\"The server is currently unavailable, please try again in a few minutes.\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
}
]
},
"rest-get-uploaded-files": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{oDataBaseUrlv4}PX_SM_UploadFileRevisionNoData",
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"$format": {
"location": "query",
"type": "string",
"value": "json",
"required": false
},
"oDataBaseUrlv4": {
"type": "string",
"location": "path",
"required": true
}
},
"outputTransform": "$.value",
"error": [
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 500",
"icon": "on-error-sad",
"title": "Something Went Wrong",
"description": "\"It seems like something went wrong when trying to get your user information.\nPlease screenshot and email to markus@jigx.com\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 503",
"icon": "on-error-sad",
"title": "Server is temporarily unavailable",
"description": "\"The server is currently unavailable, please try again in a few minutes.\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
}
]
},
"rest-get-user-roles": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{oDataBaseUrl}JIGXUserRoles",
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"$format": {
"location": "query",
"type": "string",
"value": "json",
"required": false
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
}
},
"outputTransform": "$.value.{\n \"login\" : Login,\n \"email\" : Email,\n \"employeeID\" : $trim(EmployeeID),\n \"roleName\" : RoleName\n }",
"error": [
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 500",
"icon": "on-error-sad",
"title": "Something Went Wrong",
"description": "\"It seems like something went wrong when trying to get your user information.\nPlease screenshot and email to markus@jigx.com\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 503",
"icon": "on-error-sad",
"title": "Server is temporarily unavailable",
"description": "\"The server is currently unavailable, please try again in a few minutes.\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
}
]
},
"rest-whoami": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{oDataBaseUrl}JIGXWhoAmI",
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"$format": {
"location": "query",
"type": "string",
"value": "json",
"required": false
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
}
},
"outputTransform": "$.value.{\n \"login\" : Login,\n \"email\" : Email,\n \"employeeID\" : $trim(EmployeeID),\n \"fullName\" : FullName\n }",
"error": [
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 500",
"icon": "on-error-sad",
"title": "Something Went Wrong",
"description": "\"It seems like something went wrong when trying to get your user information.\nPlease screenshot and email to markus@jigx.com\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 503",
"icon": "on-error-sad",
"title": "Server is temporarily unavailable",
"description": "\"The server is currently unavailable, please try again in a few minutes.\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
}
]
},
"rest-delete-expense": {
"provider": "DATA_PROVIDER_REST",
"url": "https://{acumaticaurl}ExpenseReceipt/{expId}",
"method": "DELETE",
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"expId": {
"type": "string",
"location": "path",
"required": true
},
"acumaticaurl": {
"type": "string",
"location": "path",
"required": true
}
},
"error": [
{
"when": "=(@ctx.response.status = 400) or (@ctx.response.status = 404)",
"title": "Bad Request",
"description": "Please screenshot and email support at markus@jigx.com",
"notification": true,
"details": "=@ctx.response.body",
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@.response.status = 500",
"title": "Internal error",
"description": "Internal error",
"notification": false
},
{
"when": "=@ctx.response.status = 504",
"title": "504 Retry error",
"description": "Retry",
"icon": "button-loop-1",
"retry": {
"delay": 16000,
"maxRetries": 4
},
"notification": true
},
{
"when": "=@ctx.response.status = 503",
"icon": "on-error-sad",
"title": "Server is temporarily unavailable",
"description": "\"The server is currently unavailable, please try again in a few minutes.\"\n",
"details": "=@ctx.response.body",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 429",
"retry": {
"delay": 16000,
"maxRetries": 4
},
"notification": true,
"description": "Too many requests",
"table": "diff-sync-errors",
"transform": "=$"
}
]
},
"rest-get-expense-inventory-item": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{oDataBaseUrl}JIGXExpenseInventory?$format=json",
"useLocalCall": true,
"records": "=$.data",
"outputTransform": "={\n \"top\": @ctx.parameters.\"$top\",\n \"skip\": @ctx.parameters.\"$skip\",\n \"data\" : @ctx.response.body ? $map(@ctx.response.body.value, function($item){$merge([$item, {\"id\": $item.InventoryID}])}) : []\n}",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"$format": {
"type": "string",
"location": "query",
"required": false,
"value": "json"
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
},
"$filter": {
"type": "string",
"location": "query",
"required": false
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 500
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": 0
}
},
"continuation": {
"when": "=$count(@ctx.output.data) = $number(@ctx.output.top)",
"url": "https://{oDataBaseUrl}JIGXExpenseInventory?$format=json",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 500
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": "=$number(@ctx.output.skip) + 500"
},
"$filter": {
"type": "string",
"location": "query",
"required": false
}
}
},
"error": [
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
}
]
},
"rest-get-expense-projects": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{oDataBaseUrl}JIGXExpProjectDetails",
"useLocalCall": true,
"records": "=$.data",
"outputTransform": "={\n \"top\": @ctx.parameters.\"$top\",\n \"skip\": @ctx.parameters.\"$skip\",\n \"data\" : @ctx.response.body.value\n}",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"$format": {
"type": "string",
"location": "query",
"required": false,
"value": "json"
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
},
"$filter": {
"type": "string",
"location": "query",
"required": false
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 1000
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": 0
}
},
"continuation": {
"when": "=$count(@ctx.output.data) = $number(@ctx.output.top)",
"url": "https://{oDataBaseUrl}JIGXExpProjectDetails",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 1000
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": "=$number(@ctx.output.skip) + 1000"
},
"$filter": {
"type": "string",
"location": "query",
"required": false
}
}
},
"error": [
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
}
]
},
"rest-get-expense-receipt-check": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"useLocalCall": true,
"url": "https://{acumaticaurl}ExpenseReceipt/{id}",
"records": "=$.data",
"forRowsWithMatchingIds": true,
"outputTransform": "={\n \"top\": @ctx.parameters.\"$top\",\n \"skip\": @ctx.parameters.\"$skip\",\n \"data\" : @ctx.response.body ? $map(@ctx.response.body, function($item){$merge([$item, { \"Remote\": true }])}) : []\n}",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"acumaticaurl": {
"type": "string",
"location": "path",
"required": true
},
"id": {
"type": "string",
"location": "path",
"required": false
}
},
"error": [
{
"when": "=$contains(@ctx.error.message, \"No credentials\")",
"notification": true,
"description": "No details for Acumatica oAuth credentials found.",
"table": "diff-sync-errors",
"transform": "=$",
"title": "No credentials for Acumatica",
"icon": "lock",
"details": "Please ensure your secondary credentials under your profile are logged in and connected to Acumatica."
},
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
}
]
},
"rest-get-expense-receipt": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"useLocalCall": true,
"url": "https://{acumaticaurl}ExpenseReceipt",
"records": "=$.data",
"forRowsWithMatchingIds": true,
"outputTransform": "={\n \"top\": @ctx.parameters.\"$top\",\n \"skip\": @ctx.parameters.\"$skip\",\n \"data\" : @ctx.response.body ? $map(@ctx.response.body, function($item){$merge([$item, { \"Remote\": true }])}) : []\n}",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"acumaticaurl": {
"type": "string",
"location": "path",
"required": true
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 500
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": 0
},
"$filter": {
"type": "string",
"location": "query",
"required": false
},
"$expand": {
"type": "string",
"location": "query",
"required": false,
"value": "ReceiptDetails,TaxDetails,Files"
}
},
"continuation": {
"when": "=$count(@ctx.output.data) = $number(@ctx.output.top)",
"url": "https://{acumaticaurl}ExpenseReceipt",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"acumaticaurl": {
"type": "string",
"location": "path",
"required": true
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 500
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": "=$number(@ctx.output.skip) + 500"
},
"$filter": {
"type": "string",
"location": "query",
"required": false
},
"$expand": {
"type": "string",
"location": "query",
"required": false,
"value": "ReceiptDetails,TaxDetails,Files"
}
}
},
"error": [
{
"when": "=$contains(@ctx.error.message, \"No credentials\")",
"notification": true,
"description": "No details for Acumatica oAuth credentials found.",
"table": "diff-sync-errors",
"transform": "=$",
"title": "No credentials for Acumatica",
"icon": "lock",
"details": "Please ensure your secondary credentials under your profile are logged in and connected to Acumatica."
},
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
}
]
},
"rest-get-file-content-plural": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{acumaticaurl}/files/{id}",
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"id": {
"type": "string",
"location": "path",
"required": true
},
"filename": {
"type": "string",
"location": "path",
"required": true
},
"acumaticaurl": {
"type": "string",
"location": "path",
"required": true
}
},
"format": "pdf",
"conversions": [
{
"property": "file",
"from": "buffer",
"to": "local-uri"
}
],
"outputTransform": "{\n \"id\" : $.inputs.id\n ,\"file\" : $.data\n ,\"filename\" : $substringAfter($.inputs.filename , '\\\\')\n}\n",
"forRowsWithValues": {
"id": "id"
}
},
"rest-get-not-deleted-expense-receipts": {
"provider": "DATA_PROVIDER_REST",
"method": "GET",
"url": "https://{oDataBaseUrl}JIGXExpenseReceiptsDeleted",
"useLocalCall": true,
"records": "=$.data",
"outputTransform": "={\n \"top\": @ctx.parameters.\"$top\",\n \"skip\": @ctx.parameters.\"$skip\",\n \"data\" : @ctx.response.body ? $map(@ctx.response.body.value, function($item){$merge([$item, {\"id\": $item.id}])}) : []\n}",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"$format": {
"type": "string",
"location": "query",
"required": false,
"value": "json"
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
},
"$filter": {
"type": "string",
"location": "query",
"required": false
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 500
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": 0
}
},
"continuation": {
"when": "=$count(@ctx.output.data) = $number(@ctx.output.top)",
"url": "https://{oDataBaseUrl}JIGXExpenseReceiptsDeleted",
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"oDataBaseUrl": {
"type": "string",
"location": "path",
"required": true
},
"$top": {
"type": "number",
"location": "query",
"required": true,
"value": 500
},
"$skip": {
"type": "number",
"location": "query",
"required": true,
"value": "=$number(@ctx.output.skip) + 500"
},
"$filter": {
"type": "string",
"location": "query",
"required": false
}
}
},
"error": [
{
"when": "=@ctx.response.status = 200 and @ctx.response.body = ''",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "No data returned from Acumatica",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@ctx.response.status = 504",
"retry": {
"delay": 5000,
"maxRetries": 4
},
"notification": false,
"description": "504 Gateway Timeout",
"table": "diff-sync-errors",
"transform": "=$"
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
}
]
},
"rest-put-expense-file": {
"provider": "DATA_PROVIDER_REST",
"url": "https://{acumaticaurl}files/PX.Objects.EP.ExpenseClaimDetailEntry/ClaimDetails/{expId}/{filename}",
"method": "PUT",
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"expId": {
"type": "string",
"location": "path",
"required": true
},
"Content-Type": {
"location": "header",
"required": true,
"type": "string",
"value": "image/jpeg"
},
"file": {
"location": "body",
"required": true,
"type": "image"
},
"filename": {
"location": "path",
"required": true,
"type": "string"
},
"acumaticaurl": {
"type": "string",
"location": "path",
"required": true
}
},
"conversions": [
{
"property": "file",
"from": "local-uri",
"to": "buffer"
}
],
"outputTransform": "={\n \"id\": $substringAfter(@ctx.response.headers.location, '/files/'),\n \"custId\": @ctx.parameters.custId\n}\n",
"guard": {
"function": "rest-get-expense-receipt-check",
"parameters": {
"accessToken": "acuerp",
"acumaticaurl": "=@ctx.solution.settings.custom.acumaticaurl",
"id": "=@ctx.parameters.expId"
},
"onInitial": true,
"result": "=@ctx.response.status = 200 ? false:true",
"operations": [
{
"type": "operation.execute-sql",
"tables": [
"customerfiles"
],
"statements": [
{
"statement": "DELETE FROM customerfiles\nWHERE json_extract(data , '$.expId') = @expId\n",
"parameters": {
"expId": "=@ctx.parameters.expId"
}
}
]
}
]
},
"error": [
{
"when": "=(@ctx.response.status = 400) or (@ctx.response.status = 404)",
"title": "Bad Request",
"description": "Please screenshot and email support at markus@jigx.com",
"notification": true,
"details": "=@ctx.response.body",
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 504",
"title": "504 Retry error",
"description": "Retry",
"icon": "button-loop-1",
"retry": {
"delay": 16000,
"maxRetries": 4
},
"notification": true
},
{
"when": "=@ctx.response.status = 503",
"icon": "on-error-sad",
"title": "Server is temporarily unavailable",
"description": "\"The server is currently unavailable, please try again in a few minutes.\"\n",
"details": "=@ctx.response.body",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 429",
"retry": {
"delay": 16000,
"maxRetries": 4
},
"notification": true,
"description": "Too many requests",
"table": "diff-sync-errors",
"transform": "=$"
}
]
},
"rest-save-expense-receipt": {
"provider": "DATA_PROVIDER_REST",
"method": "PUT",
"url": "https://{acumaticaurl}ExpenseReceipt?$expand=ReceiptDetails,TaxDetails,Files",
"forRowsWithMatchingIds": true,
"useLocalCall": true,
"parameters": {
"accessToken": {
"location": "header",
"required": true,
"type": "acuerp",
"value": "acuerp"
},
"acumaticaurl": {
"type": "string",
"location": "path",
"required": true
},
"msgBody": {
"type": "string",
"location": "body",
"required": true
}
},
"inputTransform": "=$sift(@ctx.parameters.msgBody, function($v, $k) { $k != \"id\" })",
"outputTransform": "=$merge([@ctx.response.body, { \"Remote\": true } ])",
"error": [
{
"when": "=@.response.status = 401",
"title": "Unauthorized",
"description": "Unauthorized",
"notification": true,
"retry": {
"maxRetries": 3,
"delay": 5000
}
},
{
"when": "=@ctx.response.status = 422",
"title": "Missing or incorrect information",
"description": "=\"Something went wrong when creating the Expense Receipt\"",
"details": "=$utilities.extractErrors(@ctx.response.body)",
"notification": true,
"table": "=@ctx.entity & \"_error\"",
"icon": "server-error-document",
"transform": "=@ctx.response.body"
},
{
"when": "=@ctx.response.status = 504",
"title": "504 Retry error",
"description": "Retry",
"icon": "button-loop-1",
"retry": {
"delay": 16000,
"maxRetries": 4
},
"notification": false
},
{
"when": "=@ctx.response.status = 500",
"icon": "on-error-sad",
"title": "Something Went Wrong",
"description": "\"It seems like something went wrong when trying to create expense receipt.\nPlease screenshot and email to markus@jigx.com\"\n",
"details": "=@ctx.response.body",
"notification": true
},
{
"when": "=@ctx.response.status = 503",
"icon": "on-error-sad",
"title": "Server is temporarily unavailable",
"description": "\"The server is currently unavailable, please try again in a few minutes.\"\n",
"details": "=@ctx.response.body",
"notification": true
}
]
}
},
"jigs": {
"jig-add-expense-receipt": {
"title": {
"id": "add-new-expense"
},
"type": "jig.default",
"datasources": {
"expense-receipts": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
"expense-receipts"
],
"query": "SELECT id\nFROM [expense-receipts] ORDER BY timestamp DESC LIMIT 1\n"
}
},
"ExpenseItems": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "expense-inventory-items"
}
],
"query": "SELECT trim(id) as id, data FROM [expense-inventory-items] order by id",
"jsonProperties": [
"data"
]
}
},
"ExpenseProjects": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
"expense-projects"
],
"query": "SELECT DISTINCT \n trim(json_extract(data, '$.ProjectID')) AS ProjectID,\n trim(json_extract(data, '$.ProjectDescription')) AS ProjectDescription,\n trim(json_extract(data, '$.CustomerID')) AS CustomerID,\n trim(json_extract(data, '$.Customer_Description')) AS Customer_Description,\n trim(json_extract(data, '$.CustomerLocation')) AS CustomerLocation,\n trim(json_extract(data, '$.Location_Descripion')) AS Location_Descripion\nFROM [expense-projects]\nWHERE json_extract(data, '$.ProjectStatus') = 'Active'\nAND json_extract(data, '$.Type') = 'Contract'\nand (\n json_extract(data, '$.ProjectID') LIKE '%' || @search || '%' OR\n json_extract(data, '$.ProjectDescription') LIKE '%' || @search || '%' OR\n json_extract(data, '$.CustomerID') LIKE '%' || @search || '%' OR\n json_extract(data, '$.Customer_Description') LIKE '%' || @search || '%' OR\n @search = '' OR @search IS NULL\n )\nORDER BY '$.ProjectID'\n",
"queryParameters": {
"search": "=@ctx.components.component-expense-receipt.outputs.projectSearch"
},
"jsonProperties": [
"data"
]
}
},
"projectTasks": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "expense-projects"
}
],
"query": "SELECT DISTINCT \n trim(json_extract(data, '$.TaskID')) AS TaskID,\n trim(json_extract(data, '$.TaskDescription')) AS TaskDescription\nFROM [expense-projects]\nWHERE trim(json_extract(data, '$.ProjectID')) = @projectID and TaskID IS NOT NULL\nORDER BY TaskID\n",
"queryParameters": {
"projectID": "=$trim(@ctx.components.component-expense-receipt.outputs.projectID)"
},
"jsonProperties": [
"data"
]
}
},
"costCodes": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "expense-projects"
}
],
"query": "SELECT DISTINCT \n trim(json_extract(data, '$.CostCode')) AS CostCode,\n trim(json_extract(data, '$.CostCodeDescription')) AS CostCodeDescription\nFROM [expense-projects]\nWHERE trim(json_extract(data, '$.ProjectID')) = @projectID\nAND trim(json_extract(data, '$.TaskID')) = @taskID\nORDER BY CostCode\n",
"queryParameters": {
"projectID": "=$trim(@ctx.components.component-expense-receipt.outputs.projectID)",
"taskID": "=$trim(@ctx.components.component-expense-receipt.outputs.projectTaskID)"
},
"jsonProperties": [
"data"
]
}
},
"paidWith": {
"type": "datasource.static",
"options": {
"data": [
{
"id": 1,
"title": "Personal Account"
},
{
"id": 2,
"title": "Corporate Card, Company Expense"
},
{
"id": 3,
"title": "Corporate Card, Personal Expense"
}
]
}
},
"corpCard": {
"type": "datasource.sqlite",
"options": {
"provider": "DATA_PROVIDER_LOCAL",
"entities": [
{
"entity": "corpCards"
}
],
"query": "SELECT id, '$.EmployeeID', '$.CorporateCardID','$.EmployeeName','$.CardNumber' FROM [corpCards] WHERE '$.EmployeeID' = @EmployeeID",
"queryParameters": {
"EmployeeID": "=$trim(@ctx.datasources.data-who-am-i.employeeID)"
}
}
}
},
"children": [
{
"type": "component.custom-component",
"componentId": "component-expense-receipt",
"instanceId": "component-expense-receipt",
"inputs": {
"ExpenseItems": "=@ctx.datasources.ExpenseItems",
"instanceId": "=@ctx.jig.instanceId",
"expenseProjects": "=@ctx.datasources.ExpenseProjects",
"projectTasks": "=@ctx.datasources.projectTasks",
"costCodes": "=@ctx.datasources.costCodes",
"newExpense": true,
"paidWith": "=@ctx.datasources.paidWith",
"corpCards": "=@ctx.datasources.corpCard"
}
}
],
"actions": [
{
"children": [
{
"type": "action.action-list",
"options": {
"title": "Save Expense Receipt",
"isSequential": true,
"style": {
"isDisabled": "=$not(@ctx.components.component-expense-receipt.outputs.errorState)"
},
"actions": [
{
"type": "action.set-state",
"options": {
"state": "=@ctx.solution.state.expenseFormRequired",
"value": false
}
},
{
"type": "action.execute-entity",
"options": {
"provider": "DATA_PROVIDER_REST",
"entity": "expense-receipts",
"method": "save",
"function": "rest-save-expense-receipt",
"queueOperation": "replace",
"functionParameters": {
"accessToken": "acuerp",
"acumaticaurl": "=@ctx.solution.settings.custom.acumaticaurl",
"msgBody": "=$expenses.generateExpenseReceiptJSON(@ctx.components.component-expense-receipt.outputs.expenseReceiptFormJSON)"
},
"data": "=$expenses.generateExpenseReceiptJSON(@ctx.components.component-expense-receipt.outputs.expenseReceiptFormJSON)"
}
},
{
"type": "action.execute-entities",
"when": "=(@ctx.components.component-expense-receipt.outputs.expenseImage) ? true : false\n",
"options": {
"provider": "DATA_PROVIDER_REST",
"entity": "customerfiles",
"method": "save",
"goBack": "stay",
"queueOperation": "replace",
"function": "rest-put-expense-file",
"data": "=$map(@ctx.components.component-expense-receipt.outputs.expenseImage, function($v, $i, $a) {\n $.{\n \"expId\" : @ctx.datasources.expense-receipts.id,\n \"Content-Type\" : \"image/jpeg\",\n \"acumaticaurl\" : @ctx.solution.settings.custom.AcumaticaExpURL,\n \"accessToken\" : acuerp,\n \"filename\" : $split($v, \"/\")[-1],\n \"file\" : $v\n }})[]\n"
}
},
{
"type": "action.go-back"
}
]
}
}
]
}
],
"summary": {
"children": {
"type": "component.summary",
"options": {
"layout": "default",
"leftIcon": {
"element": "icon",
"icon": "currency-dollar-circle",
"isDuotone": true
},
"title": {
"text": "=@ctx.components.component-expense-receipt.outputs.expenseReceiptFormJSON.ClaimAmount",
"format": {
"numberStyle": "currency",
"currency": "USD"
}
},
"subtitle": "Claim Amount"
}
}
}
},
"jig-edit-expense-receipt": {
"title": {
"id": "edit-expense"
},
"type": "jig.default",
"header": {
"type": "component.jig-header",
"options": {
"height": "small",
"actions": [
{
"type": "action.confirm",
"options": {
"title": "Delete",
"icon": "bin-1",
"isHidden": "=@ctx.jig.inputs.status != 'Released' or $not($exists(@ctx.jig.inputs.status)) ? @ctx.solution.state.expenseReceiptEditModeDisabled = false ? true:false:true",
"style": {
"isDanger": true
},
"isConfirmedAutomatically": false,
"