forest_admin_datasource_mambu_payments 1.33.1

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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/Rakefile +6 -0
  4. data/forest_admin_datasource_mambu_payments.gemspec +37 -0
  5. data/lib/forest_admin_datasource_mambu_payments/client/reads.rb +66 -0
  6. data/lib/forest_admin_datasource_mambu_payments/client/writes.rb +42 -0
  7. data/lib/forest_admin_datasource_mambu_payments/client.rb +166 -0
  8. data/lib/forest_admin_datasource_mambu_payments/collections/account_holder.rb +64 -0
  9. data/lib/forest_admin_datasource_mambu_payments/collections/balance.rb +75 -0
  10. data/lib/forest_admin_datasource_mambu_payments/collections/base_collection.rb +254 -0
  11. data/lib/forest_admin_datasource_mambu_payments/collections/claim.rb +98 -0
  12. data/lib/forest_admin_datasource_mambu_payments/collections/connected_account.rb +103 -0
  13. data/lib/forest_admin_datasource_mambu_payments/collections/direct_debit_mandate.rb +125 -0
  14. data/lib/forest_admin_datasource_mambu_payments/collections/event.rb +133 -0
  15. data/lib/forest_admin_datasource_mambu_payments/collections/expected_payment.rb +132 -0
  16. data/lib/forest_admin_datasource_mambu_payments/collections/external_account.rb +121 -0
  17. data/lib/forest_admin_datasource_mambu_payments/collections/file.rb +89 -0
  18. data/lib/forest_admin_datasource_mambu_payments/collections/incoming_payment.rb +120 -0
  19. data/lib/forest_admin_datasource_mambu_payments/collections/internal_account.rb +136 -0
  20. data/lib/forest_admin_datasource_mambu_payments/collections/payee_verification_request.rb +88 -0
  21. data/lib/forest_admin_datasource_mambu_payments/collections/payment_capture.rb +136 -0
  22. data/lib/forest_admin_datasource_mambu_payments/collections/payment_order.rb +132 -0
  23. data/lib/forest_admin_datasource_mambu_payments/collections/reconciliation.rb +93 -0
  24. data/lib/forest_admin_datasource_mambu_payments/collections/return.rb +132 -0
  25. data/lib/forest_admin_datasource_mambu_payments/collections/transaction.rb +113 -0
  26. data/lib/forest_admin_datasource_mambu_payments/configuration.rb +36 -0
  27. data/lib/forest_admin_datasource_mambu_payments/datasource.rb +35 -0
  28. data/lib/forest_admin_datasource_mambu_payments/plugins/disable_search.rb +31 -0
  29. data/lib/forest_admin_datasource_mambu_payments/plugins/helpers.rb +94 -0
  30. data/lib/forest_admin_datasource_mambu_payments/plugins/messages.rb +30 -0
  31. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/holder_link_plugin.rb +56 -0
  32. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_account_holder_to_direct_debit_mandates.rb +14 -0
  33. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_account_holder_to_incoming_payments.rb +14 -0
  34. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_external_account_to_direct_debit_mandates.rb +13 -0
  35. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_external_account_to_incoming_payments.rb +13 -0
  36. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_external_account_to_payment_orders.rb +13 -0
  37. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_incoming_payment_to_events.rb +13 -0
  38. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_incoming_payment_to_expected_payments.rb +21 -0
  39. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_incoming_payment_to_returns.rb +12 -0
  40. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_incoming_payment_to_transactions.rb +20 -0
  41. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_internal_account_to_balances.rb +17 -0
  42. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_internal_account_to_incoming_payments.rb +13 -0
  43. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_internal_account_to_payment_orders.rb +17 -0
  44. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_payment_order_to_events.rb +13 -0
  45. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_payment_order_to_receiving_account_holder.rb +15 -0
  46. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_payment_order_to_returns.rb +12 -0
  47. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/link_payment_order_to_transactions.rb +51 -0
  48. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/one_to_many_link_plugin.rb +35 -0
  49. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/pivot_resolution.rb +73 -0
  50. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/two_step_connected_account_filter.rb +38 -0
  51. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/two_step_cross_reconciliation_filter.rb +55 -0
  52. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/two_step_holder_filter.rb +32 -0
  53. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/two_step_link_plugin.rb +64 -0
  54. data/lib/forest_admin_datasource_mambu_payments/plugins/relations/two_step_reconciliation_filter.rb +39 -0
  55. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/approve_payment_order.rb +56 -0
  56. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/cancel_payment_order.rb +66 -0
  57. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/create_account_holder.rb +44 -0
  58. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/create_external_account.rb +54 -0
  59. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/create_internal_account.rb +58 -0
  60. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/create_payment_order.rb +66 -0
  61. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/trigger_payee_verification.rb +58 -0
  62. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/update_account_holder.rb +67 -0
  63. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/update_external_account.rb +75 -0
  64. data/lib/forest_admin_datasource_mambu_payments/plugins/smart_actions/update_internal_account.rb +75 -0
  65. data/lib/forest_admin_datasource_mambu_payments/query/condition_tree_translator.rb +115 -0
  66. data/lib/forest_admin_datasource_mambu_payments/version.rb +3 -0
  67. data/lib/forest_admin_datasource_mambu_payments.rb +44 -0
  68. metadata +170 -0
