-
Notifications
You must be signed in to change notification settings - Fork 7
/
allocate.py
99 lines (84 loc) · 4.55 KB
/
allocate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from decimal import Decimal
from typing import Callable
from logic_bank.exec_row_logic.logic_row import LogicRow
from logic_bank.rule_type.row_event import EarlyRowEvent
# see logicbank - examples/payment_allocation
# not currently tested in ApiLogicServer...
class Allocate(EarlyRowEvent):
"""
Allocates anAmount from a Provider to Recipients, creating Allocation rows.
@see https://github.com/valhuber/LogicBank/wiki/Sample-Project---Allocation
"""
def __init__(self, provider: object,
creating_allocation: object, # eg, PaymentAllocation (junction)
recipients: Callable = None,
while_calling_allocator: Callable = None):
self.recipients = recipients
if recipients is None:
raise Exception("Recipients lambda is required")
self.creating_allocation = creating_allocation # Custom Rule Arguments
self.while_calling_allocator = while_calling_allocator
super(Allocate, self).__init__(provider, None)
def __str__(self):
creating = str(self.creating_allocation)
nodes = creating.split('.')
last_node = nodes[len(nodes)-1]
last_node = last_node[0: len(last_node)-2]
return f'Allocate Rule, creating: {last_node}'
def execute(self, logic_row: LogicRow):
"""
Called by logic engine, overriding generic EarlyEvent rule.
Creates allocation row for each recipient until while_calling_allocator returns false
:return:
"""
logic_row.log(f'BEGIN {str(self)}')
provider = logic_row
to_recipients = self.recipients(provider)
for each_recipient in to_recipients:
old_way = False
if old_way:
new_allocation = self.creating_allocation()
new_allocation_logic_row = LogicRow(row=new_allocation, old_row=new_allocation,
ins_upd_dlt="ins",
nest_level=provider.nest_level + 1,
a_session=provider.session,
row_sets=provider.row_sets)
else:
new_allocation_logic_row = logic_row.new_logic_row(self.creating_allocation)
new_allocation_logic_row.link(to_parent=provider)
each_recipient_logic_row = LogicRow(row=each_recipient, old_row=each_recipient,
ins_upd_dlt="*", nest_level=0,
a_session=provider.session,
row_sets=None)
new_allocation_logic_row.link(to_parent=each_recipient_logic_row)
if self.while_calling_allocator is not None:
allocator = self.while_calling_allocator(new_allocation_logic_row,
provider)
else:
allocator = self.while_calling_allocator_default(new_allocation_logic_row,
provider)
if not allocator:
break
provider.log(f'END {str(self)}') # last log for payment_allo example
return self
def while_calling_allocator_default(self, allocation_logic_row, provider_logic_row) -> bool:
"""
Called for each created allocation, to
* insert the created allocation (triggering rules that compute `Allocation.AmountAllocated`)
* reduce Provider.AmountUnAllocated
* return boolean indicating whether Provider.AmountUnAllocated > 0 (remains)
This uses default names:
* provider.Amount
* provider.AmountUnallocated
* allocation.AmountAllocated
To use your names, copy this code and alter as as required
:param allocation_logic_row: allocation row being created
:param provider_logic_row: provider
:return: provider has AmountUnAllocated remaining
"""
if provider_logic_row.row.AmountUnAllocated is None:
provider_logic_row.row.AmountUnAllocated = provider_logic_row.row.Amount # initialization
allocation_logic_row.insert(reason="Allocate " + provider_logic_row.name) # triggers rules, eg AmountAllocated
provider_logic_row.row.AmountUnAllocated = \
provider_logic_row.row.AmountUnAllocated - allocation_logic_row.row.AmountAllocated
return provider_logic_row.row.AmountUnAllocated > 0 # terminate allocation loop if none left