@forestadmin/datasource-demo-fintech 1.0.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/LICENSE +674 -0
- package/README.md +40 -0
- package/dist/customizations/aml_alerts.d.ts +3 -0
- package/dist/customizations/aml_alerts.js +135 -0
- package/dist/customizations/cards.d.ts +3 -0
- package/dist/customizations/cards.js +95 -0
- package/dist/customizations/chargebacks.d.ts +3 -0
- package/dist/customizations/chargebacks.js +271 -0
- package/dist/customizations/customers.d.ts +3 -0
- package/dist/customizations/customers.js +15 -0
- package/dist/customizations/kyc_cases.d.ts +3 -0
- package/dist/customizations/kyc_cases.js +105 -0
- package/dist/customizations/kyc_documents.d.ts +3 -0
- package/dist/customizations/kyc_documents.js +62 -0
- package/dist/customizations/refund_requests.d.ts +3 -0
- package/dist/customizations/refund_requests.js +151 -0
- package/dist/customizations/sar_reports.d.ts +3 -0
- package/dist/customizations/sar_reports.js +27 -0
- package/dist/customizations/utils.d.ts +21 -0
- package/dist/customizations/utils.js +22 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +97 -0
- package/dist/seed/aml.d.ts +9 -0
- package/dist/seed/aml.js +131 -0
- package/dist/seed/cards.d.ts +9 -0
- package/dist/seed/cards.js +57 -0
- package/dist/seed/chargebacks.d.ts +9 -0
- package/dist/seed/chargebacks.js +118 -0
- package/dist/seed/customers.d.ts +9 -0
- package/dist/seed/customers.js +106 -0
- package/dist/seed/dates.d.ts +7 -0
- package/dist/seed/dates.js +28 -0
- package/dist/seed/index.d.ts +9 -0
- package/dist/seed/index.js +39 -0
- package/dist/seed/kyc.d.ts +9 -0
- package/dist/seed/kyc.js +102 -0
- package/dist/seed/pools.d.ts +27 -0
- package/dist/seed/pools.js +296 -0
- package/dist/seed/refunds.d.ts +9 -0
- package/dist/seed/refunds.js +72 -0
- package/dist/seed/types.d.ts +17 -0
- package/dist/seed/types.js +5 -0
- package/dist/seed/utils.d.ts +16 -0
- package/dist/seed/utils.js +40 -0
- package/package.json +33 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = customizeKycCases;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
function badge(text, color) {
|
|
6
|
+
return `<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:${color};color:#fff;">${text}</span>`;
|
|
7
|
+
}
|
|
8
|
+
function riskBadge(rating) {
|
|
9
|
+
const colors = { LOW: '#16a34a', MEDIUM: '#d97706', HIGH: '#dc2626' };
|
|
10
|
+
return badge(rating || '—', colors[rating] || '#6b7280');
|
|
11
|
+
}
|
|
12
|
+
function flag(value, label) {
|
|
13
|
+
return value
|
|
14
|
+
? `<span style="color:#dc2626;font-weight:600;">⚠ ${label}</span>`
|
|
15
|
+
: '<span style="color:#6b7280;">— No</span>';
|
|
16
|
+
}
|
|
17
|
+
function row(label, value) {
|
|
18
|
+
return `<tr><td style="padding:6px 8px;color:#6b7280;font-size:12px;width:38%;">${label}</td><td style="padding:6px 8px;font-size:13px;">${value ?? '—'}</td></tr>`;
|
|
19
|
+
}
|
|
20
|
+
function customizeKycCases(collection) {
|
|
21
|
+
// Relationships
|
|
22
|
+
collection
|
|
23
|
+
.addManyToOneRelation('customer', 'customers', { foreignKey: 'customer_id' })
|
|
24
|
+
.addOneToManyRelation('documents', 'kyc_documents', { originKey: 'kyc_case_id' });
|
|
25
|
+
collection.addAction('Approve KYC', {
|
|
26
|
+
scope: 'Single',
|
|
27
|
+
execute: async (context, resultBuilder) => {
|
|
28
|
+
const caseId = await context.getRecordId();
|
|
29
|
+
const data = await context.getRecord([
|
|
30
|
+
'customer_id',
|
|
31
|
+
'opened_at',
|
|
32
|
+
'customer:first_name',
|
|
33
|
+
'customer:last_name',
|
|
34
|
+
'customer:email',
|
|
35
|
+
'customer:nationality',
|
|
36
|
+
'customer:risk_rating',
|
|
37
|
+
'customer:is_pep',
|
|
38
|
+
'customer:is_sanctioned',
|
|
39
|
+
]);
|
|
40
|
+
const c = data.customer ?? {};
|
|
41
|
+
await context.collection.update((0, utils_1.byId)(caseId), { status: 'APPROVED', resolved_at: (0, utils_1.now)() });
|
|
42
|
+
await context.dataSource
|
|
43
|
+
.getCollection('customers')
|
|
44
|
+
.update({ conditionTree: { field: 'id', operator: 'Equal', value: data.customer_id } }, { onboarding_status: 'APPROVED' });
|
|
45
|
+
const html = `
|
|
46
|
+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;max-width:520px;padding:4px;">
|
|
47
|
+
<div style="background:#16a34a;color:#fff;padding:12px 16px;border-radius:8px;margin-bottom:16px;">
|
|
48
|
+
<div style="font-size:16px;font-weight:700;">✓ KYC Approved — Onboarding Complete</div>
|
|
49
|
+
<div style="font-size:12px;opacity:0.85;">Customer onboarding status set to APPROVED.</div>
|
|
50
|
+
</div>
|
|
51
|
+
<table style="width:100%;border-collapse:collapse;background:#f9fafb;border-radius:6px;overflow:hidden;">
|
|
52
|
+
${row('Name', `<strong>${c.first_name ?? ''} ${c.last_name ?? ''}</strong>`)}
|
|
53
|
+
${row('Email', c.email)}
|
|
54
|
+
${row('Nationality', c.nationality)}
|
|
55
|
+
${row('Risk rating', riskBadge(c.risk_rating))}
|
|
56
|
+
${row('PEP', flag(c.is_pep, 'Politically Exposed Person'))}
|
|
57
|
+
${row('Sanctions', flag(c.is_sanctioned, 'On sanctions list'))}
|
|
58
|
+
${row('Case', `#${caseId} · opened ${String(data.opened_at ?? '').slice(0, 10)}`)}
|
|
59
|
+
${row('Outcome', badge('APPROVED', '#16a34a'))}
|
|
60
|
+
</table>
|
|
61
|
+
</div>`;
|
|
62
|
+
return resultBuilder.success('KYC Approved', { html });
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
collection.addAction('Reject KYC', {
|
|
66
|
+
scope: 'Single',
|
|
67
|
+
form: [
|
|
68
|
+
{
|
|
69
|
+
label: 'Rejection Reason',
|
|
70
|
+
type: 'String',
|
|
71
|
+
isRequired: true,
|
|
72
|
+
description: 'Briefly explain why the KYC case is being rejected.',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
execute: async (context, resultBuilder) => {
|
|
76
|
+
const caseId = await context.getRecordId();
|
|
77
|
+
await context.collection.update((0, utils_1.byId)(caseId), {
|
|
78
|
+
status: 'REJECTED',
|
|
79
|
+
rejection_reason: context.formValues['Rejection Reason'],
|
|
80
|
+
resolved_at: (0, utils_1.now)(),
|
|
81
|
+
});
|
|
82
|
+
return resultBuilder.success(`KYC rejected. Reason recorded: "${context.formValues['Rejection Reason']}".`);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
collection.addAction('Escalate KYC', {
|
|
86
|
+
scope: 'Single',
|
|
87
|
+
form: [
|
|
88
|
+
{
|
|
89
|
+
label: 'Escalation Reason',
|
|
90
|
+
type: 'String',
|
|
91
|
+
isRequired: true,
|
|
92
|
+
description: 'Explain why this case requires senior compliance review.',
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
execute: async (context, resultBuilder) => {
|
|
96
|
+
const caseId = await context.getRecordId();
|
|
97
|
+
await context.collection.update((0, utils_1.byId)(caseId), {
|
|
98
|
+
status: 'ESCALATED',
|
|
99
|
+
escalation_reason: context.formValues['Escalation Reason'],
|
|
100
|
+
});
|
|
101
|
+
return resultBuilder.success('Case escalated to senior compliance team for review.');
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia3ljX2Nhc2VzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2N1c3RvbWl6YXRpb25zL2t5Y19jYXNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQTBCQSxvQ0FxR0M7QUE3SEQsbUNBQW9DO0FBRXBDLFNBQVMsS0FBSyxDQUFDLElBQVksRUFBRSxLQUFhO0lBQ3hDLE9BQU8saUhBQWlILEtBQUssaUJBQWlCLElBQUksU0FBUyxDQUFDO0FBQzlKLENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxNQUFjO0lBQy9CLE1BQU0sTUFBTSxHQUEyQixFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFFOUYsT0FBTyxLQUFLLENBQUMsTUFBTSxJQUFJLEdBQUcsRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksU0FBUyxDQUFDLENBQUM7QUFDM0QsQ0FBQztBQUVELFNBQVMsSUFBSSxDQUFDLEtBQWMsRUFBRSxLQUFhO0lBQ3pDLE9BQU8sS0FBSztRQUNWLENBQUMsQ0FBQyxrREFBa0QsS0FBSyxTQUFTO1FBQ2xFLENBQUMsQ0FBQywwQ0FBMEMsQ0FBQztBQUNqRCxDQUFDO0FBRUQsU0FBUyxHQUFHLENBQUMsS0FBYSxFQUFFLEtBQWM7SUFDeEMsT0FBTywyRUFBMkUsS0FBSyxvREFDckYsS0FBSyxJQUFJLEdBQ1gsWUFBWSxDQUFDO0FBQ2YsQ0FBQztBQUVELFNBQXdCLGlCQUFpQixDQUN2QyxVQUFzRDtJQUV0RCxnQkFBZ0I7SUFDaEIsVUFBVTtTQUNQLG9CQUFvQixDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUUsRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLENBQUM7U0FDNUUsb0JBQW9CLENBQUMsV0FBVyxFQUFFLGVBQWUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO0lBRXBGLFVBQVUsQ0FBQyxTQUFTLENBQUMsYUFB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
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = customizeKycDocuments;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
const REJECTION_REASONS = [
|
|
6
|
+
'Image quality too low — please resubmit a clearer scan.',
|
|
7
|
+
'Document partially obscured — ensure all corners are visible.',
|
|
8
|
+
'Expiry date unreadable — please provide a higher-resolution image.',
|
|
9
|
+
'Glare detected on security features — retake under natural light.',
|
|
10
|
+
];
|
|
11
|
+
function panel(title, color, subtitle, rows) {
|
|
12
|
+
return `
|
|
13
|
+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;max-width:480px;padding:4px;">
|
|
14
|
+
<div style="background:${color};color:#fff;padding:12px 16px;border-radius:8px;margin-bottom:16px;">
|
|
15
|
+
<div style="font-size:16px;font-weight:700;">${title}</div>
|
|
16
|
+
<div style="font-size:12px;opacity:0.85;">${subtitle}</div>
|
|
17
|
+
</div>
|
|
18
|
+
<table style="width:100%;border-collapse:collapse;background:#f9fafb;border-radius:6px;overflow:hidden;">${rows}</table>
|
|
19
|
+
</div>`;
|
|
20
|
+
}
|
|
21
|
+
function row(label, value) {
|
|
22
|
+
return `<tr><td style="padding:6px 8px;color:#6b7280;font-size:12px;width:40%;">${label}</td><td style="padding:6px 8px;font-size:13px;">${value}</td></tr>`;
|
|
23
|
+
}
|
|
24
|
+
function badgeOutcome(text, color) {
|
|
25
|
+
return `<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;background:${color};color:#fff;">${text}</span>`;
|
|
26
|
+
}
|
|
27
|
+
function customizeKycDocuments(collection) {
|
|
28
|
+
// Relationships
|
|
29
|
+
collection
|
|
30
|
+
.addManyToOneRelation('customer', 'customers', { foreignKey: 'customer_id' })
|
|
31
|
+
.addManyToOneRelation('kycCase', 'kyc_cases', { foreignKey: 'kyc_case_id' });
|
|
32
|
+
collection.addAction('Verify Document', {
|
|
33
|
+
scope: 'Single',
|
|
34
|
+
execute: async (context, resultBuilder) => {
|
|
35
|
+
const docId = await context.getRecordId();
|
|
36
|
+
const doc = await context.getRecord(['document_type']);
|
|
37
|
+
const passed = Math.random() < 0.8;
|
|
38
|
+
const scanRef = `SCAN-${Date.now().toString(36).toUpperCase()}`;
|
|
39
|
+
const docLabel = String(doc.document_type).replace(/_/g, ' ');
|
|
40
|
+
if (passed) {
|
|
41
|
+
await context.collection.update((0, utils_1.byId)(docId), { verified: true, verified_at: (0, utils_1.now)() });
|
|
42
|
+
return resultBuilder.success('Document verified', {
|
|
43
|
+
html: panel('✓ Document Verified', '#16a34a', 'Automated scan completed successfully.', [
|
|
44
|
+
row('Document type', docLabel),
|
|
45
|
+
row('Scan reference', `<span style="font-family:monospace;">${scanRef}</span>`),
|
|
46
|
+
row('Outcome', badgeOutcome('VERIFIED', '#16a34a')),
|
|
47
|
+
].join('')),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const reason = REJECTION_REASONS[Math.floor(Math.random() * REJECTION_REASONS.length)];
|
|
51
|
+
return resultBuilder.success('Verification failed', {
|
|
52
|
+
html: panel('⚠ Verification Failed', '#d97706', 'The document could not be verified automatically.', [
|
|
53
|
+
row('Document type', docLabel),
|
|
54
|
+
row('Scan reference', `<span style="font-family:monospace;">${scanRef}</span>`),
|
|
55
|
+
row('Reason', `<span style="color:#b45309;">${reason}</span>`),
|
|
56
|
+
row('Outcome', badgeOutcome('NOT VERIFIED', '#d97706')),
|
|
57
|
+
].join('')),
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia3ljX2RvY3VtZW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jdXN0b21pemF0aW9ucy9reWNfZG9jdW1lbnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBOEJBLHdDQW9EQztBQWhGRCxtQ0FBb0M7QUFFcEMsTUFBTSxpQkFBaUIsR0FBRztJQUN4Qix5REFBeUQ7SUFDekQsK0RBQStEO0lBQy9ELG9FQUFvRTtJQUNwRSxtRUFBbUU7Q0FDcEUsQ0FBQztBQUVGLFNBQVMsS0FBSyxDQUFDLEtBQWEsRUFBRSxLQUFhLEVBQUUsUUFBZ0IsRUFBRSxJQUFZO0lBQ3pFLE9BQU87OytCQUVzQixLQUFLO3VEQUNtQixLQUFLO29EQUNSLFFBQVE7O2lIQUVxRCxJQUFJO1dBQzFHLENBQUM7QUFDWixDQUFDO0FBRUQsU0FBUyxHQUFHLENBQUMsS0FBYSxFQUFFLEtBQWE7SUFDdkMsT0FBTywyRUFBMkUsS0FBSyxvREFBb0QsS0FBSyxZQUFZLENBQUM7QUFDL0osQ0FBQztBQUVELFNBQVMsWUFBWSxDQUFDLElBQVksRUFBRSxLQUFhO0lBQy9DLE9BQU8saUhBQWlILEtBQUssaUJBQWlCLElBQUksU0FBUyxDQUFDO0FBQzlKLENBQUM7QUFFRCxTQUF3QixxQkFBcUIsQ0FDM0MsVUFBMEQ7SUFFMUQsZ0JBQWdCO0lBQ2hCLFVBQVU7U0FDUCxvQkFBb0IsQ0FBQyxVQUFVLEVBQUUsV0FBVyxFQUFFLEVBQUUsVUFBVSxFQUFFLGFBQWEsRUFBRSxDQUFDO1NBQzVFLG9CQUFvQixDQUFDLFNBQVMsRUFBRSxXQUFXLEVBQUUsRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztJQUUvRSxVQUFVLENBQUMsU0FBUyxDQUFDLGlCQUFpQixFQUFFO1FBQ3RDLEtBQUssRUFBRSxRQUFRO1FBQ2YsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsYUFBYSxFQUFFLEVBQUU7WUFDeEMsTUFBTSxLQUFLLEdBQUcsTUFBTSxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDMUMsTUFBTSxHQUFHLEdBQUcsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQztZQUV2RCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDO1lBQ25DLE1BQU0sT0FBTyxHQUFHLFFBQVEsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDO1lBQ2hFLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztZQUU5RCxJQUFJLE1BQU0sRUFBRSxDQUFDO2dCQUNYLE1BQU0sT0FBTyxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsSUFBQSxZQUFJLEVBQUMsS0FBSyxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxJQUFBLFdBQUcsR0FBRSxFQUFFLENBQUMsQ0FBQztnQkFFckYsT0FBTyxhQUFhLENBQUMsT0FBTyxDQUFDLG1CQUFtQixFQUFFO29CQUNoRCxJQUFJLEVBQUUsS0FBSyxDQUNULHFCQUFxQixFQUNyQixTQUFTLEVBQ1Qsd0NBQXdDLEVBQ3hDO3dCQUNFLEdBQUcsQ0FBQyxlQUFlLEVBQUUsUUFBUSxDQUFDO3dCQUM5QixHQUFHLENBQUMsZ0JBQWdCLEVBQUUsd0NBQXdDLE9BQU8sU0FBUyxDQUFDO3dCQUMvRSxHQUFHLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUM7cUJBQ3BELENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUNYO2lCQUNGLENBQUMsQ0FBQztZQUNMLENBQUM7WUFFRCxNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxpQkFBaUIsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBRXZGLE9BQU8sYUFBYSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRTtnQkFDbEQsSUFBSSxFQUFFLEtBQUssQ0FDVCx1QkFBdUIsRUFDdkIsU0FBUyxFQUNULG1EQUFtRCxFQUNuRDtvQkFDRSxHQUFHLENBQUMsZUFBZSxFQUFFLFFBQVEsQ0FBQztvQkFDOUIsR0FBRyxDQUFDLGdCQUFnQixFQUFFLHdDQUF3QyxPQUFPLFNBQVMsQ0FBQztvQkFDL0UsR0FBRyxDQUFDLFFBQVEsRUFBRSxnQ0FBZ0MsTUFBTSxTQUFTLENBQUM7b0JBQzlELEdBQUcsQ0FBQyxTQUFTLEVBQUUsWUFBWSxDQUFDLGNBQWMsRUFBRSxTQUFTLENBQUMsQ0FBQztpQkFDeEQsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQ1g7YUFDRixDQUFDLENBQUM7UUFDTCxDQUFDO0tBQ0YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyJ9
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = customizeRefundRequests;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
// Our refund reasons → suggested (reason_code, description) per network.
|
|
6
|
+
const REASON_MAPPING = {
|
|
7
|
+
UNAUTHORIZED: {
|
|
8
|
+
MASTERCARD: ['4837', 'No Cardholder Authorization'],
|
|
9
|
+
VISA: ['10.4', 'Fraud — Card-Absent Environment'],
|
|
10
|
+
},
|
|
11
|
+
DUPLICATE_CHARGE: {
|
|
12
|
+
MASTERCARD: ['4834', 'Duplicate Processing / Paid by Other Means'],
|
|
13
|
+
VISA: ['12.6.1', 'Duplicate Processing'],
|
|
14
|
+
},
|
|
15
|
+
ITEM_NOT_RECEIVED: {
|
|
16
|
+
MASTERCARD: ['4855', 'Goods or Services Not Provided'],
|
|
17
|
+
VISA: ['13.1', 'Merchandise / Services Not Received'],
|
|
18
|
+
},
|
|
19
|
+
SERVICE_NOT_RENDERED: {
|
|
20
|
+
MASTERCARD: ['4853', 'Cardholder Dispute — Services Not Provided'],
|
|
21
|
+
VISA: ['13.2', 'Cancelled Recurring Transaction'],
|
|
22
|
+
},
|
|
23
|
+
CANCELLED_ORDER: {
|
|
24
|
+
MASTERCARD: ['4860', 'Credit Not Processed'],
|
|
25
|
+
VISA: ['13.6', 'Credit Not Processed'],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
function customizeRefundRequests(collection) {
|
|
29
|
+
// Relationships
|
|
30
|
+
collection
|
|
31
|
+
.addManyToOneRelation('customer', 'customers', { foreignKey: 'customer_id' })
|
|
32
|
+
.addOneToManyRelation('chargebacks', 'chargebacks', { originKey: 'refund_request_id' });
|
|
33
|
+
collection.addAction('Decline Refund', {
|
|
34
|
+
scope: 'Single',
|
|
35
|
+
form: [
|
|
36
|
+
{
|
|
37
|
+
label: 'Decline Reason',
|
|
38
|
+
type: 'String',
|
|
39
|
+
isRequired: true,
|
|
40
|
+
description: 'Explain why this refund request cannot be approved.',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
execute: async (context, resultBuilder) => {
|
|
44
|
+
const id = await context.getRecordId();
|
|
45
|
+
await context.collection.update((0, utils_1.byId)(id), {
|
|
46
|
+
status: 'DECLINED',
|
|
47
|
+
resolution_notes: context.formValues['Decline Reason'],
|
|
48
|
+
reviewed_at: (0, utils_1.now)(),
|
|
49
|
+
reviewed_by: context.caller.email,
|
|
50
|
+
});
|
|
51
|
+
return resultBuilder.success('Refund request declined.');
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
collection.addAction('Open Chargeback', {
|
|
55
|
+
scope: 'Single',
|
|
56
|
+
form: [
|
|
57
|
+
{
|
|
58
|
+
label: 'Network',
|
|
59
|
+
type: 'Enum',
|
|
60
|
+
enumValues: ['VISA', 'MASTERCARD'],
|
|
61
|
+
isRequired: true,
|
|
62
|
+
description: 'Card network the disputed transaction was processed on.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: 'Merchant Name',
|
|
66
|
+
type: 'String',
|
|
67
|
+
isRequired: true,
|
|
68
|
+
description: "Merchant as it appeared on the cardholder's statement.",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: 'Analyst Notes',
|
|
72
|
+
type: 'String',
|
|
73
|
+
description: 'Why are we escalating this refund to a chargeback?',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
execute: async (context, resultBuilder) => {
|
|
77
|
+
const refundId = await context.getRecordId();
|
|
78
|
+
const refund = await context.getRecord([
|
|
79
|
+
'customer_id',
|
|
80
|
+
'transaction_id',
|
|
81
|
+
'transaction_date',
|
|
82
|
+
'amount',
|
|
83
|
+
'currency',
|
|
84
|
+
'reason',
|
|
85
|
+
'status',
|
|
86
|
+
]);
|
|
87
|
+
const network = context.formValues.Network;
|
|
88
|
+
const analyst = context.caller.email;
|
|
89
|
+
const chargebacks = context.dataSource.getCollection('chargebacks');
|
|
90
|
+
if (refund.status === 'ESCALATED') {
|
|
91
|
+
const existing = await chargebacks.list({ conditionTree: { field: 'refund_request_id', operator: 'Equal', value: refundId } }, ['reference']);
|
|
92
|
+
if (existing.length) {
|
|
93
|
+
return resultBuilder.error(`Refund already escalated to chargeback ${existing[0].reference}.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const [code, description] = REASON_MAPPING[refund.reason]?.[network] ?? [
|
|
97
|
+
'GENERAL',
|
|
98
|
+
'General Dispute',
|
|
99
|
+
];
|
|
100
|
+
// Pick the customer's primary active card (fallback: most recently issued).
|
|
101
|
+
const cards = await context.dataSource
|
|
102
|
+
.getCollection('cards')
|
|
103
|
+
.list({ conditionTree: { field: 'customer_id', operator: 'Equal', value: refund.customer_id } }, ['id', 'status', 'issued_at']);
|
|
104
|
+
cards.sort((a, b) => {
|
|
105
|
+
if ((a.status === 'ACTIVE') !== (b.status === 'ACTIVE')) {
|
|
106
|
+
return a.status === 'ACTIVE' ? -1 : 1;
|
|
107
|
+
}
|
|
108
|
+
return String(b.issued_at).localeCompare(String(a.issued_at));
|
|
109
|
+
});
|
|
110
|
+
const cardId = cards[0]?.id ?? null;
|
|
111
|
+
const all = await chargebacks.list({}, ['id']);
|
|
112
|
+
const nextId = all.reduce((max, r) => Math.max(max, Number(r.id)), 0) + 1;
|
|
113
|
+
const reference = `CB-${new Date().getUTCFullYear()}-${String(nextId).padStart(5, '0')}`;
|
|
114
|
+
await chargebacks.create([
|
|
115
|
+
{
|
|
116
|
+
id: nextId,
|
|
117
|
+
reference,
|
|
118
|
+
customer_id: refund.customer_id,
|
|
119
|
+
card_id: cardId,
|
|
120
|
+
refund_request_id: refundId,
|
|
121
|
+
transaction_id: refund.transaction_id,
|
|
122
|
+
merchant_name: context.formValues['Merchant Name'],
|
|
123
|
+
transaction_amount: refund.amount,
|
|
124
|
+
transaction_currency: refund.currency,
|
|
125
|
+
transaction_date: refund.transaction_date,
|
|
126
|
+
amount: refund.amount,
|
|
127
|
+
currency: refund.currency,
|
|
128
|
+
network,
|
|
129
|
+
reason_code: code,
|
|
130
|
+
reason_description: description,
|
|
131
|
+
source: 'CUSTOMER_DISPUTE',
|
|
132
|
+
status: 'INITIATED',
|
|
133
|
+
filed_at: (0, utils_1.now)(),
|
|
134
|
+
provisional_credit_issued: false,
|
|
135
|
+
analyst_email: analyst,
|
|
136
|
+
resolution_notes: context.formValues['Analyst Notes'] ?? null,
|
|
137
|
+
created_at: (0, utils_1.now)(),
|
|
138
|
+
updated_at: (0, utils_1.now)(),
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
await context.collection.update((0, utils_1.byId)(refundId), {
|
|
142
|
+
status: 'ESCALATED',
|
|
143
|
+
escalated_to: 'CHARGEBACK',
|
|
144
|
+
reviewed_at: (0, utils_1.now)(),
|
|
145
|
+
reviewed_by: analyst,
|
|
146
|
+
});
|
|
147
|
+
return resultBuilder.success(`Chargeback ${reference} opened on ${network} (${code}). Refund marked as ESCALATED.`);
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVmdW5kX3JlcXVlc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2N1c3RvbWl6YXRpb25zL3JlZnVuZF9yZXF1ZXN0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQTRCQSwwQ0FtSkM7QUE3S0QsbUNBQW9DO0FBRXBDLHlFQUF5RTtBQUN6RSxNQUFNLGNBQWMsR0FBcUQ7SUFDdkUsWUFBWSxFQUFFO1FBQ1osVUFBVSxFQUFFLENBQUMsTUFBTSxFQUFFLDZCQUE2QixDQUFDO1FBQ25ELElBQUksRUFBRSxDQUFDLE1BQU0sRUFBRSxpQ0FBaUMsQ0FBQztLQUNsRDtJQUNELGdCQUFnQixFQUFFO1FBQ2hCLFVBQVUsRUFBRSxDQUFDLE1BQU0sRUFBRSw0Q0FBNEMsQ0FBQztRQUNsRSxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUUsc0JBQXNCLENBQUM7S0FDekM7SUFDRCxpQkFBaUIsRUFBRTtRQUNqQixVQUFVLEVBQUUsQ0FBQyxNQUFNLEVBQUUsZ0NBQWdDLENBQUM7UUFDdEQsSUFBSSxFQUFFLENBQUMsTUFBTSxFQUFFLHFDQUFxQyxDQUFDO0tBQ3REO0lBQ0Qsb0JBQW9CLEVBQUU7UUFDcEIsVUFBVSxFQUFFLENBQUMsTUFBTSxFQUFFLDRDQUE0QyxDQUFDO1FBQ2xFLElBQUksRUFBRSxDQUFDLE1BQU0sRUFBRSxpQ0FBaUMsQ0FBQztLQUNsRDtJQUNELGVBQWUsRUFBRTtRQUNmLFVBQVUsRUFBRSxDQUFDLE1BQU0sRUFBRSxzQkFBc0IsQ0FBQztRQUM1QyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEVBQUUsc0JBQXNCLENBQUM7S0FDdkM7Q0FDRixDQUFDO0FBRUYsU0FBd0IsdUJBQXVCLENBQzdDLFVBQTREO0lBRTVELGdCQUFnQjtJQUNoQixVQUFVO1NBQ1Asb0JBQW9CLENBQUMsVUFBVSxFQUFFLFdBQVcsRUFBRSxFQUFFLFVBQVUsRUFBRSxhQUFhLEVBQUUsQ0FBQztTQUM1RSxvQkFBb0IsQ0FBQyxhQUFhLEVBQUUs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
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = customizeSarReports;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
function customizeSarReports(collection) {
|
|
6
|
+
// Relationships
|
|
7
|
+
collection
|
|
8
|
+
.addManyToOneRelation('customer', 'customers', { foreignKey: 'customer_id' })
|
|
9
|
+
.addManyToOneRelation('alert', 'aml_alerts', { foreignKey: 'alert_id' });
|
|
10
|
+
// File the SAR with the financial intelligence unit and escalate the source alert.
|
|
11
|
+
collection.addAction('File SAR', {
|
|
12
|
+
scope: 'Single',
|
|
13
|
+
form: [],
|
|
14
|
+
execute: async (context, resultBuilder) => {
|
|
15
|
+
const sarId = await context.getRecordId();
|
|
16
|
+
const sar = await context.getRecord(['reference', 'alert_id']);
|
|
17
|
+
await context.collection.update((0, utils_1.byId)(sarId), { status: 'FILED', filed_at: (0, utils_1.now)() });
|
|
18
|
+
if (sar.alert_id) {
|
|
19
|
+
await context.dataSource
|
|
20
|
+
.getCollection('aml_alerts')
|
|
21
|
+
.update({ conditionTree: { field: 'id', operator: 'Equal', value: sar.alert_id } }, { status: 'SAR_FILED', sar_reference: sar.reference, resolved_at: (0, utils_1.now)() });
|
|
22
|
+
}
|
|
23
|
+
return resultBuilder.success(`SAR ${sar.reference} filed. Source alert escalated.`);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FyX3JlcG9ydHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY3VzdG9taXphdGlvbnMvc2FyX3JlcG9ydHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFJQSxzQ0E4QkM7QUFoQ0QsbUNBQW9DO0FBRXBDLFNBQXdCLG1CQUFtQixDQUN6QyxVQUF3RDtJQUV4RCxnQkFBZ0I7SUFDaEIsVUFBVTtTQUNQLG9CQUFvQixDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUUsRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLENBQUM7U0FDNUUsb0JBQW9CLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDO0lBRTNFLG1GQUFtRjtJQUNuRixVQUFVLENBQUMsU0FBUyxDQUFDLFVBQVUsRUFBRTtRQUMvQixLQUFLLEVBQUUsUUFBUTtRQUNmLElBQUksRUFBRSxFQUFFO1FBQ1IsT0FBTyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsYUFBYSxFQUFFLEVBQUU7WUFDeEMsTUFBTSxLQUFLLEdBQUcsTUFBTSxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDMUMsTUFBTSxHQUFHLEdBQUcsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsV0FBVyxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUM7WUFFL0QsTUFBTSxPQUFPLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxJQUFBLFlBQUksRUFBQyxLQUFLLENBQUMsRUFBRSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLElBQUEsV0FBRyxHQUFFLEVBQUUsQ0FBQyxDQUFDO1lBRW5GLElBQUksR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUNqQixNQUFNLE9BQU8sQ0FBQyxVQUFVO3FCQUNyQixhQUFhLENBQUMsWUFBWSxDQUFDO3FCQUMzQixNQUFNLENBQ0wsRUFBRSxhQUFhLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxRQUFRLEVBQUUsRUFBRSxFQUMxRSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLEdBQUcsQ0FBQyxTQUFTLEVBQUUsV0FBVyxFQUFFLElBQUEsV0FBRyxHQUFFLEVBQUUsQ0FDMUUsQ0FBQztZQUNOLENBQUM7WUFFRCxPQUFPLGFBQWEsQ0FBQyxPQUFPLENBQUMsT0FBTyxHQUFHLENBQUMsU0FBUyxpQ0FBaUMsQ0FBQyxDQUFDO1FBQ3RGLENBQUM7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDIn0=
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** A Forest filter matching a single record by primary key. */
|
|
2
|
+
export declare function byId(id: string | number): {
|
|
3
|
+
conditionTree: {
|
|
4
|
+
field: string;
|
|
5
|
+
operator: "Equal";
|
|
6
|
+
value: string | number;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
/** A Forest filter matching several records by primary key. */
|
|
10
|
+
export declare function byIds(ids: Array<string | number>): {
|
|
11
|
+
conditionTree: {
|
|
12
|
+
field: string;
|
|
13
|
+
operator: "In";
|
|
14
|
+
value: (string | number)[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/** Current instant as an ISO timestamp (the demo's stand-in for SQL `NOW()`). */
|
|
18
|
+
export declare function now(): string;
|
|
19
|
+
/** Statuses for which a chargeback is considered resolved and immutable. */
|
|
20
|
+
export declare const RESOLVED_CHARGEBACK_STATUSES: string[];
|
|
21
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Small helpers shared by the Smart Action customizations.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.RESOLVED_CHARGEBACK_STATUSES = void 0;
|
|
5
|
+
exports.byId = byId;
|
|
6
|
+
exports.byIds = byIds;
|
|
7
|
+
exports.now = now;
|
|
8
|
+
/** A Forest filter matching a single record by primary key. */
|
|
9
|
+
function byId(id) {
|
|
10
|
+
return { conditionTree: { field: 'id', operator: 'Equal', value: id } };
|
|
11
|
+
}
|
|
12
|
+
/** A Forest filter matching several records by primary key. */
|
|
13
|
+
function byIds(ids) {
|
|
14
|
+
return { conditionTree: { field: 'id', operator: 'In', value: ids } };
|
|
15
|
+
}
|
|
16
|
+
/** Current instant as an ISO timestamp (the demo's stand-in for SQL `NOW()`). */
|
|
17
|
+
function now() {
|
|
18
|
+
return new Date().toISOString();
|
|
19
|
+
}
|
|
20
|
+
/** Statuses for which a chargeback is considered resolved and immutable. */
|
|
21
|
+
exports.RESOLVED_CHARGEBACK_STATUSES = ['WON', 'LOST', 'ACCEPTED', 'DISMISSED'];
|
|
22
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY3VzdG9taXphdGlvbnMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLDJEQUEyRDs7O0FBRzNELG9CQUVDO0FBR0Qsc0JBRUM7QUFHRCxrQkFFQztBQWJELCtEQUErRDtBQUMvRCxTQUFnQixJQUFJLENBQUMsRUFBbUI7SUFDdEMsT0FBTyxFQUFFLGFBQWEsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE9BQWdCLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7QUFDbkYsQ0FBQztBQUVELCtEQUErRDtBQUMvRCxTQUFnQixLQUFLLENBQUMsR0FBMkI7SUFDL0MsT0FBTyxFQUFFLGFBQWEsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQWEsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztBQUNqRixDQUFDO0FBRUQsaUZBQWlGO0FBQ2pGLFNBQWdCLEdBQUc7SUFDakIsT0FBTyxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ2xDLENBQUM7QUFFRCw0RUFBNEU7QUFDL0QsUUFBQSw0QkFBNEIsR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFdBQVcsQ0FBQyxDQUFDIn0=
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createDemoFintechDataSource = createDemoFintechDataSource;
|
|
7
|
+
const datasource_customizer_1 = require("@forestadmin/datasource-customizer");
|
|
8
|
+
const datasource_sql_1 = require("@forestadmin/datasource-sql");
|
|
9
|
+
const aml_alerts_1 = __importDefault(require("./customizations/aml_alerts"));
|
|
10
|
+
const cards_1 = __importDefault(require("./customizations/cards"));
|
|
11
|
+
const chargebacks_1 = __importDefault(require("./customizations/chargebacks"));
|
|
12
|
+
const customers_1 = __importDefault(require("./customizations/customers"));
|
|
13
|
+
const kyc_cases_1 = __importDefault(require("./customizations/kyc_cases"));
|
|
14
|
+
const kyc_documents_1 = __importDefault(require("./customizations/kyc_documents"));
|
|
15
|
+
const refund_requests_1 = __importDefault(require("./customizations/refund_requests"));
|
|
16
|
+
const sar_reports_1 = __importDefault(require("./customizations/sar_reports"));
|
|
17
|
+
const seed_1 = __importDefault(require("./seed"));
|
|
18
|
+
const pools_1 = require("./seed/pools");
|
|
19
|
+
// Categorical columns are stored as plain text in SQLite, so introspection types
|
|
20
|
+
// them as `String`. We re-type them as `Enum` to restore Forest's dropdowns. The
|
|
21
|
+
// value lists are the seed's enum domains (single source of truth), extended with
|
|
22
|
+
// the values that only Smart Actions write (`SAR_FILED`, `DISMISSED`) so re-typing
|
|
23
|
+
// never rejects a legitimate value.
|
|
24
|
+
const ENUM_FIELDS = {
|
|
25
|
+
customers: {
|
|
26
|
+
onboarding_status: pools_1.ONBOARDING_STATUSES,
|
|
27
|
+
risk_rating: pools_1.RISK_RATINGS,
|
|
28
|
+
},
|
|
29
|
+
cards: {
|
|
30
|
+
card_type: pools_1.CARD_TYPES,
|
|
31
|
+
status: pools_1.CARD_STATUSES,
|
|
32
|
+
},
|
|
33
|
+
aml_alerts: {
|
|
34
|
+
alert_type: pools_1.ATYPES,
|
|
35
|
+
severity: pools_1.AML_SEVERITIES,
|
|
36
|
+
status: [...pools_1.AML_STATUSES, 'SAR_FILED'],
|
|
37
|
+
},
|
|
38
|
+
kyc_cases: {
|
|
39
|
+
status: pools_1.KYC_STATUSES,
|
|
40
|
+
},
|
|
41
|
+
kyc_documents: {
|
|
42
|
+
document_type: pools_1.DOC_TYPES,
|
|
43
|
+
},
|
|
44
|
+
chargebacks: {
|
|
45
|
+
status: [...pools_1.CB_STATUS, 'DISMISSED'],
|
|
46
|
+
network: pools_1.NETWORKS,
|
|
47
|
+
source: pools_1.SOURCES,
|
|
48
|
+
},
|
|
49
|
+
refund_requests: {
|
|
50
|
+
status: pools_1.REFUND_STATUSES,
|
|
51
|
+
reason: pools_1.REFUND_REASONS,
|
|
52
|
+
},
|
|
53
|
+
sar_reports: {
|
|
54
|
+
status: pools_1.SAR_STATUSES,
|
|
55
|
+
activity_type: pools_1.ATYPES,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
/** Re-types the categorical columns of the introspected schema as enums in place. */
|
|
59
|
+
function applyEnums(dataSource) {
|
|
60
|
+
for (const [collectionName, fields] of Object.entries(ENUM_FIELDS)) {
|
|
61
|
+
const { fields: schemaFields } = dataSource.getCollection(collectionName).schema;
|
|
62
|
+
for (const [fieldName, enumValues] of Object.entries(fields)) {
|
|
63
|
+
const column = schemaFields[fieldName];
|
|
64
|
+
column.columnType = 'Enum';
|
|
65
|
+
column.enumValues = [...enumValues];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Builds a throwaway SQLite database, seeds it with the demo dataset, and exposes
|
|
71
|
+
* it through the SQL data source. Schema, relations and types are introspected
|
|
72
|
+
* from the freshly-created tables. A new database is generated on every boot.
|
|
73
|
+
*/
|
|
74
|
+
async function buildSeededSqlDataSource(logger, restartAgent) {
|
|
75
|
+
const storage = 'db.sqlite';
|
|
76
|
+
await (0, seed_1.default)(storage);
|
|
77
|
+
const ds = await (0, datasource_sql_1.createSqlDataSource)({ dialect: 'sqlite', storage })(logger, restartAgent);
|
|
78
|
+
applyEnums(ds);
|
|
79
|
+
return ds;
|
|
80
|
+
}
|
|
81
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
82
|
+
function createDemoFintechDataSource() {
|
|
83
|
+
const customizer = new datasource_customizer_1.DataSourceCustomizer();
|
|
84
|
+
customizer.addDataSource(buildSeededSqlDataSource);
|
|
85
|
+
// Each module wires that collection's relationships, computed fields,
|
|
86
|
+
// segments and Smart Actions. Relations centre on `customers`.
|
|
87
|
+
customizer.customizeCollection('customers', customers_1.default);
|
|
88
|
+
customizer.customizeCollection('cards', cards_1.default);
|
|
89
|
+
customizer.customizeCollection('aml_alerts', aml_alerts_1.default);
|
|
90
|
+
customizer.customizeCollection('kyc_cases', kyc_cases_1.default);
|
|
91
|
+
customizer.customizeCollection('kyc_documents', kyc_documents_1.default);
|
|
92
|
+
customizer.customizeCollection('chargebacks', chargebacks_1.default);
|
|
93
|
+
customizer.customizeCollection('refund_requests', refund_requests_1.default);
|
|
94
|
+
customizer.customizeCollection('sar_reports', sar_reports_1.default);
|
|
95
|
+
return customizer.getFactory();
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUErR0Esa0VBaUJDO0FBekhELDhFQUEwRTtBQUMxRSxnRUFBa0U7QUFFbEUsNkVBQTZEO0FBQzdELG1FQUFvRDtBQUNwRCwrRUFBZ0U7QUFDaEUsMkVBQTREO0FBQzVELDJFQUEyRDtBQUMzRCxtRkFBbUU7QUFDbkUsdUZBQXVFO0FBQ3ZFLCtFQUErRDtBQUMvRCxrREFBMEI7QUFDMUIsd0NBZ0JzQjtBQUV0QixpRkFBaUY7QUFDakYsaUZBQWlGO0FBQ2pGLGtGQUFrRjtBQUNsRixtRkFBbUY7QUFDbkYsb0NBQW9DO0FBQ3BDLE1BQU0sV0FBVyxHQUFzRDtJQUNyRSxTQUFTLEVBQUU7UUFDVCxpQkFBaUIsRUFBRSwyQkFBbUI7UUFDdEMsV0FBVyxFQUFFLG9CQUFZO0tBQzFCO0lBQ0QsS0FBSyxFQUFFO1FBQ0wsU0FBUyxFQUFFLGtCQUFVO1FBQ3JCLE1BQU0sRUFBRSxxQkFBYTtLQUN0QjtJQUNELFVBQVUsRUFBRTtRQUNWLFVBQVUsRUFBRSxjQUFNO1FBQ2xCLFFBQVEsRUFBRSxzQkFBYztRQUN4QixNQUFNLEVBQUUsQ0FBQyxHQUFHLG9CQUFZLEVBQUUsV0FBVyxDQUFDO0tBQ3ZDO0lBQ0QsU0FBUyxFQUFFO1FBQ1QsTUFBTSxFQUFFLG9CQUFZO0tBQ3JCO0lBQ0QsYUFBYSxFQUFFO1FBQ2IsYUFBYSxFQUFFLGlCQUFTO0tBQ3pCO0lBQ0QsV0FBVyxFQUFFO1FBQ1gsTUFBTSxFQUFFLENBQUMsR0FBRyxpQkFBUyxFQUFFLFdBQVcsQ0FBQztRQUNuQyxPQUFPLEVBQUUsZ0JBQVE7UUFDakIsTUFBTSxFQUFFLGVBQU87S0FDaEI7SUFDRCxlQUFlLEVBQUU7UUFDZixNQUFNLEVBQUUsdUJBQWU7UUFDdkIsTUFBTSxFQUFFLHNCQUFjO0tBQ3ZCO0lBQ0QsV0FBVyxFQUFFO1FBQ1gsTUFBTSxFQUFFLG9CQUFZO1FBQ3BCLGFBQWEsRUFBRSxjQUFNO0tBQ3RCO0NBQ0YsQ0FBQztBQUVGLHFGQUFxRjtBQUNyRixTQUFTLFVBQVUsQ0FBQyxVQUFzQjtJQUN4QyxLQUFLLE1BQU0sQ0FBQyxjQUFjLEVBQUUsTUFBTSxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO1FBQ25FLE1BQU0sRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLEdBQUcsVUFBVSxDQUFDLGFBQWEsQ0FBQyxjQUFjLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFFakYsS0FBSyxNQUFNLENBQUMsU0FBUyxFQUFFLFVBQVUsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUM3RCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsU0FBUyxDQUFpQixDQUFDO1lBQ3ZELE1BQU0sQ0FBQyxVQUFVLEdBQUcsTUFBTSxDQUFDO1lBQzNCLE1BQU0sQ0FBQyxVQUFVLEdBQUcsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxDQUFDO1FBQ3RDLENBQUM7SUFDSCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxLQUFLLFVBQVUsd0JBQXdCLENBQ3JDLE1BQWMsRUFDZCxZQUFpQztJQUVqQyxNQUFNLE9BQU8sR0FBRyxXQUFXLENBQUM7SUFFNUIsTUFBTSxJQUFBLGNBQUksRUFBQyxPQUFPLENBQUMsQ0FBQztJQUVwQixNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsb0NBQW1CLEVBQUMsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBRTNGLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUVmLE9BQU8sRUFBRSxDQUFDO0FBQ1osQ0FBQztBQUVELHdEQUF3RDtBQUN4RCxTQUFnQiwyQkFBMkI7SUFDekMsTUFBTSxVQUFVLEdBQUcsSUFBSSw0Q0FBb0IsRUFBRSxDQUFDO0lBRTlDLFVBQVUsQ0FBQyxhQUFhLENBQUMsd0JBQXdCLENBQUMsQ0FBQztJQUVuRCxzRUFBc0U7SUFDdEUsK0RBQStEO0lBQy9ELFVBQVUsQ0FBQyxtQkFBbUIsQ0FBQyxXQUFXLEVBQUUsbUJBQWtCLENBQUMsQ0FBQztJQUNoRSxVQUFVLENBQUMsbUJBQW1CLENBQUMsT0FBTyxFQUFFLGVBQWMsQ0FBQyxDQUFDO0lBQ3hELFVBQVUsQ0FBQyxtQkFBbUIsQ0FBQyxZQUFZLEVBQUUsb0JBQWtCLENBQUMsQ0FBQztJQUNqRSxVQUFVLENBQUMsbUJBQW1CLENBQUMsV0FBVyxFQUFFLG1CQUFpQixDQUFDLENBQUM7SUFDL0QsVUFBVSxDQUFDLG1CQUFtQixDQUFDLGVBQWUsRUFBRSx1QkFBcUIsQ0FBQyxDQUFDO0lBQ3ZFLFVBQVUsQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLEVBQUUscUJBQW9CLENBQUMsQ0FBQztJQUNwRSxVQUFVLENBQUMsbUJBQW1CLENBQUMsaUJBQWlCLEVBQUUseUJBQXVCLENBQUMsQ0FBQztJQUMzRSxVQUFVLENBQUMsbUJBQW1CLENBQUMsYUFBYSxFQUFFLHFCQUFtQixDQUFDLENBQUM7SUFFbkUsT0FBTyxVQUFVLENBQUMsVUFBVSxFQUFFLENBQUM7QUFDakMsQ0FBQyJ9
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Customer } from './types';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Raises 70 AML alerts, weighting customers by risk/PEP/sanctions, then files SARs
|
|
5
|
+
* for the escalated ones. A filed SAR pushes its source alert to SAR_FILED so the
|
|
6
|
+
* two collections always tell the same story.
|
|
7
|
+
*/
|
|
8
|
+
export default function seedAml(knex: Knex, customers: Customer[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=aml.d.ts.map
|