fix a few things in backoffice chain/credebtor views and templates, and update bootstrap_devsite script to create chains, credebtors, expenses and revenues
This commit is contained in:
parent
02f0d77c21
commit
833fc85e46
|
@ -7,7 +7,7 @@ Details for Chain {{ chain.name }} | {{ block.super }}
|
|||
|
||||
{% block content %}
|
||||
<h2>Details for Chain {{ chain.name }}</h2>
|
||||
<a href="{% url "backoffice:chain_list" camp_slug=camp.slug %}">Back to Chain list</a>
|
||||
<a class="btn btn-default" href="{% url "backoffice:chain_list" camp_slug=camp.slug %}"><i class="fas fa-undo"></i> Back to Chain list</a>
|
||||
|
||||
<h3>{{ chain.credebtors.count }} Credebtors for Chain {{ chain.name }}</h3>
|
||||
<table class="table table-hover">
|
||||
|
@ -26,7 +26,7 @@ Details for Chain {{ chain.name }} | {{ block.super }}
|
|||
<tbody>
|
||||
{% for credebtor in chain.credebtors.all %}
|
||||
<tr>
|
||||
<td>{{ credebtor.name }}</td>
|
||||
<td><a class="btn btn-primary" href="{% url 'backoffice:credebtor_detail' camp_slug=camp.slug chain_slug=chain.slug credebtor_slug=credebtor.slug %}">{{ credebtor.name }}</a></td>
|
||||
<td><address>{{ credebtor.address }}</address></td>
|
||||
<td>{{ credebtor.notes|default:"N/A" }}</td>
|
||||
<td class="text-center"><span class="badge">{{ credebtor.expenses.count }}</span></td>
|
||||
|
@ -39,10 +39,10 @@ Details for Chain {{ chain.name }} | {{ block.super }}
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>{{ chain.expenses.count }} Expenses for Chain {{ chain.name }}</h3>
|
||||
<h3>{{ chain.expenses_count }} Expenses for Chain {{ chain.name }}</h3>
|
||||
{% include 'includes/expense_list_panel.html' with expense_list=chain.expenses.all %}
|
||||
|
||||
<h3>{{ chain.revenues.count }} Revenues for Chain {{ chain.name }}</h3>
|
||||
<h3>{{ chain.revenues_count }} Revenues for Chain {{ chain.name }}</h3>
|
||||
{% include 'includes/revenue_list_panel.html' with revenue_list=chain.revenues.all %}
|
||||
|
||||
{% endblock content %}
|
||||
|
|
|
@ -8,7 +8,7 @@ Select Chain | {{ block.super }}
|
|||
|
||||
{% block content %}
|
||||
<h2>Chains</h2>
|
||||
<p><a href="{% url "backoffice:index" camp_slug=camp.slug %}">Back to Backoffice Index</a></p>
|
||||
<p><a class="btn btn-default" href="{% url "backoffice:index" camp_slug=camp.slug %}"><i class="fas fa-undo"></i> Back to Backoffice Index</a></p>
|
||||
|
||||
{% if chain_list %}
|
||||
<table class="table table-hover datatable">
|
||||
|
@ -30,9 +30,9 @@ Select Chain | {{ block.super }}
|
|||
<td>{{ chain.name }}</td>
|
||||
<td>{{ chain.notes|default:"N/A" }}</td>
|
||||
<td class="text-center"><span class="badge">{{ chain.credebtors.count }}</span></td>
|
||||
<td class="text-center"><span class="badge">{{ chain.expenses.count }}</span></td>
|
||||
<td class="text-center"><span class="badge">{{ chain.expenses_count }}</span></td>
|
||||
<td class="text-center">{{ chain.expenses_total|default:"0" }} DKK</td>
|
||||
<td class="text-center"><span class="badge">{{ chain.revenues.count }}</span></td>
|
||||
<td class="text-center"><span class="badge">{{ chain.revenues_count }}</span></td>
|
||||
<td class="text-center">{{ chain.revenues_total|default:"0" }} DKK</td>
|
||||
<td>
|
||||
<a class="btn btn-primary" href="{% url 'backoffice:chain_detail' camp_slug=camp.slug chain_slug=chain.slug %}"><i class="fas fa-search"></i> Details</a>
|
||||
|
|
|
@ -7,7 +7,7 @@ Details for Credebtor {{ credebtor.name }} (Chain {{ credebtor.chain.name }}) |
|
|||
|
||||
{% block content %}
|
||||
<h2>Details for Credebtor {{ credebtor.name }} (Chain {{ credebtor.chain.name }})</h2>
|
||||
<a href="{% url "backoffice:chain_detail" camp_slug=camp.slug chain_slug=credebtor.chain.slug %}">Back to Credebtor list</a>
|
||||
<a class="btn btn-default" href="{% url "backoffice:chain_detail" camp_slug=camp.slug chain_slug=credebtor.chain.slug %}"><i class="fas fa-undo"></i> Back to Credebtor list</a>
|
||||
|
||||
<h3>{{ credebtor.expenses.count }} Expenses for Credebtor {{ credebtor.name }}</h3>
|
||||
{% include 'includes/expense_list_panel.html' with expense_list=credebtor.expenses.all %}
|
||||
|
|
|
@ -16,8 +16,8 @@ class ChainAdmin(admin.ModelAdmin):
|
|||
@admin.register(Credebtor)
|
||||
class CredebtorAdmin(admin.ModelAdmin):
|
||||
list_filter = ["chain", "name"]
|
||||
list_display = ["chain", "name", "notes"]
|
||||
search_fields = ["chain", "name", "notes"]
|
||||
list_display = ["name", "chain", "address", "notes"]
|
||||
search_fields = ["chain", "name", "address", "notes"]
|
||||
|
||||
|
||||
###############################
|
||||
|
|
|
@ -20,12 +20,17 @@ from .email import (
|
|||
class ChainManager(models.Manager):
|
||||
"""
|
||||
ChainManager adds 'expenses_total' and 'revenues_total' to the Chain qs
|
||||
Also adds 'expenses_count' and 'revenues_count' and prefetches all expenses
|
||||
and revenues for the credebtors.
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
qs = qs.prefetch_related("credebtors__expenses", "credebtors__revenues")
|
||||
qs = qs.annotate(expenses_total=models.Sum("credebtors__expenses__amount"))
|
||||
qs = qs.annotate(expenses_count=models.Count("credebtors__expenses", distinct=True))
|
||||
qs = qs.annotate(revenues_total=models.Sum("credebtors__revenues__amount"))
|
||||
qs = qs.annotate(revenues_count=models.Count("credebtors__revenues", distinct=True))
|
||||
return qs
|
||||
|
||||
|
||||
|
@ -81,11 +86,13 @@ class Chain(CreatedUpdatedModel, UUIDModel):
|
|||
|
||||
class CredebtorManager(models.Manager):
|
||||
"""
|
||||
CredebtorManager adds 'expenses_total' and 'revenues_total' to the Credebtor qs
|
||||
CredebtorManager adds 'expenses_total' and 'revenues_total' to the Credebtor qs,
|
||||
and prefetches expenses and revenues for the credebtor(s).
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
qs = qs.prefetch_related("expenses", "revenues")
|
||||
qs = qs.annotate(expenses_total=models.Sum("expenses__amount"))
|
||||
qs = qs.annotate(revenues_total=models.Sum("revenues__amount"))
|
||||
return qs
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% if expense_list %}
|
||||
<table class="table table-hover">
|
||||
<table class="table table-hover datatable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice Date</th>
|
||||
|
|
|
@ -12,6 +12,7 @@ from django.contrib.auth.models import User
|
|||
from django.contrib.gis.geos import Point
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.management import call_command
|
||||
from django.db.models.signals import post_save
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
|
@ -52,13 +53,65 @@ from tickets.models import TicketType
|
|||
from tokens.models import Token, TokenFind
|
||||
from utils.slugs import unique_slugify
|
||||
from villages.models import Village
|
||||
from economy.models import Chain, Credebtor, Expense, Revenue
|
||||
|
||||
fake = Faker()
|
||||
tz = pytz.timezone("Europe/Copenhagen")
|
||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||
|
||||
|
||||
@factory.django.mute_signals(post_save)
|
||||
class ChainFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Chain
|
||||
|
||||
name = factory.Faker("company")
|
||||
slug = factory.LazyAttribute(lambda f: unique_slugify(f.name, Chain.objects.all().values_list("slug", flat=True)))
|
||||
|
||||
|
||||
class CredebtorFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Credebtor
|
||||
|
||||
chain = factory.SubFactory(ChainFactory)
|
||||
name = factory.Faker("company")
|
||||
slug = factory.LazyAttribute(lambda f: unique_slugify(f.name, Credebtor.objects.all().values_list("slug", flat=True)))
|
||||
address = factory.Faker("address", locale="dk_DK")
|
||||
notes = factory.Faker("text")
|
||||
|
||||
|
||||
class ExpenseFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Expense
|
||||
|
||||
camp = factory.Faker("random_element", elements=Camp.objects.all())
|
||||
creditor = factory.Faker("random_element", elements=Credebtor.objects.all())
|
||||
user = factory.Faker("random_element", elements=User.objects.all())
|
||||
amount = factory.Faker("random_int", min=20, max=20000)
|
||||
description = factory.Faker("text")
|
||||
paid_by_bornhack = factory.Faker("random_element", elements=[True, True, False])
|
||||
invoice = factory.django.ImageField(color=random.choice(['#ff0000', '#00ff00', '#0000ff']))
|
||||
invoice_date = factory.Faker("date")
|
||||
responsible_team = factory.Faker("random_element", elements=Team.objects.all())
|
||||
approved = factory.Faker("random_element", elements=[True, True, False])
|
||||
notes = factory.Faker("text")
|
||||
|
||||
|
||||
class RevenueFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Revenue
|
||||
|
||||
camp = factory.Faker("random_element", elements=Camp.objects.all())
|
||||
debtor = factory.Faker("random_element", elements=Credebtor.objects.all())
|
||||
user = factory.Faker("random_element", elements=User.objects.all())
|
||||
amount = factory.Faker("random_int", min=20, max=20000)
|
||||
description = factory.Faker("text")
|
||||
invoice = factory.django.ImageField(color=random.choice(['#ff0000', '#00ff00', '#0000ff']))
|
||||
invoice_date = factory.Faker("date")
|
||||
responsible_team = factory.Faker("random_element", elements=Team.objects.all())
|
||||
approved = factory.Faker("random_element", elements=[True, True, False])
|
||||
notes = factory.Faker("text")
|
||||
|
||||
|
||||
class ProfileFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Profile
|
||||
|
@ -159,8 +212,9 @@ class Command(BaseCommand):
|
|||
dict(year=2017, tagline="Make Tradition", colour="#750787", read_only=True),
|
||||
dict(year=2018, tagline="scale it", colour="#008026", read_only=True),
|
||||
dict(year=2019, tagline="a new /home", colour="#ffed00", read_only=True),
|
||||
dict(year=2020, tagline="Going Viral", colour="#ff8c00", read_only=False),
|
||||
dict(year=2020, tagline="Make Clean", colour="#ff8c00", read_only=False),
|
||||
dict(year=2021, tagline="Undecided", colour="#e40303", read_only=False),
|
||||
dict(year=2022, tagline="Undecided", colour="#004dff", read_only=False),
|
||||
]
|
||||
|
||||
camp_instances = []
|
||||
|
@ -508,6 +562,21 @@ class Command(BaseCommand):
|
|||
name="Recording", defaults={"icon": "fas fa-film"}
|
||||
)
|
||||
|
||||
def create_credebtors(self):
|
||||
self.output("Creating Chains and Credebtors...")
|
||||
try:
|
||||
CredebtorFactory.create_batch(50)
|
||||
except ValidationError:
|
||||
self.outout("Name conflict, retrying...")
|
||||
CredebtorFactory.create_batch(50)
|
||||
for _ in range(20):
|
||||
# add 20 more credebtors to random existing chains
|
||||
try:
|
||||
CredebtorFactory.create(chain=Chain.objects.order_by("?").first())
|
||||
except ValidationError:
|
||||
self.outout("Name conflict, skipping...")
|
||||
continue
|
||||
|
||||
def create_product_categories(self):
|
||||
categories = {}
|
||||
self.output("Creating productcategories...")
|
||||
|
@ -686,7 +755,7 @@ class Command(BaseCommand):
|
|||
orders = {}
|
||||
self.output("Creating orders...")
|
||||
orders[0] = Order.objects.create(
|
||||
user=users[1], payment_method="cash", open=None, paid=True
|
||||
user=users[1], payment_method="in_person", open=None, paid=True
|
||||
)
|
||||
orders[0].orderproductrelation_set.create(
|
||||
product=camp_products["ticket1"], quantity=1
|
||||
|
@ -697,7 +766,7 @@ class Command(BaseCommand):
|
|||
orders[0].mark_as_paid(request=None)
|
||||
|
||||
orders[1] = Order.objects.create(
|
||||
user=users[2], payment_method="cash", open=None
|
||||
user=users[2], payment_method="in_person", open=None
|
||||
)
|
||||
orders[1].orderproductrelation_set.create(
|
||||
product=camp_products["ticket1"], quantity=1
|
||||
|
@ -708,7 +777,7 @@ class Command(BaseCommand):
|
|||
orders[1].mark_as_paid(request=None)
|
||||
|
||||
orders[2] = Order.objects.create(
|
||||
user=users[3], payment_method="cash", open=None
|
||||
user=users[3], payment_method="in_person", open=None
|
||||
)
|
||||
orders[2].orderproductrelation_set.create(
|
||||
product=camp_products["ticket2"], quantity=1
|
||||
|
@ -722,7 +791,7 @@ class Command(BaseCommand):
|
|||
orders[2].mark_as_paid(request=None)
|
||||
|
||||
orders[3] = Order.objects.create(
|
||||
user=users[4], payment_method="cash", open=None
|
||||
user=users[4], payment_method="in_person", open=None
|
||||
)
|
||||
orders[3].orderproductrelation_set.create(
|
||||
product=global_products["product0"], quantity=1
|
||||
|
@ -1189,6 +1258,13 @@ class Command(BaseCommand):
|
|||
mailing_list="content@example.com",
|
||||
permission_set="contentteam_permission",
|
||||
)
|
||||
teams["economy"] = Team.objects.create(
|
||||
name="Economy",
|
||||
description="The Economy Team handles the money and accounts.",
|
||||
camp=camp,
|
||||
mailing_list="economy@example.com",
|
||||
permission_set="economyteam_permission",
|
||||
)
|
||||
|
||||
return teams
|
||||
|
||||
|
@ -1600,6 +1676,14 @@ class Command(BaseCommand):
|
|||
for i in range(0, 6):
|
||||
TokenFind.objects.create(token=tokens[i], user=users[1])
|
||||
|
||||
def create_camp_expenses(self, camp):
|
||||
self.output(f"Creating expenses for {camp}...")
|
||||
ExpenseFactory.create_batch(200, camp=camp)
|
||||
|
||||
def create_camp_revenues(self, camp):
|
||||
self.output(f"Creating revenues for {camp}...")
|
||||
RevenueFactory.create_batch(20, camp=camp)
|
||||
|
||||
def output(self, message):
|
||||
self.stdout.write(
|
||||
"%s: %s" % (timezone.now().strftime("%Y-%m-%d %H:%M:%S"), message)
|
||||
|
@ -1610,6 +1694,13 @@ class Command(BaseCommand):
|
|||
self.output(
|
||||
self.style.SUCCESS("----------[ Running bootstrap-devsite ]----------")
|
||||
)
|
||||
self.output(
|
||||
self.style.SUCCESS(
|
||||
"----------[ Deleting all data from database ]----------"
|
||||
)
|
||||
)
|
||||
call_command("flush", "--noinput")
|
||||
|
||||
self.output(self.style.SUCCESS("----------[ Global stuff ]----------"))
|
||||
|
||||
camps = self.create_camps()
|
||||
|
@ -1628,6 +1719,8 @@ class Command(BaseCommand):
|
|||
|
||||
quickfeedback_options = self.create_quickfeedback_options()
|
||||
|
||||
self.create_credebtors()
|
||||
|
||||
for (camp, read_only) in camps:
|
||||
year = camp.camp.lower.year
|
||||
|
||||
|
@ -1635,7 +1728,7 @@ class Command(BaseCommand):
|
|||
self.style.SUCCESS("----------[ Bornhack {} ]----------".format(year))
|
||||
)
|
||||
|
||||
if year < 2021:
|
||||
if year < 2022:
|
||||
ticket_types = self.create_camp_ticket_types(camp)
|
||||
|
||||
camp_products = self.create_camp_products(
|
||||
|
@ -1716,6 +1809,10 @@ class Command(BaseCommand):
|
|||
tokens = self.create_camp_tokens(camp)
|
||||
|
||||
self.create_camp_token_finds(camp, tokens, users)
|
||||
|
||||
self.create_camp_expenses(camp)
|
||||
|
||||
self.create_camp_revenues(camp)
|
||||
else:
|
||||
self.output("Not creating anything for this year yet")
|
||||
|
Loading…
Reference in a new issue