Bump platform and requisition throughput on successful trades
This commit is contained in:
parent
9627fb7d41
commit
04f5595a86
|
@ -50,10 +50,13 @@ class AggregatorClient(ABC):
|
||||||
self.instance.currencies = currencies
|
self.instance.currencies = currencies
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
|
|
||||||
async def process_transactions(self, account_id, transactions):
|
async def process_transactions(self, account_id, transactions, req):
|
||||||
if not transactions:
|
if not transactions:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not req:
|
||||||
|
return False
|
||||||
|
|
||||||
platforms = self.instance.platforms
|
platforms = self.instance.platforms
|
||||||
for transaction in transactions:
|
for transaction in transactions:
|
||||||
transaction_id = transaction["transaction_id"]
|
transaction_id = transaction["transaction_id"]
|
||||||
|
@ -71,6 +74,7 @@ class AggregatorClient(ABC):
|
||||||
"note": transaction["reference"],
|
"note": transaction["reference"],
|
||||||
}
|
}
|
||||||
tx_obj = self.instance.add_transaction(
|
tx_obj = self.instance.add_transaction(
|
||||||
|
req,
|
||||||
account_id,
|
account_id,
|
||||||
tx_cast,
|
tx_cast,
|
||||||
)
|
)
|
||||||
|
|
|
@ -294,7 +294,9 @@ class NordigenClient(BaseClient, AggregatorClient):
|
||||||
else:
|
else:
|
||||||
raise Exception(f"No way to get reference: {transaction}")
|
raise Exception(f"No way to get reference: {transaction}")
|
||||||
|
|
||||||
async def get_transactions(self, account_id, process=False, pending=False):
|
async def get_transactions(
|
||||||
|
self, account_id, req=None, process=False, pending=False
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Get all transactions for an account.
|
Get all transactions for an account.
|
||||||
:param account_id: account to fetch transactions for
|
:param account_id: account to fetch transactions for
|
||||||
|
@ -307,7 +309,7 @@ class NordigenClient(BaseClient, AggregatorClient):
|
||||||
self.normalise_transactions(parsed, state="booked")
|
self.normalise_transactions(parsed, state="booked")
|
||||||
|
|
||||||
if process:
|
if process:
|
||||||
await self.process_transactions(account_id, parsed)
|
await self.process_transactions(account_id, parsed, req=req)
|
||||||
if pending:
|
if pending:
|
||||||
parsed_pending = response["pending"]
|
parsed_pending = response["pending"]
|
||||||
self.normalise_transactions(parsed_pending, state="pending")
|
self.normalise_transactions(parsed_pending, state="pending")
|
||||||
|
|
|
@ -750,6 +750,53 @@ class LocalPlatformClient(ABC):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
async def reset_trade_tx(self, stored_trade, tx_obj):
|
||||||
|
"""
|
||||||
|
Remove a trade to point to a given transaction ID.
|
||||||
|
"""
|
||||||
|
if not tx_obj.reconciled:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if tx_obj not in stored_trade.linked.all():
|
||||||
|
return False
|
||||||
|
|
||||||
|
stored_trade.linked.remove(tx_obj)
|
||||||
|
stored_trade.save()
|
||||||
|
|
||||||
|
tx_obj.reconciled = False
|
||||||
|
tx_obj.save()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def successful_release(self, trade, transaction):
|
||||||
|
"""
|
||||||
|
Called when a trade has been successfully released.
|
||||||
|
Increment the platform and requisition throughput by the trade value.
|
||||||
|
Currently only XMR is supported.
|
||||||
|
"""
|
||||||
|
if trade.asset != "XMR":
|
||||||
|
raise NotImplementedError("Only XMR is supported at the moment.")
|
||||||
|
|
||||||
|
# Increment the platform throughput
|
||||||
|
trade.platform.throughput += trade.amount_crypto
|
||||||
|
|
||||||
|
# Increment the requisition throughput
|
||||||
|
if transaction.requisition is not None:
|
||||||
|
transaction.requisition.throughput += trade.amount_crypto
|
||||||
|
|
||||||
|
async def successful_withdrawal(self):
|
||||||
|
platforms = self.instance.platforms.all()
|
||||||
|
aggregators = self.instance.aggregators.all()
|
||||||
|
|
||||||
|
for platform in platforms:
|
||||||
|
platform.throughput = 0
|
||||||
|
platform.save()
|
||||||
|
|
||||||
|
for aggregator in aggregators:
|
||||||
|
for requisition in aggregator.requisitions.all():
|
||||||
|
requisition.throughput = 0
|
||||||
|
requisition.save()
|
||||||
|
|
||||||
async def release_map_trade(self, stored_trade, tx_obj):
|
async def release_map_trade(self, stored_trade, tx_obj):
|
||||||
"""
|
"""
|
||||||
Map a trade to a transaction and release if no other TX is
|
Map a trade to a transaction and release if no other TX is
|
||||||
|
@ -761,9 +808,15 @@ class LocalPlatformClient(ABC):
|
||||||
is_updated = await self.update_trade_tx(stored_trade, tx_obj)
|
is_updated = await self.update_trade_tx(stored_trade, tx_obj)
|
||||||
if is_updated is True:
|
if is_updated is True:
|
||||||
# We mapped the trade successfully
|
# We mapped the trade successfully
|
||||||
await self.release_trade_escrow(trade_id, stored_trade.reference)
|
|
||||||
await antifraud.add_bank_sender(platform_buyer, bank_sender)
|
await antifraud.add_bank_sender(platform_buyer, bank_sender)
|
||||||
return True
|
released = await self.release_trade_escrow(trade_id, stored_trade.reference)
|
||||||
|
if not released:
|
||||||
|
# We failed to release the funds
|
||||||
|
# Set the TX back to not reconciled, so we can try this again
|
||||||
|
await self.reset_trade_tx(stored_trade, tx_obj)
|
||||||
|
return False
|
||||||
|
await self.successful_release(stored_trade, tx_obj)
|
||||||
|
return released
|
||||||
else:
|
else:
|
||||||
# Already mapped
|
# Already mapped
|
||||||
log.error(
|
log.error(
|
||||||
|
|
|
@ -119,3 +119,5 @@ class AgoraClient(LocalPlatformClient, BaseClient):
|
||||||
|
|
||||||
# self.irc.sendmsg(f"Withdrawal: {rtrn1['success']} | {rtrn2['success']}")
|
# self.irc.sendmsg(f"Withdrawal: {rtrn1['success']} | {rtrn2['success']}")
|
||||||
# self.ux.notify.notify_withdrawal(half_rounded)
|
# self.ux.notify.notify_withdrawal(half_rounded)
|
||||||
|
|
||||||
|
# await self.successful_withdrawal()
|
||||||
|
|
|
@ -27,7 +27,10 @@ async def aggregator_job():
|
||||||
for bank, accounts in aggregator.account_info.items():
|
for bank, accounts in aggregator.account_info.items():
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
account_id = account["account_id"]
|
account_id = account["account_id"]
|
||||||
task = instance.get_transactions(account_id, process=True)
|
requisition_id = account["requisition_id"]
|
||||||
|
task = instance.get_transactions(
|
||||||
|
account_id, req=requisition_id, process=True
|
||||||
|
)
|
||||||
fetch_tasks.append(task)
|
fetch_tasks.append(task)
|
||||||
await asyncio.gather(*fetch_tasks)
|
await asyncio.gather(*fetch_tasks)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-03-20 13:35
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0033_platform_throughput'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='transaction',
|
||||||
|
name='requisition',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='core.requisition'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -151,17 +151,17 @@ class Aggregator(models.Model):
|
||||||
Then, join them all together.
|
Then, join them all together.
|
||||||
"""
|
"""
|
||||||
return Platform.objects.filter(link_group=self.link_group)
|
return Platform.objects.filter(link_group=self.link_group)
|
||||||
# platforms = []
|
|
||||||
# linkgroups = LinkGroup.objects.filter(
|
|
||||||
# aggregators=self,
|
|
||||||
# enabled=True,
|
|
||||||
# )
|
|
||||||
# for link in linkgroups:
|
|
||||||
# for platform in link.platforms.all():
|
|
||||||
# if platform not in platforms:
|
|
||||||
# platforms.append(platform)
|
|
||||||
|
|
||||||
# return platforms
|
@property
|
||||||
|
def requisitions(self):
|
||||||
|
"""
|
||||||
|
Get requisitions for this aggregator.
|
||||||
|
Do this by looking up LinkGroups with the aggregator.
|
||||||
|
Then, join them all together.
|
||||||
|
"""
|
||||||
|
return Requisition.objects.filter(
|
||||||
|
aggregator=self,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_currencies_for_platform(cls, platform):
|
def get_currencies_for_platform(cls, platform):
|
||||||
|
@ -187,7 +187,12 @@ class Aggregator(models.Model):
|
||||||
account_info[bank].append(account)
|
account_info[bank].append(account)
|
||||||
return account_info
|
return account_info
|
||||||
|
|
||||||
def add_transaction(self, account_id, tx_data):
|
def add_transaction(self, requisition_id, account_id, tx_data):
|
||||||
|
requisition = Requisition.objects.filter(
|
||||||
|
aggregator=self, requisition_id=requisition_id
|
||||||
|
).first()
|
||||||
|
if requisition:
|
||||||
|
tx_data["requisition"] = requisition
|
||||||
return Transaction.objects.create(
|
return Transaction.objects.create(
|
||||||
aggregator=self,
|
aggregator=self,
|
||||||
account_id=account_id,
|
account_id=account_id,
|
||||||
|
@ -426,22 +431,23 @@ class Platform(models.Model):
|
||||||
Do this by looking up LinkGroups with the platform.
|
Do this by looking up LinkGroups with the platform.
|
||||||
Then, join them all together.
|
Then, join them all together.
|
||||||
"""
|
"""
|
||||||
# aggregators = []
|
|
||||||
# linkgroups = LinkGroup.objects.filter(
|
|
||||||
# platforms=self,
|
|
||||||
# enabled=True,
|
|
||||||
# )
|
|
||||||
# for link in linkgroups:
|
|
||||||
# for aggregator in link.aggregators.all():
|
|
||||||
# if aggregator not in aggregators:
|
|
||||||
# aggregators.append(aggregator)
|
|
||||||
|
|
||||||
# return aggregators
|
|
||||||
|
|
||||||
return Aggregator.objects.filter(
|
return Aggregator.objects.filter(
|
||||||
link_group=self.link_group,
|
link_group=self.link_group,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def platforms(self):
|
||||||
|
"""
|
||||||
|
Get all platforms in this link group.
|
||||||
|
Do this by looking up LinkGroups with the platform.
|
||||||
|
Then, join them all together.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return Platform.objects.filter(
|
||||||
|
link_group=self.link_group,
|
||||||
|
)
|
||||||
|
|
||||||
def get_requisition(self, aggregator_id, requisition_id):
|
def get_requisition(self, aggregator_id, requisition_id):
|
||||||
"""
|
"""
|
||||||
Get a Requisition object with the provided values.
|
Get a Requisition object with the provided values.
|
||||||
|
@ -524,6 +530,9 @@ class Transaction(models.Model):
|
||||||
|
|
||||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
aggregator = models.ForeignKey(Aggregator, on_delete=models.CASCADE)
|
aggregator = models.ForeignKey(Aggregator, on_delete=models.CASCADE)
|
||||||
|
requisition = models.ForeignKey(
|
||||||
|
"core.Requisition", null=True, on_delete=models.CASCADE
|
||||||
|
)
|
||||||
account_id = models.CharField(max_length=255)
|
account_id = models.CharField(max_length=255)
|
||||||
transaction_id = models.CharField(max_length=255)
|
transaction_id = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<th>id</th>
|
<th>id</th>
|
||||||
<th>aggregator</th>
|
<th>aggregator</th>
|
||||||
<th>payees</th>
|
<th>payees</th>
|
||||||
|
<th>throughput</th>
|
||||||
</thead>
|
</thead>
|
||||||
{% for item in requisitions %}
|
{% for item in requisitions %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
{{ payee.name }}{% if not forloop.last %}, {% endif %}
|
{{ payee.name }}{% if not forloop.last %}, {% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>{{ item.throughput }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<th>service</th>
|
<th>service</th>
|
||||||
<th>payees</th>
|
<th>payees</th>
|
||||||
<th>link group</th>
|
<th>link group</th>
|
||||||
|
<th>throughput</th>
|
||||||
<th>enabled</th>
|
<th>enabled</th>
|
||||||
<th>actions</th>
|
<th>actions</th>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.link_group|default_if_none:"—" }}</td>
|
<td>{{ item.link_group|default_if_none:"—" }}</td>
|
||||||
|
<td>{{ item.throughput }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if item.enabled %}
|
{% if item.enabled %}
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
|
|
|
@ -156,3 +156,18 @@ class TestPlatform(AggregatorPlatformMixin, TransactionTestCase):
|
||||||
|
|
||||||
expected = "CUSTOM PAYMENT"
|
expected = "CUSTOM PAYMENT"
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
async def test_successful_withdrawal(self):
|
||||||
|
self.platform.throughput = 100
|
||||||
|
self.req.throughput = 100
|
||||||
|
|
||||||
|
self.platform.save()
|
||||||
|
self.req.save()
|
||||||
|
|
||||||
|
await self.plat_client.successful_withdrawal()
|
||||||
|
|
||||||
|
self.platform.refresh_from_db()
|
||||||
|
self.req.refresh_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(self.platform.throughput, 0)
|
||||||
|
self.assertEqual(self.req.throughput, 0)
|
||||||
|
|
Loading…
Reference in New Issue