@@ -0,0 +1,136 @@
1
+ # rubocop:disable Metrics/ClassLength, Metrics/MethodLength
2
+ module ForestAdminDatasourceMambuPayments
3
+ module Collections
4
+ class InternalAccount < BaseCollection
5
+ ManyToOneSchema = ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema
6
+
7
+ client_resource :internal_account
8
+
9
+ def initialize(datasource)
10
+ super(datasource, 'MambuInternalAccount')
11
+ define_schema
12
+ define_relations
13
+ reconcile_filter_operators!
14
+ end
15
+
16
+ def create(_caller, data)
17
+ serialize(datasource.client.create_internal_account(build_payload(data)))
18
+ end
19
+
20
+ def update(caller, filter, patch)
21
+ payload = build_payload(patch)
22
+ ids_for(caller, filter).each { |id| datasource.client.update_internal_account(id, payload) }
23
+ end
24
+
25
+ def delete(caller, filter)
26
+ ids_for(caller, filter).each { |id| datasource.client.delete_internal_account(id) }
27
+ end
28
+
29
+ def serialize(record)
30
+ a = attrs_of(record)
31
+ {
32
+ 'id' => a['id'],
33
+ 'object' => a['object'],
34
+ 'status' => a['status'],
35
+ 'status_details' => a['status_details'],
36
+ 'type' => a['type'],
37
+ 'name' => a['name'],
38
+ 'holder_name' => a['holder_name'],
39
+ 'alternative_holder_names' => a['alternative_holder_names'],
40
+ 'connected_account_ids' => a['connected_account_ids'],
41
+ 'account_number' => a['account_number'],
42
+ 'account_number_format' => a['account_number_format'],
43
+ 'bank_code' => a['bank_code'],
44
+ 'bank_name' => a['bank_name'],
45
+ 'bank_address' => a['bank_address'],
46
+ 'bank_code_format' => a['bank_code_format'],
47
+ 'holder_address' => a['holder_address'],
48
+ 'account_holder_id' => a['account_holder_id'],
49
+ 'creditor_identifier' => a['creditor_identifier'],
50
+ 'organization_identification' => a['organization_identification'],
51
+ 'customer_bic' => a['customer_bic'],
52
+ 'distinguished_name' => a['distinguished_name'],
53
+ 'currencies' => a['currencies'],
54
+ 'cbs_source' => a['cbs_source'],
55
+ 'cbs_account_id' => a['cbs_account_id'],
56
+ 'cbs_account_type' => a['cbs_account_type'],
57
+ 'synchronized_with_bank' => a['synchronized_with_bank'],
58
+ 'metadata' => a['metadata'],
59
+ 'bank_data' => a['bank_data'],
60
+ 'custom_fields' => a['custom_fields'],
61
+ 'created_at' => a['created_at']
62
+ }
63
+ end
64
+
65
+ protected
66
+
67
+ def collection_filters
68
+ {
69
+ 'account_holder_id' => { ops: [Operators::EQUAL, Operators::IN] }
70
+ }
71
+ end
72
+
73
+ def many_to_one_embeds
74
+ [
75
+ { foreign_key: 'account_holder_id', relation_name: 'account_holder',
76
+ collection: 'MambuAccountHolder' }
77
+ ]
78
+ end
79
+
80
+ private
81
+
82
+ def define_schema
83
+ add_field('id', ColumnSchema.new(column_type: 'String', is_primary_key: true,
84
+ is_read_only: true, is_sortable: true))
85
+ add_field('object', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
86
+ add_field('status', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: true))
87
+ add_field('status_details', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
88
+ add_field('type', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: true))
89
+ add_field('name', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: true))
90
+ add_field('holder_name', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
91
+ add_field('alternative_holder_names', ColumnSchema.new(column_type: 'Json', is_read_only: false,
92
+ is_sortable: false))
93
+ add_field('connected_account_ids', ColumnSchema.new(column_type: 'Json', is_read_only: false,
94
+ is_sortable: false))
95
+ add_field('account_number', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
96
+ add_field('account_number_format', ColumnSchema.new(column_type: 'String', is_read_only: false,
97
+ is_sortable: false))
98
+ add_field('bank_code', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
99
+ add_field('bank_name', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
100
+ add_field('bank_address', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
101
+ add_field('bank_code_format', ColumnSchema.new(column_type: 'String', is_read_only: false,
102
+ is_sortable: false))
103
+ add_field('holder_address', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
104
+ add_field('account_holder_id', ColumnSchema.new(column_type: 'String', is_read_only: false,
105
+ is_sortable: true))
106
+ add_field('creditor_identifier', ColumnSchema.new(column_type: 'String', is_read_only: false,
107
+ is_sortable: false))
108
+ add_field('organization_identification', ColumnSchema.new(column_type: 'Json', is_read_only: false,
109
+ is_sortable: false))
110
+ add_field('customer_bic', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
111
+ add_field('distinguished_name', ColumnSchema.new(column_type: 'String', is_read_only: false,
112
+ is_sortable: false))
113
+ add_field('currencies', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
114
+ add_field('cbs_source', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
115
+ add_field('cbs_account_id', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
116
+ add_field('cbs_account_type', ColumnSchema.new(column_type: 'String', is_read_only: false,
117
+ is_sortable: false))
118
+ add_field('synchronized_with_bank', ColumnSchema.new(column_type: 'Boolean', is_read_only: false,
119
+ is_sortable: false))
120
+ add_field('metadata', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
121
+ add_field('bank_data', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
122
+ add_field('custom_fields', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
123
+ add_field('created_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
124
+ end
125
+
126
+ def define_relations
127
+ add_field('account_holder', ManyToOneSchema.new(
128
+ foreign_collection: 'MambuAccountHolder',
129
+ foreign_key: 'account_holder_id',
130
+ foreign_key_target: 'id'
131
+ ))
132
+ end
133
+ end
134
+ end
135
+ end
136
+ # rubocop:enable Metrics/ClassLength, Metrics/MethodLength
@@ -0,0 +1,88 @@
1
+ module ForestAdminDatasourceMambuPayments
2
+ module Collections
3
+ # Payee Verification Requests are emitted by Numeral when an outgoing
4
+ # verification is sent (via the `Trigger payee verification` smart
5
+ # action on external accounts) or when an incoming verification arrives
6
+ # from the network. From Forest's perspective they are read-only: send
7
+ # and simulate are lifecycle operations exposed as smart-action plugins
8
+ # (see TriggerPayeeVerification) rather than collection writes.
9
+ class PayeeVerificationRequest < BaseCollection
10
+ ENUM_STATUS = %w[completed failed].freeze
11
+ ENUM_FAILURE_CODE = %w[business_error technical_error psp_technical_error].freeze
12
+ ENUM_DIRECTION = %w[outgoing incoming].freeze
13
+ ENUM_SCHEME = %w[vop].freeze
14
+ ENUM_MATCHING_RESULT = %w[match close_match no_match impossible_match].freeze
15
+
16
+ client_resource :payee_verification_request
17
+
18
+ def initialize(datasource)
19
+ super(datasource, 'MambuPayeeVerificationRequest')
20
+ define_schema
21
+ reconcile_filter_operators!
22
+ end
23
+
24
+ def serialize(record)
25
+ a = attrs_of(record)
26
+ {
27
+ 'id' => a['id'],
28
+ 'object' => a['object'],
29
+ 'status' => a['status'],
30
+ 'failure_code' => a['failure_code'],
31
+ 'status_details' => a['status_details'],
32
+ 'direction' => a['direction'],
33
+ 'scheme' => a['scheme'],
34
+ 'request' => a['request'],
35
+ 'matching_result' => a['matching_result'],
36
+ 'payee_suggested_name' => a['payee_suggested_name'],
37
+ 'matching_details' => a['matching_details'],
38
+ 'scheme_data' => a['scheme_data'],
39
+ 'metadata' => a['metadata'],
40
+ 'response_received_at' => a['response_received_at'],
41
+ 'created_at' => a['created_at']
42
+ }
43
+ end
44
+
45
+ protected
46
+
47
+ def collection_filters
48
+ {
49
+ 'status' => { ops: [Operators::EQUAL, Operators::IN] },
50
+ 'direction' => { ops: [Operators::EQUAL, Operators::IN] },
51
+ 'scheme' => { ops: [Operators::EQUAL, Operators::IN] },
52
+ 'matching_result' => { ops: [Operators::EQUAL, Operators::IN] }
53
+ }
54
+ end
55
+
56
+ private
57
+
58
+ def define_schema
59
+ add_field('id', ColumnSchema.new(column_type: 'String', is_primary_key: true,
60
+ is_read_only: true, is_sortable: true))
61
+ add_field('object', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
62
+ add_field('status', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_STATUS,
63
+ is_read_only: true, is_sortable: true))
64
+ add_field('failure_code', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_FAILURE_CODE,
65
+ is_read_only: true, is_sortable: false))
66
+ add_field('status_details', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
67
+ add_field('direction', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_DIRECTION,
68
+ is_read_only: true, is_sortable: true))
69
+ add_field('scheme', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_SCHEME,
70
+ is_read_only: true, is_sortable: true))
71
+ # request, matching_details, scheme_data are nested objects with their
72
+ # own sub-fields (payee_identification, scheme_request_id, ...). Forest
73
+ # can't model nested columns natively, so we expose them as Json
74
+ # snapshots — matches how IncomingPayment handles originating_account.
75
+ add_field('request', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
76
+ add_field('matching_result', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_MATCHING_RESULT,
77
+ is_read_only: true, is_sortable: true))
78
+ add_field('payee_suggested_name', ColumnSchema.new(column_type: 'String', is_read_only: true,
79
+ is_sortable: false))
80
+ add_field('matching_details', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
81
+ add_field('scheme_data', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
82
+ add_field('metadata', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
83
+ add_field('response_received_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
84
+ add_field('created_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,136 @@
1
+ # rubocop:disable Metrics/ClassLength, Metrics/MethodLength
2
+ module ForestAdminDatasourceMambuPayments
3
+ module Collections
4
+ class PaymentCapture < BaseCollection
5
+ ManyToOneSchema = ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema
6
+
7
+ ENUM_TYPE = %w[charge chargeback refund].freeze
8
+ ENUM_SOURCE = %w[api reporting_file].freeze
9
+ ENUM_RECONCILIATION_STATUS = %w[unreconciled reconciled partially_reconciled].freeze
10
+
11
+ client_resource :payment_capture
12
+
13
+ def initialize(datasource)
14
+ super(datasource, 'MambuPaymentCapture')
15
+ define_schema
16
+ define_relations
17
+ reconcile_filter_operators!
18
+ end
19
+
20
+ def serialize(record)
21
+ a = attrs_of(record)
22
+ {
23
+ 'id' => a['id'],
24
+ 'object' => a['object'],
25
+ 'idempotency_key' => a['idempotency_key'],
26
+ 'connected_account_id' => a['connected_account_id'],
27
+ 'type' => a['type'],
28
+ 'source' => a['source'],
29
+ 'amount' => a['amount'],
30
+ 'original_payment_amount' => a['original_payment_amount'],
31
+ 'currency' => a['currency'],
32
+ 'date' => a['date'],
33
+ 'value_date' => a['value_date'],
34
+ 'remittance_date' => a['remittance_date'],
35
+ 'remittance_reference' => a['remittance_reference'],
36
+ 'transaction_reference' => a['transaction_reference'],
37
+ 'authorization_id' => a['authorization_id'],
38
+ 'payment_reference' => a['payment_reference'],
39
+ 'network' => a['network'],
40
+ 'merchant_id' => a['merchant_id'],
41
+ 'fee_amount' => a['fee_amount'],
42
+ 'fee_amount_currency' => a['fee_amount_currency'],
43
+ 'net_amount' => a['net_amount'],
44
+ 'net_amount_currency' => a['net_amount_currency'],
45
+ 'reconciliation_status' => a['reconciliation_status'],
46
+ 'reconciled_amount' => a['reconciled_amount'],
47
+ 'cbs_data' => a['cbs_data'],
48
+ 'lending' => a['lending'],
49
+ 'metadata' => a['metadata'],
50
+ 'canceled_at' => a['canceled_at'],
51
+ 'updated_at' => a['updated_at'],
52
+ 'created_at' => a['created_at']
53
+ }
54
+ end
55
+
56
+ protected
57
+
58
+ def collection_filters
59
+ {
60
+ 'connected_account_id' => { ops: [Operators::EQUAL, Operators::IN] },
61
+ 'type' => { ops: [Operators::EQUAL, Operators::IN] },
62
+ 'source' => { ops: [Operators::EQUAL, Operators::IN] },
63
+ 'reconciliation_status' => { ops: [Operators::EQUAL, Operators::IN] }
64
+ }
65
+ end
66
+
67
+ def many_to_one_embeds
68
+ [
69
+ { foreign_key: 'connected_account_id', relation_name: 'connected_account',
70
+ collection: 'MambuConnectedAccount' }
71
+ ]
72
+ end
73
+
74
+ private
75
+
76
+ # Payment captures are emitted by PSPs (or registered manually via API
77
+ # to reconcile reporting files). From Forest's perspective they're
78
+ # read-only: create / update / cancel exist on the Numeral API but are
79
+ # lifecycle operations better expressed as smart-action plugins later
80
+ # (same approach as payment_orders' approve/cancel).
81
+ def define_schema
82
+ add_field('id', ColumnSchema.new(column_type: 'String', is_primary_key: true,
83
+ is_read_only: true, is_sortable: true))
84
+ add_field('object', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
85
+ add_field('idempotency_key', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
86
+ add_field('connected_account_id', ColumnSchema.new(column_type: 'String',
87
+ is_read_only: true, is_sortable: true))
88
+ add_field('type', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_TYPE,
89
+ is_read_only: true, is_sortable: true))
90
+ add_field('source', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_SOURCE,
91
+ is_read_only: true, is_sortable: true))
92
+ add_field('amount', ColumnSchema.new(column_type: 'Number', is_read_only: true, is_sortable: false))
93
+ add_field('original_payment_amount', ColumnSchema.new(column_type: 'Number', is_read_only: true,
94
+ is_sortable: false))
95
+ add_field('currency', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
96
+ add_field('date', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
97
+ add_field('value_date', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
98
+ add_field('remittance_date', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
99
+ add_field('remittance_reference', ColumnSchema.new(column_type: 'String', is_read_only: true,
100
+ is_sortable: false))
101
+ add_field('transaction_reference', ColumnSchema.new(column_type: 'String', is_read_only: true,
102
+ is_sortable: false))
103
+ add_field('authorization_id', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
104
+ add_field('payment_reference', ColumnSchema.new(column_type: 'String', is_read_only: true,
105
+ is_sortable: false))
106
+ add_field('network', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
107
+ add_field('merchant_id', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
108
+ add_field('fee_amount', ColumnSchema.new(column_type: 'Number', is_read_only: true, is_sortable: false))
109
+ add_field('fee_amount_currency', ColumnSchema.new(column_type: 'String', is_read_only: true,
110
+ is_sortable: false))
111
+ add_field('net_amount', ColumnSchema.new(column_type: 'Number', is_read_only: true, is_sortable: false))
112
+ add_field('net_amount_currency', ColumnSchema.new(column_type: 'String', is_read_only: true,
113
+ is_sortable: false))
114
+ add_field('reconciliation_status', ColumnSchema.new(column_type: 'Enum',
115
+ enum_values: ENUM_RECONCILIATION_STATUS,
116
+ is_read_only: true, is_sortable: true))
117
+ add_field('reconciled_amount', ColumnSchema.new(column_type: 'Number', is_read_only: true, is_sortable: false))
118
+ add_field('cbs_data', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
119
+ add_field('lending', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
120
+ add_field('metadata', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
121
+ add_field('canceled_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
122
+ add_field('updated_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
123
+ add_field('created_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
124
+ end
125
+
126
+ def define_relations
127
+ add_field('connected_account', ManyToOneSchema.new(
128
+ foreign_collection: 'MambuConnectedAccount',
129
+ foreign_key: 'connected_account_id',
130
+ foreign_key_target: 'id'
131
+ ))
132
+ end
133
+ end
134
+ end
135
+ end
136
+ # rubocop:enable Metrics/ClassLength, Metrics/MethodLength
@@ -0,0 +1,132 @@
1
+ # rubocop:disable Metrics/ClassLength, Metrics/MethodLength
2
+ module ForestAdminDatasourceMambuPayments
3
+ module Collections
4
+ class PaymentOrder < BaseCollection
5
+ ManyToOneSchema = ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema
6
+
7
+ ENUM_DIRECTION = %w[debit credit].freeze
8
+
9
+ client_resource :payment_order
10
+
11
+ def initialize(datasource)
12
+ super(datasource, 'MambuPaymentOrder')
13
+ define_schema
14
+ define_relations
15
+ reconcile_filter_operators!
16
+ end
17
+
18
+ def create(_caller, data)
19
+ serialize(datasource.client.create_payment_order(build_payload(data)))
20
+ end
21
+
22
+ def update(caller, filter, patch)
23
+ payload = build_payload(patch)
24
+ ids_for(caller, filter).each { |id| datasource.client.update_payment_order(id, payload) }
25
+ end
26
+
27
+ def delete(caller, filter)
28
+ ids_for(caller, filter).each { |id| datasource.client.delete_payment_order(id) }
29
+ end
30
+
31
+ def serialize(record)
32
+ a = attrs_of(record)
33
+ {
34
+ 'id' => a['id'],
35
+ 'connected_account_id' => a['connected_account_id'],
36
+ 'receiving_account_id' => a['receiving_account_id'],
37
+ 'type' => a['type'],
38
+ 'direction' => a['direction'],
39
+ 'status' => a['status'],
40
+ 'amount' => a['amount'],
41
+ 'currency' => a['currency'],
42
+ 'reference' => a['reference'],
43
+ 'purpose' => a['purpose'],
44
+ 'end_to_end_id' => a['end_to_end_id'],
45
+ 'idempotency_key' => a['idempotency_key'],
46
+ 'requested_execution_date' => a['requested_execution_date'],
47
+ 'value_date' => a['value_date'],
48
+ 'initiated_at' => a['initiated_at'],
49
+ 'reconciliation_status' => a['reconciliation_status'],
50
+ 'reconciled_amount' => a['reconciled_amount'],
51
+ 'originating_account' => a['originating_account'],
52
+ 'receiving_account' => a['receiving_account'],
53
+ 'metadata' => a['metadata'],
54
+ 'custom_fields' => a['custom_fields'],
55
+ 'created_at' => a['created_at']
56
+ }
57
+ end
58
+
59
+ protected
60
+
61
+ # NOTE: server-side filters verified against Numeral's `GET /payment_orders` docs.
62
+ # Add new entries here (status, direction, currency, created_at ranges, …) as
63
+ # we confirm them — anything not declared raises a clear error rather than
64
+ # silently returning unfiltered results.
65
+ def collection_filters
66
+ {
67
+ 'connected_account_id' => { ops: [Operators::EQUAL, Operators::IN] },
68
+ # Numeral's list endpoint exposes the receiving external account
69
+ # under the `external_account_id` query param.
70
+ 'receiving_account_id' => { ops: [Operators::EQUAL, Operators::IN],
71
+ param: 'external_account_id' }
72
+ }
73
+ end
74
+
75
+ def many_to_one_embeds
76
+ [
77
+ { foreign_key: 'connected_account_id', relation_name: 'connected_account',
78
+ collection: 'MambuConnectedAccount' },
79
+ { foreign_key: 'receiving_account_id', relation_name: 'external_account',
80
+ collection: 'MambuExternalAccount' }
81
+ ]
82
+ end
83
+
84
+ private
85
+
86
+ def define_schema
87
+ add_field('id', ColumnSchema.new(column_type: 'String', is_primary_key: true,
88
+ is_read_only: true, is_sortable: true))
89
+ add_field('connected_account_id', ColumnSchema.new(column_type: 'String',
90
+ is_read_only: false, is_sortable: true))
91
+ add_field('receiving_account_id', ColumnSchema.new(column_type: 'String',
92
+ is_read_only: true, is_sortable: true))
93
+ add_field('type', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: true))
94
+ add_field('direction', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_DIRECTION,
95
+ is_read_only: false, is_sortable: false))
96
+ add_field('status', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: true))
97
+ add_field('amount', ColumnSchema.new(column_type: 'Number', is_read_only: false, is_sortable: false))
98
+ add_field('currency', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
99
+ add_field('reference', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
100
+ add_field('purpose', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
101
+ add_field('end_to_end_id', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
102
+ add_field('idempotency_key', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
103
+ add_field('requested_execution_date', ColumnSchema.new(column_type: 'Date', is_read_only: false,
104
+ is_sortable: true))
105
+ add_field('value_date', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
106
+ add_field('initiated_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
107
+ add_field('reconciliation_status', ColumnSchema.new(column_type: 'String', is_read_only: true,
108
+ is_sortable: false))
109
+ add_field('reconciled_amount', ColumnSchema.new(column_type: 'Number', is_read_only: true, is_sortable: false))
110
+ add_field('originating_account', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
111
+ add_field('receiving_account', ColumnSchema.new(column_type: 'Json', is_read_only: true, is_sortable: false))
112
+ add_field('metadata', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
113
+ add_field('custom_fields', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
114
+ add_field('created_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
115
+ end
116
+
117
+ def define_relations
118
+ add_field('connected_account', ManyToOneSchema.new(
119
+ foreign_collection: 'MambuConnectedAccount',
120
+ foreign_key: 'connected_account_id',
121
+ foreign_key_target: 'id'
122
+ ))
123
+ add_field('external_account', ManyToOneSchema.new(
124
+ foreign_collection: 'MambuExternalAccount',
125
+ foreign_key: 'receiving_account_id',
126
+ foreign_key_target: 'id'
127
+ ))
128
+ end
129
+ end
130
+ end
131
+ end
132
+ # rubocop:enable Metrics/ClassLength, Metrics/MethodLength
@@ -0,0 +1,93 @@
1
+ module ForestAdminDatasourceMambuPayments
2
+ module Collections
3
+ class Reconciliation < BaseCollection
4
+ ManyToOneSchema = ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema
5
+
6
+ ENUM_MATCH_TYPE = %w[manual auto].freeze
7
+ ENUM_PAYMENT_TYPE = %w[payment_order incoming_payment return expected_payment payment_capture].freeze
8
+
9
+ client_resource :reconciliation
10
+
11
+ def initialize(datasource)
12
+ super(datasource, 'MambuReconciliation')
13
+ define_schema
14
+ define_relations
15
+ reconcile_filter_operators!
16
+ end
17
+
18
+ def create(_caller, data)
19
+ serialize(datasource.client.create_reconciliation(build_payload(data)))
20
+ end
21
+
22
+ def update(caller, filter, patch)
23
+ payload = build_payload(patch)
24
+ ids_for(caller, filter).each { |id| datasource.client.update_reconciliation(id, payload) }
25
+ end
26
+
27
+ def serialize(record)
28
+ a = attrs_of(record)
29
+ {
30
+ 'id' => a['id'],
31
+ 'object' => a['object'],
32
+ 'transaction_id' => a['transaction_id'],
33
+ 'payment_id' => a['payment_id'],
34
+ 'payment_type' => a['payment_type'],
35
+ 'amount' => a['amount'],
36
+ 'match_type' => a['match_type'],
37
+ 'metadata' => a['metadata'],
38
+ 'canceled_at' => a['canceled_at'],
39
+ 'created_at' => a['created_at']
40
+ }
41
+ end
42
+
43
+ protected
44
+
45
+ def collection_filters
46
+ {
47
+ 'transaction_id' => { ops: [Operators::EQUAL, Operators::IN] },
48
+ 'payment_id' => { ops: [Operators::EQUAL, Operators::IN] },
49
+ 'payment_type' => { ops: [Operators::EQUAL, Operators::IN] },
50
+ 'match_type' => { ops: [Operators::EQUAL, Operators::IN] }
51
+ }
52
+ end
53
+
54
+ def many_to_one_embeds
55
+ [
56
+ { foreign_key: 'transaction_id', relation_name: 'transaction', collection: 'MambuTransaction' }
57
+ ]
58
+ end
59
+
60
+ private
61
+
62
+ def define_schema
63
+ add_field('id', ColumnSchema.new(column_type: 'String', is_primary_key: true,
64
+ is_read_only: true, is_sortable: true))
65
+ add_field('object', ColumnSchema.new(column_type: 'String', is_read_only: true, is_sortable: false))
66
+ # transaction_id is set on create and never mutated afterwards — Numeral
67
+ # rejects PATCH on anything besides metadata.
68
+ add_field('transaction_id', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: true))
69
+ # payment_id is polymorphic in Numeral (payment_order / incoming_payment /
70
+ # return / expected_payment / payment_capture, discriminated by
71
+ # payment_type). Forest can't model that natively, so we expose it as
72
+ # a plain string column rather than a typed ManyToOne.
73
+ add_field('payment_id', ColumnSchema.new(column_type: 'String', is_read_only: false, is_sortable: false))
74
+ add_field('payment_type', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_PAYMENT_TYPE,
75
+ is_read_only: true, is_sortable: true))
76
+ add_field('amount', ColumnSchema.new(column_type: 'Number', is_read_only: false, is_sortable: false))
77
+ add_field('match_type', ColumnSchema.new(column_type: 'Enum', enum_values: ENUM_MATCH_TYPE,
78
+ is_read_only: true, is_sortable: true))
79
+ add_field('metadata', ColumnSchema.new(column_type: 'Json', is_read_only: false, is_sortable: false))
80
+ add_field('canceled_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
81
+ add_field('created_at', ColumnSchema.new(column_type: 'Date', is_read_only: true, is_sortable: true))
82
+ end
83
+
84
+ def define_relations
85
+ add_field('transaction', ManyToOneSchema.new(
86
+ foreign_collection: 'MambuTransaction',
87
+ foreign_key: 'transaction_id',
88
+ foreign_key_target: 'id'
89
+ ))
90
+ end
91
+ end
92
+ end
93
+ end