Add payouts

This commit is contained in:
Mark Veidemanis 2023-05-06 11:12:54 +01:00
parent c534abf8f6
commit ddfee0b328
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
8 changed files with 246 additions and 5 deletions

View File

@ -27,6 +27,7 @@ from core.views import (
base,
linkgroups,
notifications,
payouts,
platforms,
profit,
wallets,
@ -241,6 +242,27 @@ urlpatterns = [
wallets.WalletDelete.as_view(),
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
path(
"links/<str:type>/",

View File

@ -11,6 +11,7 @@ from .models import (
LinkGroup,
NotificationSettings,
OperatorWallets,
Payout,
Platform,
Provider,
Requisition,
@ -332,3 +333,18 @@ class OperatorWalletsForm(RestrictedFormMixin, ModelForm):
help_text=Meta.help_texts["payees"],
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.",
}

View File

@ -27,7 +27,7 @@ async def withdrawal_job(group=None):
groups = LinkGroup.objects.filter(enabled=True)
for group in groups:
checks = await money.check_all(
user=group.user, nordigen=NordigenClient, agora=AgoraClient
link_group=group, nordigen=NordigenClient, agora=AgoraClient
)
print("CHECKS", checks)
aggregators = Aggregator.objects.filter(

View File

@ -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')),
],
),
]

View File

@ -652,6 +652,19 @@ class OperatorWallets(models.Model):
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 = {
"XMR": "Monero",
"BTC": "Bitcoin",

View File

@ -220,14 +220,21 @@
Home
</a>
{% if user.is_authenticated %}
<a class="navbar-item" href="{% url 'profit' type='page' %}">
Profit
</a>
<div class="navbar-item has-dropdown is-hoverable">
<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">
<a class="navbar-link">
Banks
</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="{% url 'currencies' type='page' %}">
Currencies

View File

@ -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 #}

82
core/views/payouts.py Normal file
View File

@ -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