Add payouts
This commit is contained in:
parent
c534abf8f6
commit
ddfee0b328
22
app/urls.py
22
app/urls.py
|
@ -27,6 +27,7 @@ from core.views import (
|
||||||
base,
|
base,
|
||||||
linkgroups,
|
linkgroups,
|
||||||
notifications,
|
notifications,
|
||||||
|
payouts,
|
||||||
platforms,
|
platforms,
|
||||||
profit,
|
profit,
|
||||||
wallets,
|
wallets,
|
||||||
|
@ -241,6 +242,27 @@ urlpatterns = [
|
||||||
wallets.WalletDelete.as_view(),
|
wallets.WalletDelete.as_view(),
|
||||||
name="wallet_delete",
|
name="wallet_delete",
|
||||||
),
|
),
|
||||||
|
# Payouts
|
||||||
|
path(
|
||||||
|
"payouts/<str:type>/",
|
||||||
|
payouts.PayoutList.as_view(),
|
||||||
|
name="payouts",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"payouts/<str:type>/create/",
|
||||||
|
payouts.PayoutCreate.as_view(),
|
||||||
|
name="payout_create",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"payouts/<str:type>/update/<str:pk>/",
|
||||||
|
payouts.PayoutUpdate.as_view(),
|
||||||
|
name="payout_update",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"payouts/<str:type>/delete/<str:pk>/",
|
||||||
|
payouts.PayoutDelete.as_view(),
|
||||||
|
name="payout_delete",
|
||||||
|
),
|
||||||
# Link groups
|
# Link groups
|
||||||
path(
|
path(
|
||||||
"links/<str:type>/",
|
"links/<str:type>/",
|
||||||
|
|
|
@ -11,6 +11,7 @@ from .models import (
|
||||||
LinkGroup,
|
LinkGroup,
|
||||||
NotificationSettings,
|
NotificationSettings,
|
||||||
OperatorWallets,
|
OperatorWallets,
|
||||||
|
Payout,
|
||||||
Platform,
|
Platform,
|
||||||
Provider,
|
Provider,
|
||||||
Requisition,
|
Requisition,
|
||||||
|
@ -332,3 +333,18 @@ class OperatorWalletsForm(RestrictedFormMixin, ModelForm):
|
||||||
help_text=Meta.help_texts["payees"],
|
help_text=Meta.help_texts["payees"],
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PayoutForm(RestrictedFormMixin, ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Payout
|
||||||
|
fields = (
|
||||||
|
"wallet",
|
||||||
|
"amount",
|
||||||
|
"description",
|
||||||
|
)
|
||||||
|
help_texts = {
|
||||||
|
"wallet": "The wallet the payment was sent to.",
|
||||||
|
"amount": "The amount of the payment.",
|
||||||
|
"description": "The description of the payment.",
|
||||||
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ async def withdrawal_job(group=None):
|
||||||
groups = LinkGroup.objects.filter(enabled=True)
|
groups = LinkGroup.objects.filter(enabled=True)
|
||||||
for group in groups:
|
for group in groups:
|
||||||
checks = await money.check_all(
|
checks = await money.check_all(
|
||||||
user=group.user, nordigen=NordigenClient, agora=AgoraClient
|
link_group=group, nordigen=NordigenClient, agora=AgoraClient
|
||||||
)
|
)
|
||||||
print("CHECKS", checks)
|
print("CHECKS", checks)
|
||||||
aggregators = Aggregator.objects.filter(
|
aggregators = Aggregator.objects.filter(
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-05-06 10:09
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0036_requisition_owner_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Payout',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('amount', models.FloatField()),
|
||||||
|
('description', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('ts', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('wallet', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.wallet')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -652,6 +652,19 @@ class OperatorWallets(models.Model):
|
||||||
payees = models.ManyToManyField(Wallet, blank=True)
|
payees = models.ManyToManyField(Wallet, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Payout(models.Model):
|
||||||
|
"""
|
||||||
|
A profit payout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
wallet = models.ForeignKey(Wallet, on_delete=models.CASCADE)
|
||||||
|
amount = models.FloatField()
|
||||||
|
description = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
ts = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
|
||||||
assets = {
|
assets = {
|
||||||
"XMR": "Monero",
|
"XMR": "Monero",
|
||||||
"BTC": "Bitcoin",
|
"BTC": "Bitcoin",
|
||||||
|
|
|
@ -220,14 +220,21 @@
|
||||||
Home
|
Home
|
||||||
</a>
|
</a>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<a class="navbar-item" href="{% url 'profit' type='page' %}">
|
|
||||||
Profit
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
</a>
|
<a class="navbar-link" href="{% url 'profit' type='page' %}">
|
||||||
|
Profit
|
||||||
|
</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="{% url 'payouts' type='page' %}">
|
||||||
|
Payouts
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
<a class="navbar-link">
|
<a class="navbar-link">
|
||||||
Banks
|
Banks
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="navbar-dropdown">
|
<div class="navbar-dropdown">
|
||||||
<a class="navbar-item" href="{% url 'currencies' type='page' %}">
|
<a class="navbar-item" href="{% url 'currencies' type='page' %}">
|
||||||
Currencies
|
Currencies
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Payout' as last %}
|
||||||
|
{% include 'mixins/partials/notify.html' %}
|
||||||
|
{# cache 600 objects_apayouts request.user.id object_list type last #}
|
||||||
|
<table
|
||||||
|
class="table is-fullwidth is-hoverable"
|
||||||
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
id="{{ context_object_name }}-table"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-trigger="{{ context_object_name_singular }}Event from:body"
|
||||||
|
hx-get="{{ list_url }}">
|
||||||
|
<thead>
|
||||||
|
<th>id</th>
|
||||||
|
<th>user</th>
|
||||||
|
<th>wallet</th>
|
||||||
|
<th>amount</th>
|
||||||
|
<th>description</th>
|
||||||
|
<th>ts</th>
|
||||||
|
<th>actions</th>
|
||||||
|
</thead>
|
||||||
|
{% for item in object_list %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
class="has-text-grey"
|
||||||
|
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
|
||||||
|
<span class="icon" data-tooltip="Copy to clipboard">
|
||||||
|
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ item.user }}</td>
|
||||||
|
<td>{{ item.wallet }}</td>
|
||||||
|
<td>{{ item.amount }}</td>
|
||||||
|
<td>{{ item.description }}</td>
|
||||||
|
<td>{{ item.ts }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="buttons">
|
||||||
|
<button
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-get="{% url 'payout_update' type=type pk=item.id %}"
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="#{{ type }}s-here"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
class="button">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-pencil"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-delete="{% url 'payout_delete' type=type pk=item.id %}"
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="#modals-here"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-confirm="Are you sure you wish to delete {{ item.name }}?"
|
||||||
|
class="button">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</table>
|
||||||
|
{# endcache #}
|
|
@ -0,0 +1,82 @@
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from mixins.views import ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate
|
||||||
|
from two_factor.views.mixins import OTPRequiredMixin
|
||||||
|
|
||||||
|
from core.forms import PayoutForm
|
||||||
|
from core.models import Payout
|
||||||
|
|
||||||
|
|
||||||
|
class PayoutList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
|
||||||
|
list_template = "partials/payout-list.html"
|
||||||
|
model = Payout
|
||||||
|
page_title = "List of payouts"
|
||||||
|
page_subtitle = (
|
||||||
|
"Payouts are informational only. "
|
||||||
|
"Adjustments and creations will not have any effect."
|
||||||
|
)
|
||||||
|
|
||||||
|
list_url_name = "payouts"
|
||||||
|
list_url_args = ["type"]
|
||||||
|
|
||||||
|
submit_url_name = "payout_create"
|
||||||
|
|
||||||
|
# def get_context_data(self):
|
||||||
|
# context = super().get_context_data()
|
||||||
|
# self.extra_buttons = [
|
||||||
|
# {
|
||||||
|
# "url": reverse("ad_dist"),
|
||||||
|
# "action": "distribute",
|
||||||
|
# "method": "get",
|
||||||
|
# "label": "Distribute ads",
|
||||||
|
# "icon": "fa-solid fa-upload",
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "url": reverse("ad_redist"),
|
||||||
|
# "action": "redistribute",
|
||||||
|
# "method": "get",
|
||||||
|
# "label": "Update ads",
|
||||||
|
# "icon": "fa-solid fa-refresh",
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "url": reverse("cheat"),
|
||||||
|
# "action": "cheat",
|
||||||
|
# "method": "get",
|
||||||
|
# "label": "Run cheat",
|
||||||
|
# "icon": "fa-solid fa-bolt",
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "url": reverse("ad_nuke"),
|
||||||
|
# "action": "nuke",
|
||||||
|
# "method": "get",
|
||||||
|
# "label": "Nuke ads",
|
||||||
|
# "icon": "fa-solid fa-bomb",
|
||||||
|
# "confirm": True,
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "url": reverse("ad_dedup"),
|
||||||
|
# "action": "deduplicate",
|
||||||
|
# "method": "get",
|
||||||
|
# "label": "Deduplicate ads",
|
||||||
|
# "icon": "fa-thin fa-copy",
|
||||||
|
# "confirm": True,
|
||||||
|
# },
|
||||||
|
# ]
|
||||||
|
# return context
|
||||||
|
|
||||||
|
|
||||||
|
class PayoutCreate(LoginRequiredMixin, OTPRequiredMixin, ObjectCreate):
|
||||||
|
model = Payout
|
||||||
|
form_class = PayoutForm
|
||||||
|
|
||||||
|
submit_url_name = "payout_create"
|
||||||
|
|
||||||
|
|
||||||
|
class PayoutUpdate(LoginRequiredMixin, OTPRequiredMixin, ObjectUpdate):
|
||||||
|
model = Payout
|
||||||
|
form_class = PayoutForm
|
||||||
|
|
||||||
|
submit_url_name = "payout_update"
|
||||||
|
|
||||||
|
|
||||||
|
class PayoutDelete(LoginRequiredMixin, OTPRequiredMixin, ObjectDelete):
|
||||||
|
model = Payout
|
Loading…
Reference in New Issue