@engine9/schemas 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -0
- package/.prettierrc +7 -0
- package/AGENTS.md +10 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/channels/email/README.md +83 -0
- package/channels/email/index.js +22 -0
- package/channels/email/search.js +86 -0
- package/channels/email/segments.js +81 -0
- package/eslint.config.js +21 -0
- package/event/README.md +79 -0
- package/event/index.js +17 -0
- package/event/schema.js +111 -0
- package/event/search.js +51 -0
- package/message/index.js +8 -0
- package/message/schema.js +130 -0
- package/message/ui.console.json5 +233 -0
- package/package.json +26 -0
- package/person/index.js +100 -0
- package/person/metrics.js +17 -0
- package/person/schema.js +47 -0
- package/person/transforms/inbound/normalize_field_names.js +16 -0
- package/person/transforms/inbound/upsert_tables.js +21 -0
- package/person/transforms/outbound/appendName.js +32 -0
- package/person/transforms/simpleMap.js +34 -0
- package/person/ui.console.json5 +165 -0
- package/person/upsert.test.js +72 -0
- package/person_address/index.js +30 -0
- package/person_address/normalize.js +232 -0
- package/person_address/normalize.test.js +69 -0
- package/person_address/schema.js +49 -0
- package/person_address/transforms/inbound/normalize.js +23 -0
- package/person_address/transforms/inbound/upsert_tables.js +51 -0
- package/person_address/transforms/outbound/appendAddress.js +87 -0
- package/person_address/ui.console.json5 +31 -0
- package/person_custom/index.js +24 -0
- package/person_custom/transforms/appendRemoteId.js +31 -0
- package/person_custom/transforms/inbound/upsert_tables.js +31 -0
- package/person_email/README.md +82 -0
- package/person_email/index.js +35 -0
- package/person_email/schema.js +49 -0
- package/person_email/search.js +61 -0
- package/person_email/segments.js +16 -0
- package/person_email/transforms/inbound/extract_identifiers.js +33 -0
- package/person_email/transforms/inbound/upsert_tables.js +142 -0
- package/person_email/transforms/outbound/appendEmail.js +32 -0
- package/person_email/transforms/outbound/appendEmailHash.js +20 -0
- package/person_email/transforms/outbound/tables.js +14 -0
- package/person_email/ui.console.json5 +58 -0
- package/person_email/upsert.test.js +126 -0
- package/person_hash/README.md +70 -0
- package/person_hash/hash.js +103 -0
- package/person_hash/hash.test.js +165 -0
- package/person_hash/index.js +37 -0
- package/person_hash/schema.js +54 -0
- package/person_hash/search.js +86 -0
- package/person_hash/transforms/inbound/extract_identifiers.js +70 -0
- package/person_hash/transforms/inbound/upsert_tables.js +105 -0
- package/person_hash/transforms/outbound/appendEmailHash.js +42 -0
- package/person_hash/transforms/outbound/appendPhoneHash.js +42 -0
- package/person_hash/ui.console.json5 +80 -0
- package/person_phone/README.md +79 -0
- package/person_phone/index.js +33 -0
- package/person_phone/schema.js +58 -0
- package/person_phone/search.js +79 -0
- package/person_phone/segments.js +26 -0
- package/person_phone/transforms/inbound/extract_identifiers.js +59 -0
- package/person_phone/transforms/inbound/upsert_tables.js +119 -0
- package/person_phone/transforms/outbound/appendPhoneHash.js +37 -0
- package/person_phone/ui.console.json5 +31 -0
- package/person_phone/upsert.test.js +50 -0
- package/person_remote/index.js +58 -0
- package/person_remote/metrics.js +17 -0
- package/person_remote/schema.js +21 -0
- package/person_remote/transforms/inbound/extract_identifiers.js +34 -0
- package/person_remote/transforms/inbound/upsert_tables.js +92 -0
- package/person_remote/transforms/outbound/appendRemotePersonId.js +59 -0
- package/plugin/index.js +13 -0
- package/plugin/schema.js +70 -0
- package/plugin/settings.js +19 -0
- package/plugin/ui.console.json5 +79 -0
- package/report/index.js +7 -0
- package/report/ui.console.json5 +21 -0
- package/segment/index.js +21 -0
- package/segment/schema.js +81 -0
- package/segment/search.js +64 -0
- package/segment/transforms/inbound/upsert.js +44 -0
- package/segment/ui.console.json5 +142 -0
- package/segment_stats/index.js +16 -0
- package/segment_stats/schema.js +30 -0
- package/source_code/index.js +16 -0
- package/source_code/metrics.js +28 -0
- package/source_code/schema.js +29 -0
- package/source_code/ui.console.json5 +31 -0
- package/stacks/limited-pii/index.js +8 -0
- package/stacks/limited-pii/stack.json +22 -0
- package/stacks/stacks.test.js +27 -0
- package/stacks/standard/index.js +7 -0
- package/stacks/standard/stack.json +20 -0
- package/tag/index.js +13 -0
- package/tag/schema.js +23 -0
- package/task/index.js +13 -0
- package/task/schema.js +54 -0
- package/task/ui.console.json5 +60 -0
- package/timeline/index.js +18 -0
- package/timeline/schema.js +18 -0
- package/timeline/transforms/inbound/upsert_tables.js +22 -0
- package/timeline/ui.console.json5 +31 -0
- package/transaction/contact_details/index.js +11 -0
- package/transaction/contact_details/schema.js +22 -0
- package/transaction/core/README.md +79 -0
- package/transaction/core/attribute.test.js +231 -0
- package/transaction/core/index.js +31 -0
- package/transaction/core/metrics.js +20 -0
- package/transaction/core/schema.js +45 -0
- package/transaction/core/search.js +87 -0
- package/transaction/core/segments.js +14 -0
- package/transaction/core/transforms/appendTransactionSummary.js +101 -0
- package/transaction/core/transforms/attribute.js +327 -0
- package/transaction/core/transforms/inbound/upsert_tables.js +57 -0
- package/utilities/limited-pii/index.js +14 -0
- package/utilities/limited-pii/settings.js +12 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
routes: {
|
|
3
|
+
'/person/:id/*': {
|
|
4
|
+
components: {
|
|
5
|
+
sidebar: {
|
|
6
|
+
items: [
|
|
7
|
+
{
|
|
8
|
+
component: 'Link',
|
|
9
|
+
properties: {
|
|
10
|
+
label: 'Timeline',
|
|
11
|
+
to: 'timeline'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
main: [
|
|
17
|
+
{
|
|
18
|
+
path: 'timeline',
|
|
19
|
+
label: 'Timeline',
|
|
20
|
+
component: 'RecordDisplay',
|
|
21
|
+
properties: {
|
|
22
|
+
table: 'timeline',
|
|
23
|
+
conditions: [{ eql: 'person_id={{person_id}}' }],
|
|
24
|
+
rows: ['{{email}}']
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@engine9/schemas/stacks/limited-pii",
|
|
3
|
+
"description": "PII-minimized Engine9 stack: hash identifiers instead of plaintext email, phone, or postal address. Excludes person_email, person_phone, and person_address even if requested.",
|
|
4
|
+
"version": "1.1.0",
|
|
5
|
+
"unique": true,
|
|
6
|
+
"include": [
|
|
7
|
+
"@engine9/schemas/plugin",
|
|
8
|
+
"@engine9/schemas/person",
|
|
9
|
+
"@engine9/schemas/person_remote",
|
|
10
|
+
"@engine9/schemas/segment",
|
|
11
|
+
"@engine9/schemas/timeline",
|
|
12
|
+
"@engine9/schemas/source_code",
|
|
13
|
+
"@engine9/schemas/transaction/core",
|
|
14
|
+
"@engine9/schemas/transaction/contact_details",
|
|
15
|
+
"@engine9/schemas/person_hash"
|
|
16
|
+
],
|
|
17
|
+
"exclude": [
|
|
18
|
+
"@engine9/schemas/person_email",
|
|
19
|
+
"@engine9/schemas/person_phone",
|
|
20
|
+
"@engine9/schemas/person_address"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { metadata as standard } from './standard/index.js';
|
|
4
|
+
import { metadata as limitedPii } from './limited-pii/index.js';
|
|
5
|
+
|
|
6
|
+
test('standard stack includes plaintext contact plugins and does not exclude them', () => {
|
|
7
|
+
assert.equal(standard.name, '@engine9/schemas/stacks/standard');
|
|
8
|
+
assert.ok(standard.include.includes('@engine9/schemas/person_email'));
|
|
9
|
+
assert.ok(standard.include.includes('@engine9/schemas/person_phone'));
|
|
10
|
+
assert.ok(standard.include.includes('@engine9/schemas/person_address'));
|
|
11
|
+
assert.ok(standard.include.includes('@engine9/schemas/timeline'));
|
|
12
|
+
assert.ok(standard.include.includes('@engine9/schemas/source_code'));
|
|
13
|
+
assert.ok(standard.include.includes('@engine9/schemas/transaction/core'));
|
|
14
|
+
assert.ok(!standard.include.includes('@engine9/plugins/reports/people'));
|
|
15
|
+
assert.deepEqual(standard.exclude, []);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('limited-pii stack includes person_hash and excludes plaintext contact plugins', () => {
|
|
19
|
+
assert.equal(limitedPii.name, '@engine9/schemas/stacks/limited-pii');
|
|
20
|
+
assert.ok(limitedPii.include.includes('@engine9/schemas/person_hash'));
|
|
21
|
+
assert.ok(!limitedPii.include.includes('@engine9/schemas/person_email'));
|
|
22
|
+
assert.ok(!limitedPii.include.includes('@engine9/schemas/person_phone'));
|
|
23
|
+
assert.ok(!limitedPii.include.includes('@engine9/schemas/person_address'));
|
|
24
|
+
assert.ok(limitedPii.exclude.includes('@engine9/schemas/person_email'));
|
|
25
|
+
assert.ok(limitedPii.exclude.includes('@engine9/schemas/person_phone'));
|
|
26
|
+
assert.ok(limitedPii.exclude.includes('@engine9/schemas/person_address'));
|
|
27
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@engine9/schemas/stacks/standard",
|
|
3
|
+
"description": "Opt-in Engine9 schema stack: person, plaintext email, phone, and postal address, timeline, source codes, and transactions. Reports install separately.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"unique": true,
|
|
6
|
+
"include": [
|
|
7
|
+
"@engine9/schemas/plugin",
|
|
8
|
+
"@engine9/schemas/person",
|
|
9
|
+
"@engine9/schemas/person_remote",
|
|
10
|
+
"@engine9/schemas/segment",
|
|
11
|
+
"@engine9/schemas/person_email",
|
|
12
|
+
"@engine9/schemas/person_phone",
|
|
13
|
+
"@engine9/schemas/person_address",
|
|
14
|
+
"@engine9/schemas/timeline",
|
|
15
|
+
"@engine9/schemas/source_code",
|
|
16
|
+
"@engine9/schemas/transaction/core",
|
|
17
|
+
"@engine9/schemas/transaction/contact_details"
|
|
18
|
+
],
|
|
19
|
+
"exclude": []
|
|
20
|
+
}
|
package/tag/index.js
ADDED
package/tag/schema.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const tables = [
|
|
2
|
+
{
|
|
3
|
+
name: 'tag',
|
|
4
|
+
columns: {
|
|
5
|
+
id: 'uuid',
|
|
6
|
+
table_name: 'string',
|
|
7
|
+
tag_category: 'string',
|
|
8
|
+
tag: 'string'
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'tag_row',
|
|
13
|
+
columns: {
|
|
14
|
+
id: 'id',
|
|
15
|
+
tag_id: 'foreign_uuid',
|
|
16
|
+
row_id: 'id',
|
|
17
|
+
modified_at: 'modified_at'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
];
|
|
21
|
+
export default {
|
|
22
|
+
tables
|
|
23
|
+
};
|
package/task/index.js
ADDED
package/task/schema.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const tables = [
|
|
2
|
+
{
|
|
3
|
+
name: 'task_run',
|
|
4
|
+
columns: {
|
|
5
|
+
id: 'id_uuid',
|
|
6
|
+
account_id: 'string',
|
|
7
|
+
originator: { type: 'string', nullable: false, default_value: 'sql' },
|
|
8
|
+
flow_run_id: 'id_uuid',
|
|
9
|
+
task_key: 'string',
|
|
10
|
+
dynamic_key: { type: 'string', nullable: false, default_value: '0' },
|
|
11
|
+
name: 'string',
|
|
12
|
+
cache_key: 'string',
|
|
13
|
+
cache_expiration: 'datetime',
|
|
14
|
+
task_version: 'string',
|
|
15
|
+
empirical_policy: 'json',
|
|
16
|
+
tags: 'json',
|
|
17
|
+
labels: 'json',
|
|
18
|
+
state_id: 'id_uuid',
|
|
19
|
+
state_type: {
|
|
20
|
+
type: 'enum',
|
|
21
|
+
nullable: false,
|
|
22
|
+
default_value: 'PENDING',
|
|
23
|
+
values: ['PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED', 'CRASHED']
|
|
24
|
+
},
|
|
25
|
+
state_name: 'string',
|
|
26
|
+
state: 'json',
|
|
27
|
+
task_inputs: 'json',
|
|
28
|
+
run_count: 'int',
|
|
29
|
+
flow_run_run_count: 'int',
|
|
30
|
+
expected_start_time: 'datetime',
|
|
31
|
+
next_scheduled_start_time: 'datetime',
|
|
32
|
+
start_time: 'datetime',
|
|
33
|
+
end_time: 'datetime',
|
|
34
|
+
total_run_time: 'double',
|
|
35
|
+
estimated_run_time: 'double',
|
|
36
|
+
estimated_start_time_delta: 'double',
|
|
37
|
+
created_at: 'created_at',
|
|
38
|
+
modified_at: 'modified_at'
|
|
39
|
+
},
|
|
40
|
+
indexes: [
|
|
41
|
+
{ columns: 'id', primary: true },
|
|
42
|
+
{ columns: ['account_id'] },
|
|
43
|
+
{ columns: ['originator'] },
|
|
44
|
+
{ columns: ['flow_run_id'] },
|
|
45
|
+
{ columns: ['state_type'] },
|
|
46
|
+
{ columns: ['task_key'] },
|
|
47
|
+
{ columns: ['created_at'] }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export default {
|
|
53
|
+
tables
|
|
54
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
menu: {
|
|
3
|
+
'data-menu': {
|
|
4
|
+
title: 'Data',
|
|
5
|
+
icon: 'data',
|
|
6
|
+
type: 'collapse',
|
|
7
|
+
children: {
|
|
8
|
+
task: {
|
|
9
|
+
title: 'Tasks',
|
|
10
|
+
icon: 'forklift',
|
|
11
|
+
url: '/task'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
routes: {
|
|
17
|
+
task: {
|
|
18
|
+
layout: 'full_width',
|
|
19
|
+
components: {
|
|
20
|
+
header: ['Tasks'],
|
|
21
|
+
main: [
|
|
22
|
+
{
|
|
23
|
+
component: 'RecordTable',
|
|
24
|
+
properties: {
|
|
25
|
+
table: 'task_run',
|
|
26
|
+
columns: [
|
|
27
|
+
{ title: 'Id', dataIndex: 'id', sorter: true, width: '24%' },
|
|
28
|
+
{ title: 'Created', dataIndex: 'created_at', sorter: true, width: '18%' },
|
|
29
|
+
{ title: 'State', dataIndex: 'state_type', sorter: true, width: '14%' },
|
|
30
|
+
{ title: 'Task Key', dataIndex: 'task_key', sorter: true, width: '22%' },
|
|
31
|
+
{ title: 'Flow Run', dataIndex: 'flow_run_id', sorter: true, width: '22%' }
|
|
32
|
+
],
|
|
33
|
+
onRecord: {
|
|
34
|
+
onClick: {
|
|
35
|
+
action: 'navigate',
|
|
36
|
+
url: '/task/{{record.id}}'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
'task/:id/*': {
|
|
45
|
+
layout: 'full_width',
|
|
46
|
+
components: {
|
|
47
|
+
main: [
|
|
48
|
+
{
|
|
49
|
+
component: 'RecordForm',
|
|
50
|
+
properties: {
|
|
51
|
+
table: 'task_run',
|
|
52
|
+
title: 'Task Run {{record.id}}',
|
|
53
|
+
readOnly: true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import schema from './schema.js';
|
|
2
|
+
import upsert from './transforms/inbound/upsert_tables.js';
|
|
3
|
+
const metadata = {
|
|
4
|
+
name: '@engine9/schemas/timeline',
|
|
5
|
+
dependencies: {
|
|
6
|
+
'@engine9/schemas/person': '>=1.9.0'
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
export const transforms = {
|
|
10
|
+
upsert
|
|
11
|
+
};
|
|
12
|
+
export { metadata };
|
|
13
|
+
export { schema };
|
|
14
|
+
export default {
|
|
15
|
+
metadata,
|
|
16
|
+
schema,
|
|
17
|
+
transforms
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const tables = [
|
|
2
|
+
{
|
|
3
|
+
name: 'timeline',
|
|
4
|
+
columns: {
|
|
5
|
+
id: 'id_uuid',
|
|
6
|
+
ts: 'datetime',
|
|
7
|
+
input_id: 'uuid', // keep as uuid because it's required
|
|
8
|
+
entry_type_id: 'int',
|
|
9
|
+
person_id: 'person_id',
|
|
10
|
+
source_code_id: 'source_code_id',
|
|
11
|
+
created_at: 'created_at'
|
|
12
|
+
},
|
|
13
|
+
indexes: [{ columns: 'id', primary: true }, { columns: 'ts' }, { columns: 'person_id' }, { columns: 'input_id' }]
|
|
14
|
+
}
|
|
15
|
+
];
|
|
16
|
+
export default {
|
|
17
|
+
tables
|
|
18
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const bindings = {
|
|
2
|
+
tablesToUpsert: { path: 'sql.tables.upsert' }
|
|
3
|
+
};
|
|
4
|
+
export async function transform({ batch, tablesToUpsert }) {
|
|
5
|
+
tablesToUpsert.timeline = tablesToUpsert.timeline || [];
|
|
6
|
+
batch.forEach((o) => {
|
|
7
|
+
if (o.timeline_id) {
|
|
8
|
+
tablesToUpsert.timeline.push({
|
|
9
|
+
id: o.timeline_id,
|
|
10
|
+
ts: o.ts || new Date(),
|
|
11
|
+
input_id: o.input_id,
|
|
12
|
+
entry_type_id: o.entry_type_id,
|
|
13
|
+
person_id: o.person_id,
|
|
14
|
+
source_code_id: o.source_code_id
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export default {
|
|
20
|
+
bindings,
|
|
21
|
+
transform
|
|
22
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
routes: {
|
|
3
|
+
'/person/:id/*': {
|
|
4
|
+
components: {
|
|
5
|
+
sidebar: {
|
|
6
|
+
items: [
|
|
7
|
+
{
|
|
8
|
+
component: 'Link',
|
|
9
|
+
properties: {
|
|
10
|
+
label: 'Timeline',
|
|
11
|
+
to: 'timeline'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
main: [
|
|
17
|
+
{
|
|
18
|
+
path: 'timeline',
|
|
19
|
+
label: 'Timeline',
|
|
20
|
+
component: 'RecordDisplay',
|
|
21
|
+
properties: {
|
|
22
|
+
table: 'timeline',
|
|
23
|
+
conditions: [{ eql: 'person_id={{person_id}}' }],
|
|
24
|
+
rows: ['{{email}}']
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import schema from './schema.js';
|
|
2
|
+
|
|
3
|
+
const metadata = {
|
|
4
|
+
name: '@engine9/schemas/transaction/contact_details',
|
|
5
|
+
dependencies: {
|
|
6
|
+
'@engine9/schemas/transaction/core': '>=1.9.0'
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { metadata, schema };
|
|
11
|
+
export default { metadata, schema };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Contact details captured on a transaction: address, email, phone, employer, and occupation.
|
|
2
|
+
export const tables = [
|
|
3
|
+
{
|
|
4
|
+
name: 'transaction',
|
|
5
|
+
prefix: false,
|
|
6
|
+
columns: {
|
|
7
|
+
street_1: 'string',
|
|
8
|
+
street_2: 'string',
|
|
9
|
+
city: 'string',
|
|
10
|
+
region: 'string',
|
|
11
|
+
postal_code: { type: 'string', length: 16 },
|
|
12
|
+
country: 'string',
|
|
13
|
+
email: 'string',
|
|
14
|
+
phone: { type: 'string', length: 24 },
|
|
15
|
+
employer: 'string',
|
|
16
|
+
occupation: 'string'
|
|
17
|
+
},
|
|
18
|
+
indexes: []
|
|
19
|
+
}
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export default { tables };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Transaction Core Schema Plugin
|
|
2
|
+
|
|
3
|
+
The `@engine9/schemas/transaction/core` plugin stores payment and gift transactions for people and provides the common transaction search, metrics, and segment used by imports, reports, and exports.
|
|
4
|
+
|
|
5
|
+
It depends on `@engine9/schemas/person` because every transaction belongs to a person through `person_id`.
|
|
6
|
+
|
|
7
|
+
## Data Model
|
|
8
|
+
|
|
9
|
+
The plugin creates the `transaction` table. Each row is a dated amount tied to a person, an input, and optional source-code / message attribution.
|
|
10
|
+
|
|
11
|
+
| Field | Type | Description |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `id` | `id_uuid` | Primary row identifier. |
|
|
14
|
+
| `ts` | `datetime` | When the transaction occurred. |
|
|
15
|
+
| `input_id` | `uuid` | Input that produced the row. Required. |
|
|
16
|
+
| `entry_type_id` | `int` | Timeline entry type for the transaction. |
|
|
17
|
+
| `person_id` | `person_id` | Person the transaction is assigned to. |
|
|
18
|
+
| `amount` | `currency` | Transaction amount. |
|
|
19
|
+
| `remote_entry_id` | `string` | Remote system transaction id when available. |
|
|
20
|
+
| `remote_page_name` | `string` | Remote page or form name when available. |
|
|
21
|
+
| `remote_recurring_id` | `string` | Remote recurring-series id when available. |
|
|
22
|
+
| `recurs_id` | `int` | Recurring frequency. `0` is one-time. Defaults to `0`. |
|
|
23
|
+
| `recurring_number` | `int` | Sequence number in a recurring series. |
|
|
24
|
+
| `refund_amount` | `currency` | Refunded amount when applicable. |
|
|
25
|
+
| `given_name` / `family_name` / `email` | `string` | Snapshot of payer identity on the transaction. |
|
|
26
|
+
| `source_code_id` | `source_code_id` | Source code captured on the transaction. |
|
|
27
|
+
| `override_source_code_id` | `source_code_id` | Manual source-code override. |
|
|
28
|
+
| `final_source_code_id` | `source_code_id` | Source code after attribution. |
|
|
29
|
+
| `recommended_message_id` | `uuid` | Message suggested by last-click attribution. |
|
|
30
|
+
| `override_message_id` | `uuid` | Manual message override. |
|
|
31
|
+
| `final_message_id` | `uuid` | Message after attribution. |
|
|
32
|
+
| `extra` | `json` | Additional payload from the source system. |
|
|
33
|
+
| `created_at` | `created_at` | Timestamp when the row was created. |
|
|
34
|
+
| `modified_at` | `modified_at` | Timestamp when the row was last modified. |
|
|
35
|
+
|
|
36
|
+
Indexes:
|
|
37
|
+
|
|
38
|
+
- primary `id`
|
|
39
|
+
- `ts`
|
|
40
|
+
- `person_id`
|
|
41
|
+
- `remote_entry_id`
|
|
42
|
+
- `modified_at`
|
|
43
|
+
|
|
44
|
+
## Inbound Behavior
|
|
45
|
+
|
|
46
|
+
The inbound `upsert` transform writes rows to `transaction`.
|
|
47
|
+
|
|
48
|
+
- Each row gets an `id` from the timeline-entry UUID helper and an `entry_type_id` from the entry type.
|
|
49
|
+
- When recurring fields are present, `recurs_id` is derived from `recurs` (`daily` through `annually`) or from `recurring_number` when no frequency is set.
|
|
50
|
+
|
|
51
|
+
## Outbound Behavior
|
|
52
|
+
|
|
53
|
+
`appendTransactionSummary` looks up a person's transactions and attaches summary fields such as count, revenue, first/last/smallest/largest gifts, and recurring totals.
|
|
54
|
+
|
|
55
|
+
## Search
|
|
56
|
+
|
|
57
|
+
- `all` finds people who have any transaction, optionally filtered by `plugin_id` and a `start` / `end` window on `ts` (ISO datetimes or relative expressions such as `-30d`).
|
|
58
|
+
- `minimum` finds people who have a transaction of at least a given `amount`.
|
|
59
|
+
|
|
60
|
+
## Metrics
|
|
61
|
+
|
|
62
|
+
`summaryByDate` is a monthly card: record count and summed `amount` grouped by month of `ts`.
|
|
63
|
+
|
|
64
|
+
## Segments
|
|
65
|
+
|
|
66
|
+
Predefined audiences shipped with this schema plugin. On install, each key becomes a `segment` row whose definition path is `@engine9/schemas/transaction/core:segments:<key>`.
|
|
67
|
+
|
|
68
|
+
### Customers
|
|
69
|
+
|
|
70
|
+
| | |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| **Key** | `customers` |
|
|
73
|
+
| **Definition path** | `@engine9/schemas/transaction/core:segments:customers` |
|
|
74
|
+
| **Who is included** | People who have at least one row in `transaction` |
|
|
75
|
+
| **Who is excluded** | People with no transactions |
|
|
76
|
+
| **How it is built** | A `transaction` table search that selects `person_id` with no extra filters |
|
|
77
|
+
| **Universe** | None — any transaction from any plugin, date, or amount counts |
|
|
78
|
+
|
|
79
|
+
Refunds, recurring gifts, and $0 rows are not filtered out. If a person has ever appeared on a transaction row, they are a customer.
|