@pax2pay/model-banking 0.1.384 → 0.1.386
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/Card/Creatable.ts +2 -1
- package/Card/index.ts +2 -1
- package/Operation/Changes.ts +25 -0
- package/Operation/Creatable.ts +1 -18
- package/Organization/index.ts +2 -1
- package/Rule/{Rule/Base.ts → Base.ts} +7 -0
- package/Rule/Charge.ts +108 -0
- package/Rule/Other.ts +48 -0
- package/Rule/Reserve.ts +111 -0
- package/Rule/Score.ts +45 -0
- package/Rule/State/Transaction.ts +17 -4
- package/Rule/State/index.ts +3 -2
- package/Rule/control.ts +8 -0
- package/Rule/index.ts +76 -97
- package/Rule/type.ts +13 -0
- package/Transaction/index.ts +3 -3
- package/dist/Authorization/Status.d.ts +1 -1
- package/dist/Card/Creatable.d.ts +1 -1
- package/dist/Card/Creatable.js +1 -1
- package/dist/Card/Creatable.js.map +1 -1
- package/dist/Card/index.d.ts +1 -1
- package/dist/Card/index.js +1 -1
- package/dist/Card/index.js.map +1 -1
- package/dist/Operation/Changes.d.ts +2 -0
- package/dist/Operation/Changes.js +20 -0
- package/dist/Operation/Changes.js.map +1 -1
- package/dist/Operation/Creatable.d.ts +1 -1
- package/dist/Operation/Creatable.js +1 -13
- package/dist/Operation/Creatable.js.map +1 -1
- package/dist/Organization/index.d.ts +3 -3
- package/dist/Organization/index.js +1 -1
- package/dist/Organization/index.js.map +1 -1
- package/dist/Rail/index.d.ts +1 -1
- package/dist/Rule/{Rule/Base.d.ts → Base.d.ts} +1 -0
- package/dist/Rule/{Rule/Base.js → Base.js} +6 -0
- package/dist/Rule/Base.js.map +1 -0
- package/dist/Rule/Charge.d.ts +40 -0
- package/dist/Rule/Charge.js +68 -0
- package/dist/Rule/Charge.js.map +1 -0
- package/dist/Rule/{Rule/Other.d.ts → Other.d.ts} +8 -0
- package/dist/Rule/Other.js +38 -0
- package/dist/Rule/Other.js.map +1 -0
- package/dist/Rule/Reserve.d.ts +38 -0
- package/dist/Rule/Reserve.js +76 -0
- package/dist/Rule/Reserve.js.map +1 -0
- package/dist/Rule/{Rule/Score.d.ts → Score.d.ts} +6 -0
- package/dist/Rule/Score.js +30 -0
- package/dist/Rule/Score.js.map +1 -0
- package/dist/Rule/State/Transaction.d.ts +9 -4
- package/dist/Rule/State/Transaction.js +7 -2
- package/dist/Rule/State/Transaction.js.map +1 -1
- package/dist/Rule/State/index.d.ts +3 -3
- package/dist/Rule/State/index.js +2 -2
- package/dist/Rule/State/index.js.map +1 -1
- package/dist/Rule/control.d.ts +4 -0
- package/dist/Rule/control.js +6 -0
- package/dist/Rule/control.js.map +1 -0
- package/dist/Rule/index.d.ts +29 -13
- package/dist/Rule/index.js +68 -70
- package/dist/Rule/index.js.map +1 -1
- package/dist/Rule/type.d.ts +3 -0
- package/dist/Rule/type.js +7 -0
- package/dist/Rule/type.js.map +1 -0
- package/dist/Transaction/Status.d.ts +1 -1
- package/dist/Transaction/index.js +3 -3
- package/dist/Transaction/index.js.map +1 -1
- package/package.json +1 -1
- package/Rule/Rule/Charge.ts +0 -48
- package/Rule/Rule/Other.ts +0 -14
- package/Rule/Rule/Score.ts +0 -21
- package/Rule/Rule/index.ts +0 -37
- package/dist/Rule/Rule/Base.js.map +0 -1
- package/dist/Rule/Rule/Charge.d.ts +0 -29
- package/dist/Rule/Rule/Charge.js +0 -34
- package/dist/Rule/Rule/Charge.js.map +0 -1
- package/dist/Rule/Rule/Other.js +0 -12
- package/dist/Rule/Rule/Other.js.map +0 -1
- package/dist/Rule/Rule/Score.js +0 -16
- package/dist/Rule/Rule/Score.js.map +0 -1
- package/dist/Rule/Rule/index.d.ts +0 -24
- package/dist/Rule/Rule/index.js +0 -27
- package/dist/Rule/Rule/index.js.map +0 -1
package/Card/Creatable.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { isoly } from "isoly"
|
|
2
2
|
import { isly } from "isly"
|
|
3
3
|
import { Amount } from "../Amount"
|
|
4
|
-
import { Rule
|
|
4
|
+
import type { Rule } from "../Rule"
|
|
5
|
+
import { type as ruleType } from "../Rule/type"
|
|
5
6
|
import { Expiry } from "./Expiry"
|
|
6
7
|
import { Meta } from "./Meta"
|
|
7
8
|
import { Preset } from "./Preset"
|
package/Card/index.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { isly } from "isly"
|
|
|
3
3
|
import { Amount } from "../Amount"
|
|
4
4
|
import { Realm } from "../Realm"
|
|
5
5
|
import { Report } from "../Report"
|
|
6
|
-
import { Rule
|
|
6
|
+
import type { Rule } from "../Rule"
|
|
7
|
+
import { type as ruleType } from "../Rule/type"
|
|
7
8
|
import { Changeable as CardChangeable } from "./Changeable"
|
|
8
9
|
import { Creatable as CardCreatable } from "./Creatable"
|
|
9
10
|
import { Expiry as CardExpiry } from "./Expiry"
|
package/Operation/Changes.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { isoly } from "isoly"
|
|
|
2
2
|
import { isly } from "isly"
|
|
3
3
|
import { Balance as AccountBalance } from "../Balance"
|
|
4
4
|
import { Counterbalance as CounterbalanceOperation } from "../Counterbalance"
|
|
5
|
+
import type { Settlement } from "../Settlement"
|
|
5
6
|
import { Change } from "./Change"
|
|
6
7
|
|
|
7
8
|
export type Changes = Partial<Record<Changes.Entry.Balance, Change>> & Record<Changes.Entry.Counterbalance, Change>
|
|
@@ -113,4 +114,28 @@ export namespace Changes {
|
|
|
113
114
|
}),
|
|
114
115
|
}
|
|
115
116
|
}
|
|
117
|
+
export function fromRefund(
|
|
118
|
+
settlement: string | undefined, // FIXME: remove | undefined when we're sure we send the id
|
|
119
|
+
refund: Settlement.Entry.Refund.Creatable,
|
|
120
|
+
charge: number | undefined,
|
|
121
|
+
sum: Sum
|
|
122
|
+
): Changes {
|
|
123
|
+
const currency = refund.amount[0]
|
|
124
|
+
const fee = refund.fee.other[currency] ?? 0
|
|
125
|
+
const net = refund.amount[1]
|
|
126
|
+
const available = isoly.Currency.subtract(currency, isoly.Currency.add(currency, fee, net), charge ?? 0)
|
|
127
|
+
return {
|
|
128
|
+
available: { type: available > 0 ? "add" : "subtract", amount: Math.abs(available), status: "pending" },
|
|
129
|
+
["reserved-incoming"]: {
|
|
130
|
+
type: "subtract",
|
|
131
|
+
amount: sum["reserved-incoming"] ?? 0,
|
|
132
|
+
status: "pending",
|
|
133
|
+
},
|
|
134
|
+
[`${settlement}-net`]: { type: "subtract" as const, amount: net, status: "pending" as const },
|
|
135
|
+
[`${settlement}-fee`]: { type: "subtract" as const, amount: fee, status: "pending" as const },
|
|
136
|
+
...(charge && {
|
|
137
|
+
[`${settlement}-charge`]: { type: "add" as const, amount: charge, status: "pending" as const },
|
|
138
|
+
}),
|
|
139
|
+
}
|
|
140
|
+
}
|
|
116
141
|
}
|
package/Operation/Creatable.ts
CHANGED
|
@@ -38,12 +38,7 @@ export namespace Creatable {
|
|
|
38
38
|
})
|
|
39
39
|
export const is = type.is
|
|
40
40
|
export const flaw = type.flaw
|
|
41
|
-
export function fromRefund(
|
|
42
|
-
account: string,
|
|
43
|
-
settlement: string,
|
|
44
|
-
entry: Settlement.Entry.Refund.Creatable,
|
|
45
|
-
charge: number
|
|
46
|
-
): Creatable {
|
|
41
|
+
export function fromRefund(account: string, settlement: string, entry: Settlement.Entry.Refund.Creatable): Creatable {
|
|
47
42
|
// The Entry.Refund.Creatable has negative amount and fee
|
|
48
43
|
// The operation amounts should always be positive
|
|
49
44
|
const [currency, entryAmount] = entry.amount
|
|
@@ -69,18 +64,6 @@ export namespace Creatable {
|
|
|
69
64
|
amount: operationFee,
|
|
70
65
|
status: "pending" as const,
|
|
71
66
|
},
|
|
72
|
-
...(charge > 0 && {
|
|
73
|
-
[`${settlement}-charge`]: {
|
|
74
|
-
type: "add" as const,
|
|
75
|
-
amount: charge,
|
|
76
|
-
status: "pending" as const,
|
|
77
|
-
},
|
|
78
|
-
available: {
|
|
79
|
-
type: "subtract" as const,
|
|
80
|
-
amount: charge,
|
|
81
|
-
status: "pending" as const,
|
|
82
|
-
},
|
|
83
|
-
}),
|
|
84
67
|
},
|
|
85
68
|
}
|
|
86
69
|
}
|
package/Organization/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isly } from "isly"
|
|
2
2
|
import { Realm } from "../Realm"
|
|
3
|
-
import { Rule
|
|
3
|
+
import { Rule } from "../Rule"
|
|
4
|
+
import { type as ruleType } from "../Rule/type"
|
|
4
5
|
import { Changeable as OrganizationChangeable } from "./Changeable"
|
|
5
6
|
import { Contact as OrganizationContact } from "./Contact"
|
|
6
7
|
|
|
@@ -15,6 +15,13 @@ export namespace Base {
|
|
|
15
15
|
export namespace Kind {
|
|
16
16
|
export const values = ["authorization", "outbound", "inbound", "capture", "refund"] as const
|
|
17
17
|
export const type = isly.string<Kind>(values)
|
|
18
|
+
export function is(kind: Kind, rule: Base, groups: string[] | undefined): boolean {
|
|
19
|
+
return (
|
|
20
|
+
kind == rule.type &&
|
|
21
|
+
(!rule.groups ||
|
|
22
|
+
rule.groups.some(ruleGroup => groups?.some(organizationGroup => organizationGroup == ruleGroup)))
|
|
23
|
+
)
|
|
24
|
+
}
|
|
18
25
|
}
|
|
19
26
|
export type Category = typeof Category.values[number]
|
|
20
27
|
export namespace Category {
|
package/Rule/Charge.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isoly } from "isoly"
|
|
2
|
+
import { selectively } from "selectively"
|
|
3
|
+
import { isly } from "isly"
|
|
4
|
+
import { Amount } from "../Amount"
|
|
5
|
+
import { Exchange } from "../Exchange"
|
|
6
|
+
import { Realm } from "../Realm"
|
|
7
|
+
import { Base } from "./Base"
|
|
8
|
+
import { control } from "./control"
|
|
9
|
+
import type { State } from "./State"
|
|
10
|
+
|
|
11
|
+
export interface Charge extends Charge.Api {
|
|
12
|
+
charge: {
|
|
13
|
+
percentage?: number
|
|
14
|
+
fixed?: Amount
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export namespace Charge {
|
|
18
|
+
export type Action = typeof Action.value
|
|
19
|
+
export namespace Action {
|
|
20
|
+
export const value = "charge"
|
|
21
|
+
}
|
|
22
|
+
export interface Api extends Base {
|
|
23
|
+
action: Charge.Action
|
|
24
|
+
charge: {
|
|
25
|
+
percentage?: number
|
|
26
|
+
fixed?: number | Amount
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export namespace Api {
|
|
30
|
+
export const type = Base.type.extend<Api>({
|
|
31
|
+
action: isly.string(Action.value),
|
|
32
|
+
charge: isly.object({
|
|
33
|
+
percentage: isly.number().optional(),
|
|
34
|
+
fixed: isly.union<number | Amount>(Amount.type, isly.number()).optional(),
|
|
35
|
+
}),
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
export function fromApi(rule: Api, realm: Realm): Charge
|
|
39
|
+
export function fromApi(rule: Api, currency: isoly.Currency): Charge
|
|
40
|
+
export function fromApi(rule: Api, currency: Realm | isoly.Currency): Charge {
|
|
41
|
+
return {
|
|
42
|
+
...rule,
|
|
43
|
+
charge: {
|
|
44
|
+
...rule.charge,
|
|
45
|
+
fixed:
|
|
46
|
+
typeof rule.charge.fixed == "number"
|
|
47
|
+
? [Realm.is(currency) ? Realm.currency[currency] : currency, rule.charge.fixed]
|
|
48
|
+
: rule.charge.fixed,
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function evaluate(
|
|
53
|
+
rules: Charge[],
|
|
54
|
+
state: State,
|
|
55
|
+
macros?: Record<string, selectively.Definition>,
|
|
56
|
+
table: Exchange.Rates = {}
|
|
57
|
+
): { outcomes: Charge[]; charge: Required<State["transaction"]["original"]>["charge"] } {
|
|
58
|
+
const result: ReturnType<typeof evaluate> = {
|
|
59
|
+
outcomes: [],
|
|
60
|
+
charge: { current: 0, total: state.transaction.original.charge?.total ?? 0 },
|
|
61
|
+
}
|
|
62
|
+
if (state.transaction.stage == "finalize" && ["card", "external"].some(type => type == state.transaction.type))
|
|
63
|
+
for (const rule of rules) {
|
|
64
|
+
if (control(rule, state, macros)) {
|
|
65
|
+
if (rule.charge.percentage)
|
|
66
|
+
result.charge.current = isoly.Currency.add(
|
|
67
|
+
state.transaction.original.currency,
|
|
68
|
+
result.charge.current,
|
|
69
|
+
isoly.Currency.multiply(
|
|
70
|
+
state.transaction.original.currency,
|
|
71
|
+
state.transaction.original.amount,
|
|
72
|
+
rule.charge.percentage / 100
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
if (rule.charge.fixed) {
|
|
76
|
+
const charge =
|
|
77
|
+
state.transaction.original.currency === rule.charge.fixed[0]
|
|
78
|
+
? rule.charge.fixed[1]
|
|
79
|
+
: Exchange.convert(
|
|
80
|
+
rule.charge.fixed[1],
|
|
81
|
+
rule.charge.fixed[0],
|
|
82
|
+
state.transaction.original.currency,
|
|
83
|
+
table
|
|
84
|
+
) ?? 0
|
|
85
|
+
result.charge.current = isoly.Currency.add(
|
|
86
|
+
state.transaction.original.currency,
|
|
87
|
+
result.charge.current,
|
|
88
|
+
charge
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
result.outcomes.push(rule)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
result.charge.total = isoly.Currency.add(
|
|
95
|
+
state.transaction.original.currency,
|
|
96
|
+
result.charge.current,
|
|
97
|
+
result.charge.total
|
|
98
|
+
)
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
export function apply(charge: { current: number; total: number }, state: State): number {
|
|
102
|
+
return state.transaction.kind == "authorization" ||
|
|
103
|
+
state.transaction.kind == "outbound" ||
|
|
104
|
+
state.transaction.kind == "capture"
|
|
105
|
+
? isoly.Currency.add(state.transaction.original.currency, state.transaction.original.total, charge.current)
|
|
106
|
+
: isoly.Currency.subtract(state.transaction.original.currency, state.transaction.original.total, charge.current)
|
|
107
|
+
}
|
|
108
|
+
}
|
package/Rule/Other.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isoly } from "isoly"
|
|
2
|
+
import { selectively } from "selectively"
|
|
3
|
+
import { isly } from "isly"
|
|
4
|
+
import type { Note } from "../Transaction/Note"
|
|
5
|
+
import { Base } from "./Base"
|
|
6
|
+
import { control } from "./control"
|
|
7
|
+
import type { State } from "./State"
|
|
8
|
+
|
|
9
|
+
export interface Other extends Base {
|
|
10
|
+
action: Other.Action
|
|
11
|
+
}
|
|
12
|
+
export namespace Other {
|
|
13
|
+
export type Action = typeof Action.values[number]
|
|
14
|
+
export namespace Action {
|
|
15
|
+
export const values = ["review", "reject", "flag"] as const
|
|
16
|
+
export const type = isly.string<Action>(values)
|
|
17
|
+
}
|
|
18
|
+
export const type = Base.type.extend<Other>({ action: Action.type })
|
|
19
|
+
export function evaluate(
|
|
20
|
+
rules: Other[],
|
|
21
|
+
state: State,
|
|
22
|
+
macros?: Record<string, selectively.Definition>
|
|
23
|
+
): { outcomes: Record<Other.Action, Other[]>; notes: Note[]; flags: Set<string> } {
|
|
24
|
+
const now = isoly.DateTime.now()
|
|
25
|
+
const result: ReturnType<typeof evaluate> = {
|
|
26
|
+
outcomes: {
|
|
27
|
+
review: [],
|
|
28
|
+
reject: [],
|
|
29
|
+
flag: [],
|
|
30
|
+
},
|
|
31
|
+
notes: [],
|
|
32
|
+
flags: new Set<string>(),
|
|
33
|
+
}
|
|
34
|
+
if (
|
|
35
|
+
state.transaction.stage == "initiate" &&
|
|
36
|
+
["card", "external", "internal"].some(type => type == state.transaction.type)
|
|
37
|
+
)
|
|
38
|
+
for (const rule of rules) {
|
|
39
|
+
if (control(rule, state, macros)) {
|
|
40
|
+
result.outcomes[rule.action].push(rule)
|
|
41
|
+
result.notes.push({ author: "automatic", created: now, text: rule.name, rule })
|
|
42
|
+
rule.flags.forEach(f => result.flags.add(f))
|
|
43
|
+
rule.action == "review" && result.flags.add("review")
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result
|
|
47
|
+
}
|
|
48
|
+
}
|
package/Rule/Reserve.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { isoly } from "isoly"
|
|
2
|
+
import { selectively } from "selectively"
|
|
3
|
+
import { isly } from "isly"
|
|
4
|
+
import { Amount } from "../Amount"
|
|
5
|
+
import { Exchange } from "../Exchange"
|
|
6
|
+
import { Realm } from "../Realm"
|
|
7
|
+
import { Base } from "./Base"
|
|
8
|
+
import { control } from "./control"
|
|
9
|
+
import type { State } from "./State"
|
|
10
|
+
|
|
11
|
+
export interface Reserve extends Reserve.Api {
|
|
12
|
+
reserve: {
|
|
13
|
+
percentage?: number
|
|
14
|
+
fixed?: Amount
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export namespace Reserve {
|
|
18
|
+
export type Action = typeof Action.value
|
|
19
|
+
export namespace Action {
|
|
20
|
+
export const value = "reserve"
|
|
21
|
+
}
|
|
22
|
+
export const type = Base.type.extend<Reserve>({
|
|
23
|
+
action: isly.string(Action.value),
|
|
24
|
+
reserve: isly.object({
|
|
25
|
+
percentage: isly.number(),
|
|
26
|
+
fixed: Amount.type.optional(),
|
|
27
|
+
}),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export interface Api extends Base {
|
|
31
|
+
action: Reserve.Action
|
|
32
|
+
reserve: {
|
|
33
|
+
percentage?: number
|
|
34
|
+
fixed?: number | Amount
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export namespace Api {
|
|
38
|
+
export const type = Base.type.extend<Api>({
|
|
39
|
+
action: isly.string(Action.value),
|
|
40
|
+
reserve: isly.object({
|
|
41
|
+
percentage: isly.number().optional(),
|
|
42
|
+
fixed: isly.union<number | Amount>(Amount.type, isly.number()).optional(),
|
|
43
|
+
}),
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
export function fromApi(rule: Api, realm: Realm): Reserve
|
|
47
|
+
export function fromApi(rule: Api, currency: isoly.Currency): Reserve
|
|
48
|
+
export function fromApi(rule: Api, currency: Realm | isoly.Currency): Reserve {
|
|
49
|
+
return {
|
|
50
|
+
...rule,
|
|
51
|
+
reserve: {
|
|
52
|
+
...rule.reserve,
|
|
53
|
+
fixed:
|
|
54
|
+
typeof rule.reserve.fixed == "number"
|
|
55
|
+
? [Realm.is(currency) ? Realm.currency[currency] : currency, rule.reserve.fixed]
|
|
56
|
+
: rule.reserve.fixed,
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function evaluate(
|
|
61
|
+
rules: Reserve[],
|
|
62
|
+
state: State,
|
|
63
|
+
macros?: Record<string, selectively.Definition>,
|
|
64
|
+
table: Exchange.Rates = {}
|
|
65
|
+
): { outcomes: Reserve[]; reserve: number } {
|
|
66
|
+
const result: ReturnType<typeof evaluate> = {
|
|
67
|
+
outcomes: [],
|
|
68
|
+
reserve: 0,
|
|
69
|
+
}
|
|
70
|
+
if (
|
|
71
|
+
state.transaction.stage == "initiate" &&
|
|
72
|
+
["authorization", "outbound"].some(kind => kind == state.transaction.kind) &&
|
|
73
|
+
["card", "external"].some(type => type == state.transaction.type)
|
|
74
|
+
)
|
|
75
|
+
for (const rule of rules) {
|
|
76
|
+
if (control(rule, state, macros)) {
|
|
77
|
+
if (rule.reserve.percentage)
|
|
78
|
+
result.reserve = isoly.Currency.add(
|
|
79
|
+
state.transaction.original.currency,
|
|
80
|
+
result.reserve,
|
|
81
|
+
isoly.Currency.multiply(
|
|
82
|
+
state.transaction.original.currency,
|
|
83
|
+
state.transaction.original.amount,
|
|
84
|
+
rule.reserve.percentage / 100
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
if (rule.reserve.fixed) {
|
|
88
|
+
const reserve =
|
|
89
|
+
state.transaction.original.currency === rule.reserve.fixed[0]
|
|
90
|
+
? rule.reserve.fixed[1]
|
|
91
|
+
: Exchange.convert(
|
|
92
|
+
rule.reserve.fixed[1],
|
|
93
|
+
rule.reserve.fixed[0],
|
|
94
|
+
state.transaction.original.currency,
|
|
95
|
+
table
|
|
96
|
+
) ?? 0
|
|
97
|
+
result.reserve = isoly.Currency.add(state.transaction.original.currency, result.reserve, reserve)
|
|
98
|
+
}
|
|
99
|
+
result.outcomes.push(rule)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return result
|
|
103
|
+
}
|
|
104
|
+
export function apply(reserve: number, state: State): number {
|
|
105
|
+
return state.transaction.kind == "authorization" ||
|
|
106
|
+
state.transaction.kind == "outbound" ||
|
|
107
|
+
state.transaction.kind == "capture"
|
|
108
|
+
? isoly.Currency.add(state.transaction.original.currency, state.transaction.original.total, reserve)
|
|
109
|
+
: isoly.Currency.subtract(state.transaction.original.currency, state.transaction.original.total, reserve)
|
|
110
|
+
}
|
|
111
|
+
}
|
package/Rule/Score.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { selectively } from "selectively"
|
|
2
|
+
import { isly } from "isly"
|
|
3
|
+
import { Base } from "./Base"
|
|
4
|
+
import { control } from "./control"
|
|
5
|
+
import type { State } from "./State"
|
|
6
|
+
|
|
7
|
+
export interface Score extends Base {
|
|
8
|
+
action: Score.Action
|
|
9
|
+
category: "fincrime"
|
|
10
|
+
risk: Score.Risk
|
|
11
|
+
}
|
|
12
|
+
export namespace Score {
|
|
13
|
+
export type Action = typeof Action.value
|
|
14
|
+
export namespace Action {
|
|
15
|
+
export const value = "score"
|
|
16
|
+
}
|
|
17
|
+
export type Risk = number
|
|
18
|
+
export const Risk = isly.number<Risk>(["positive", "integer"])
|
|
19
|
+
export const type = Base.type.extend<Score>({
|
|
20
|
+
action: isly.string(Action.value),
|
|
21
|
+
category: isly.string("fincrime"),
|
|
22
|
+
risk: Risk,
|
|
23
|
+
})
|
|
24
|
+
export function evaluate(
|
|
25
|
+
rules: Score[],
|
|
26
|
+
state: State,
|
|
27
|
+
macros?: Record<string, selectively.Definition>
|
|
28
|
+
): {
|
|
29
|
+
outcomes: Score[]
|
|
30
|
+
risk?: number | undefined
|
|
31
|
+
} {
|
|
32
|
+
const result: ReturnType<typeof evaluate> = { outcomes: [] }
|
|
33
|
+
if (
|
|
34
|
+
state.transaction.stage == "initiate" &&
|
|
35
|
+
["card", "external", "internal"].some(type => type == state.transaction.type)
|
|
36
|
+
)
|
|
37
|
+
for (const rule of rules) {
|
|
38
|
+
if (control(rule, state, macros)) {
|
|
39
|
+
result.risk = (result.risk ?? 100) * (rule.risk / 100)
|
|
40
|
+
result.outcomes.push(rule)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
import { isoly } from "isoly"
|
|
2
2
|
import type { Address } from "../../Rail/Address"
|
|
3
3
|
import { Transaction as ModelTransaction } from "../../Transaction"
|
|
4
|
-
import { Rule } from "../
|
|
4
|
+
import type { Rule } from "../index"
|
|
5
5
|
|
|
6
6
|
export interface Transaction extends ModelTransaction.Creatable {
|
|
7
7
|
kind: Rule.Base.Kind
|
|
8
|
+
stage: "finalize" | "initiate"
|
|
8
9
|
amount: number
|
|
9
10
|
type: ModelTransaction.Types
|
|
10
11
|
risk?: number
|
|
11
|
-
original: {
|
|
12
|
+
original: {
|
|
13
|
+
currency: isoly.Currency
|
|
14
|
+
total: number
|
|
15
|
+
amount: number
|
|
16
|
+
charge?: { current: number; total: number }
|
|
17
|
+
reserve?: number
|
|
18
|
+
}
|
|
12
19
|
}
|
|
13
20
|
export namespace Transaction {
|
|
14
21
|
export function from(
|
|
15
22
|
accountName: string,
|
|
16
23
|
transaction: ModelTransaction.Creatable & { counterpart: Address },
|
|
17
|
-
kind: Rule.Base.Kind
|
|
24
|
+
kind: Rule.Base.Kind,
|
|
25
|
+
stage: "finalize" | "initiate"
|
|
18
26
|
): Transaction {
|
|
19
27
|
return {
|
|
20
28
|
...transaction,
|
|
29
|
+
stage,
|
|
21
30
|
kind,
|
|
22
31
|
amount: Math.abs(transaction.amount),
|
|
23
32
|
type: ModelTransaction.getType(transaction.counterpart, accountName),
|
|
24
|
-
original: {
|
|
33
|
+
original: {
|
|
34
|
+
currency: transaction.currency,
|
|
35
|
+
amount: Math.abs(transaction.amount),
|
|
36
|
+
total: Math.abs(transaction.amount),
|
|
37
|
+
},
|
|
25
38
|
}
|
|
26
39
|
}
|
|
27
40
|
}
|
package/Rule/State/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { isly } from "isly"
|
|
|
2
2
|
import { Account as ModelAccount } from "../../Account"
|
|
3
3
|
import type { Address } from "../../Rail/Address"
|
|
4
4
|
import type { Transaction as ModelTransaction } from "../../Transaction"
|
|
5
|
-
import { Rule } from "../
|
|
5
|
+
import type { Rule } from "../index"
|
|
6
6
|
import { Account as StateAccount } from "./Account"
|
|
7
7
|
import { Authorization as StateAuthorization } from "./Authorization"
|
|
8
8
|
import { Card as StateCard } from "./Card"
|
|
@@ -45,6 +45,7 @@ export namespace State {
|
|
|
45
45
|
days: Account.Days,
|
|
46
46
|
transaction: ModelTransaction.Creatable & { counterpart: Address },
|
|
47
47
|
kind: Rule.Base.Kind,
|
|
48
|
+
stage: "finalize" | "initiate",
|
|
48
49
|
authorization?: Authorization,
|
|
49
50
|
card?: Card,
|
|
50
51
|
organization?: Organization
|
|
@@ -52,7 +53,7 @@ export namespace State {
|
|
|
52
53
|
return {
|
|
53
54
|
data,
|
|
54
55
|
account: Account.from(account, transactions, days),
|
|
55
|
-
transaction: Transaction.from(account.name, transaction, kind),
|
|
56
|
+
transaction: Transaction.from(account.name, transaction, kind, stage),
|
|
56
57
|
authorization,
|
|
57
58
|
card,
|
|
58
59
|
organization,
|
package/Rule/control.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { selectively } from "selectively"
|
|
2
|
+
import type { Rule } from "."
|
|
3
|
+
import { definitions } from "./definitions"
|
|
4
|
+
import type { State } from "./State"
|
|
5
|
+
|
|
6
|
+
export function control(rule: Rule, state: State, macros?: Record<string, selectively.Definition>): boolean {
|
|
7
|
+
return selectively.resolve({ ...macros, ...definitions }, selectively.parse(rule.condition)).is(state)
|
|
8
|
+
}
|