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,
|
||||
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>/",
|
||||
|
|
|
@ -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.",
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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