more work on backoffice economy stuff, chains and credebtors section now only shows expenses and revenues for the current camp, thanks to some ORM foo; while here add a cost and a comment field to the Product table, and fix a bug in bootstrap_devsite and a few nags to make new flake8 happy

This commit is contained in:
Thomas Steen Rasmussen 2020-10-17 02:35:12 +02:00
parent 833fc85e46
commit fbda2b4b53
8 changed files with 339 additions and 116 deletions

View file

@ -6,11 +6,17 @@ Details for Chain {{ chain.name }} | {{ block.super }}
{% endblock %}
{% block content %}
<h2>Details for Chain {{ chain.name }}</h2>
<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>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Details for Chain {{ chain.name }} - BackOffice</h3></div>
<div class="panel-body">
<p class="lead">This chain has <b>{{ chain.camp_expenses_count }} expenses</b> for a total of <b>{{ chain.camp_expenses_amount }} DKK</b> and <b>{{ chain.camp_revenues_count }} revenues</b> for a total of <b>{{ chain.camp_revenues_amount }} DKK</b> for {{ camp.title }}.</p>
<p class="lead">This chain has <b>{{ chain.all_expenses_count }} expenses</b> for a total of <b>{{ chain.all_expenses_amount }} DKK</b> and <b>{{ chain.all_revenues_count }} revenues</b> for a total of <b>{{ chain.all_revenues_amount }} DKK</b> across all camps.</p>
<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">
<hr>
<h3>{{ chain.credebtors.count }} Credebtors for Chain {{ chain.name }}</h3>
<table class="table table-hover">
<thead>
<tr>
<th>Credebtor Name</th>
@ -24,26 +30,32 @@ Details for Chain {{ chain.name }} | {{ block.super }}
</tr>
</thead>
<tbody>
{% for credebtor in chain.credebtors.all %}
{% for credebtor in credebtors %}
<tr>
<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>
<td class="text-center">{{ credebtor.expenses_total }} DKK</td>
<td class="text-center"><span class="badge">{{ credebtor.revenues.count }}</span></td>
<td class="text-center">{{ credebtor.revenues_total }} DKK</td>
<td class="text-center"><span class="badge">{{ credebtor.camp_expenses_count }}</span></td>
<td class="text-center">{{ credebtor.camp_expenses_amount }} DKK</td>
<td class="text-center"><span class="badge">{{ credebtor.camp_revenues_count }}</span></td>
<td class="text-center">{{ credebtor.camp_revenues_amount }} DKK</td>
<td>
<a class="btn btn-primary" href="{% url 'backoffice:credebtor_detail' camp_slug=camp.slug chain_slug=chain.slug credebtor_slug=credebtor.slug %}"><i class="fas fa-search"></i> Details</a>
{% endfor %}
</tbody>
</table>
</table>
<h3>{{ chain.expenses_count }} Expenses for Chain {{ chain.name }}</h3>
{% include 'includes/expense_list_panel.html' with expense_list=chain.expenses.all %}
<hr>
<h3>{{ chain.revenues_count }} Revenues for Chain {{ chain.name }}</h3>
{% include 'includes/revenue_list_panel.html' with revenue_list=chain.revenues.all %}
<h3>{{ expenses.count }} Expenses for Chain {{ chain.name }}</h3>
{% include 'includes/expense_list_panel.html' with expense_list=expenses %}
<hr>
<h3>{{ revenues.count }} Revenues for Chain {{ chain.name }}</h3>
{% include 'includes/revenue_list_panel.html' with revenue_list=revenues %}
</div>
</div>
{% endblock content %}

View file

@ -7,11 +7,14 @@ Select Chain | {{ block.super }}
{% endblock %}
{% block content %}
<h2>Chains</h2>
<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>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Chains - BackOffice</h3></div>
<div class="panel-body">
<p class="lead">Showing {{ chain_list.count }} chains. Not all chains have expenses or revenues for {{ camp.title }}.</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">
{% if chain_list %}
<table class="table table-hover datatable">
<thead>
<tr>
<th>Chain Name</th>
@ -30,19 +33,20 @@ 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">{{ chain.expenses_total|default:"0" }} DKK</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 class="text-center"><span class="badge">{{ chain.camp_expenses_count }}</span></td>
<td class="text-center">{{ chain.camp_expenses_amount|default:"0" }} DKK</td>
<td class="text-center"><span class="badge">{{ chain.camp_revenues_count }}</span></td>
<td class="text-center">{{ chain.camp_revenues_amount|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>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="lead"><i>No Chains found.</i></p>
{% endif %}
</table>
{% else %}
<p class="lead"><i>No Chains found.</i></p>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -6,14 +6,17 @@ Details for Credebtor {{ credebtor.name }} (Chain {{ credebtor.chain.name }}) |
{% endblock %}
{% block content %}
<h2>Details for Credebtor {{ credebtor.name }} (Chain {{ credebtor.chain.name }})</h2>
<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>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Details for Credebtor {{ credebtor.name }} (Chain {{ credebtor.chain.name }}) - BackOffice</h3></div>
<div class="panel-body">
<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 for {{ credebtor.chain.name }}</a>
<h3>{{ credebtor.expenses.count }} Expenses for Credebtor {{ credebtor.name }}</h3>
{% include 'includes/expense_list_panel.html' with expense_list=credebtor.expenses.all %}
<h3>{{ credebtor.revenues.count }} Revenues for Credebtor {{ credebtor.name }}</h3>
{% include 'includes/revenue_list_panel.html' with revenue_list=credebtor.revenues.all %}
<h3>{{ expenses.count }} Expenses for Credebtor {{ credebtor.name }}</h3>
{% include 'includes/expense_list_panel.html' with expense_list=expenses %}
<h3>{{ revenues.count }} Revenues for Credebtor {{ credebtor.name }}</h3>
{% include 'includes/revenue_list_panel.html' with revenue_list=revenues %}
</div>
</div>
{% endblock content %}

View file

@ -298,7 +298,11 @@ class FacilityCreateView(CampViewMixin, OrgaTeamPermissionMixin, CreateView):
def get_form(self, *args, **kwargs):
form = super().get_form(*args, **kwargs)
form.fields["location"].widget = LeafletWidget(attrs={"display_raw": "true",})
form.fields["location"].widget = LeafletWidget(
attrs={
"display_raw": "true",
}
)
return form
def get_context_data(self, **kwargs):
@ -324,7 +328,11 @@ class FacilityUpdateView(CampViewMixin, OrgaTeamPermissionMixin, UpdateView):
def get_form(self, *args, **kwargs):
form = super().get_form(*args, **kwargs)
form.fields["location"].widget = LeafletWidget(attrs={"display_raw": "true",})
form.fields["location"].widget = LeafletWidget(
attrs={
"display_raw": "true",
}
)
return form
def get_success_url(self):
@ -445,7 +453,7 @@ class FacilityOpeningHoursCreateView(
hours = form.save(commit=False)
hours.facility = self.facility
hours.save()
messages.success(self.request, f"New opening hours created successfully!")
messages.success(self.request, "New opening hours created successfully!")
return redirect(
reverse(
"backoffice:facility_detail",
@ -547,7 +555,9 @@ class SpeakerProposalListView(CampViewMixin, ContentTeamPermissionMixin, ListVie
class SpeakerProposalDetailView(
AvailabilityMatrixViewMixin, ContentTeamPermissionMixin, DetailView,
AvailabilityMatrixViewMixin,
ContentTeamPermissionMixin,
DetailView,
):
""" This view permits Content Team members to see SpeakerProposal details """
@ -915,15 +925,16 @@ class EventDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
def get_success_url(self):
messages.success(
self.request, f"Event '{self.get_object().title}' has been deleted!",
self.request,
f"Event '{self.get_object().title}' has been deleted!",
)
return reverse("backoffice:event_list", kwargs={"camp_slug": self.camp.slug})
class EventScheduleView(CampViewMixin, ContentTeamPermissionMixin, FormView):
""" This view is used by the Content Team to manually schedule Events.
"""This view is used by the Content Team to manually schedule Events.
It shows a table with radioselect buttons for the available slots for the
EventType of the Event """
EventType of the Event"""
form_class = EventScheduleForm
template_name = "event_schedule.html"
@ -1259,9 +1270,9 @@ class AutoScheduleCrashCourseView(
class AutoScheduleValidateView(CampViewMixin, ContentTeamPermissionMixin, FormView):
""" This view is used to validate schedules. It uses the AutoScheduler and can
"""This view is used to validate schedules. It uses the AutoScheduler and can
either validate the currently applied schedule or a new similar schedule, or a
brand new schedule """
brand new schedule"""
template_name = "autoschedule_validate.html"
form_class = AutoScheduleValidateForm
@ -1314,7 +1325,7 @@ class AutoScheduleDiffView(CampViewMixin, ContentTeamPermissionMixin, TemplateVi
class AutoScheduleApplyView(CampViewMixin, ContentTeamPermissionMixin, FormView):
""" This view is used by the Content Team to apply a new schedules by unscheduling
"""This view is used by the Content Team to apply a new schedules by unscheduling
all autoscheduled Events, and scheduling all Event/Slot combinations in the schedule.
TODO: see comment in program.autoscheduler.AutoScheduler.apply() method.
@ -1481,18 +1492,118 @@ class ChainListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
model = Chain
template_name = "chain_list_backoffice.html"
def get_queryset(self, *args, **kwargs):
"""Annotate the total count and amount for expenses and revenues for all credebtors in each chain."""
qs = Chain.objects.annotate(
camp_expenses_amount=Sum(
"credebtors__expenses__amount",
filter=Q(credebtors__expenses__camp=self.camp),
distinct=True,
),
camp_expenses_count=Count(
"credebtors__expenses",
filter=Q(credebtors__expenses__camp=self.camp),
distinct=True,
),
camp_revenues_amount=Sum(
"credebtors__revenues__amount",
filter=Q(credebtors__revenues__camp=self.camp),
distinct=True,
),
camp_revenues_count=Count(
"credebtors__revenues",
filter=Q(credebtors__revenues__camp=self.camp),
distinct=True,
),
)
return qs
class ChainDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
model = Chain
template_name = "chain_detail_backoffice.html"
slug_url_kwarg = "chain_slug"
def get_queryset(self, *args, **kwargs):
"""Annotate the Chain object with the camp filtered expense and revenue info."""
qs = super().get_queryset(*args, **kwargs)
qs = qs.annotate(
camp_expenses_amount=Sum(
"credebtors__expenses__amount",
filter=Q(credebtors__expenses__camp=self.camp),
distinct=True,
),
camp_expenses_count=Count(
"credebtors__expenses",
filter=Q(credebtors__expenses__camp=self.camp),
distinct=True,
),
camp_revenues_amount=Sum(
"credebtors__revenues__amount",
filter=Q(credebtors__revenues__camp=self.camp),
distinct=True,
),
camp_revenues_count=Count(
"credebtors__revenues",
filter=Q(credebtors__revenues__camp=self.camp),
distinct=True,
),
)
return qs
def get_context_data(self, *args, **kwargs):
"""Add credebtors, expenses and revenues to the context in camp-filtered versions."""
context = super().get_context_data(*args, **kwargs)
# include credebtors as a seperate queryset with annotations for total number and
# amount of expenses and revenues
context["credebtors"] = Credebtor.objects.filter(
chain=self.get_object()
).annotate(
camp_expenses_amount=Sum(
"expenses__amount", filter=Q(expenses__camp=self.camp), distinct=True
),
camp_expenses_count=Count(
"expenses", filter=Q(expenses__camp=self.camp), distinct=True
),
camp_revenues_amount=Sum(
"revenues__amount", filter=Q(revenues__camp=self.camp), distinct=True
),
camp_revenues_count=Count(
"revenues", filter=Q(revenues__camp=self.camp), distinct=True
),
)
# Include expenses and revenues for the Chain in context as seperate querysets,
# since accessing them through the relatedmanager returns for all camps
context["expenses"] = Expense.objects.filter(
camp=self.camp, creditor__chain=self.get_object()
).prefetch_related("responsible_team", "user", "creditor")
context["revenues"] = Revenue.objects.filter(
camp=self.camp, debtor__chain=self.get_object()
).prefetch_related("responsible_team", "user", "debtor")
return context
class CredebtorDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
model = Credebtor
template_name = "credebtor_detail_backoffice.html"
slug_url_kwarg = "credebtor_slug"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["expenses"] = (
self.get_object()
.expenses.filter(camp=self.camp)
.prefetch_related("responsible_team", "user", "creditor")
)
context["revenues"] = (
self.get_object()
.revenues.filter(camp=self.camp)
.prefetch_related("responsible_team", "user", "debtor")
)
return context
################################
# EXPENSES
@ -1507,7 +1618,11 @@ class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
Exclude unapproved expenses, they are shown seperately
"""
queryset = super().get_queryset(**kwargs)
return queryset.exclude(approved__isnull=True)
return queryset.exclude(approved__isnull=True).prefetch_related(
"creditor",
"user",
"responsible_team",
)
def get_context_data(self, **kwargs):
"""
@ -1516,6 +1631,10 @@ class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
context = super().get_context_data(**kwargs)
context["unapproved_expenses"] = Expense.objects.filter(
camp=self.camp, approved__isnull=True
).prefetch_related(
"creditor",
"user",
"responsible_team",
)
return context
@ -1747,7 +1866,11 @@ class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
Exclude unapproved revenues, they are shown seperately
"""
queryset = super().get_queryset(**kwargs)
return queryset.exclude(approved__isnull=True)
return queryset.exclude(approved__isnull=True).prefetch_related(
"debtor",
"user",
"responsible_team",
)
def get_context_data(self, **kwargs):
"""
@ -2040,7 +2163,8 @@ class PosReportCreateView(PosViewMixin, CreateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"].fields["bank_responsible"].queryset = Team.objects.get(
camp=self.camp, name="Orga",
camp=self.camp,
name="Orga",
).approved_members.all()
context["form"].fields[
"pos_responsible"
@ -2054,7 +2178,7 @@ class PosReportCreateView(PosViewMixin, CreateView):
pr = form.save(commit=False)
pr.pos = self.pos
pr.save()
messages.success(self.request, f"New PosReport created successfully!")
messages.success(self.request, "New PosReport created successfully!")
return redirect(
reverse(
"backoffice:posreport_detail",
@ -2086,7 +2210,8 @@ class PosReportUpdateView(PosViewMixin, UpdateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"].fields["bank_responsible"].queryset = Team.objects.get(
camp=self.camp, name="Orga",
camp=self.camp,
name="Orga",
).approved_members.all()
context["form"].fields[
"pos_responsible"

View file

@ -26,11 +26,26 @@ class ChainManager(models.Manager):
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))
qs = qs.prefetch_related(
models.Prefetch("credebtors__expenses", to_attr="all_expenses"),
models.Prefetch("credebtors__revenues", to_attr="all_revenues"),
)
qs = qs.annotate(
all_expenses_amount=models.Sum(
"credebtors__expenses__amount", distinct=True
)
)
qs = qs.annotate(
all_expenses_count=models.Count("credebtors__expenses", distinct=True)
)
qs = qs.annotate(
all_revenues_amount=models.Sum(
"credebtors__revenues__amount", distinct=True
)
)
qs = qs.annotate(
all_revenues_count=models.Count("credebtors__revenues", distinct=True)
)
return qs
@ -92,9 +107,12 @@ class CredebtorManager(models.Manager):
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"))
qs = qs.prefetch_related(
models.Prefetch("expenses", to_attr="all_expenses"),
models.Prefetch("revenues", to_attr="all_revenues"),
)
qs = qs.annotate(all_expenses_amount=models.Sum("expenses__amount"))
qs = qs.annotate(all_revenues_amount=models.Sum("revenues__amount"))
return qs
@ -494,7 +512,9 @@ class Pos(CampRelatedModel, UUIDModel):
)
team = models.ForeignKey(
"teams.Team", on_delete=models.PROTECT, help_text="The Team managning this POS",
"teams.Team",
on_delete=models.PROTECT,
help_text="The Team managning this POS",
)
def save(self, **kwargs):
@ -559,7 +579,8 @@ class PosReport(CampRelatedModel, UUIDModel):
)
comments = models.TextField(
blank=True, help_text="Any comments about this PosReport",
blank=True,
help_text="Any comments about this PosReport",
)
dkk_sales_izettle = models.PositiveIntegerField(
@ -567,7 +588,8 @@ class PosReport(CampRelatedModel, UUIDModel):
)
hax_sold_izettle = models.PositiveIntegerField(
default=0, help_text="The number of HAX sold through the iZettle from the POS",
default=0,
help_text="The number of HAX sold through the iZettle from the POS",
)
hax_sold_website = models.PositiveIntegerField(

View file

@ -0,0 +1,28 @@
# Generated by Django 3.1 on 2020-10-17 00:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("shop", "0063_auto_20200812_1732"),
]
operations = [
migrations.AddField(
model_name="product",
name="comment",
field=models.TextField(
blank=True, help_text="Internal comments for this product."
),
),
migrations.AddField(
model_name="product",
name="cost",
field=models.IntegerField(
default=0,
help_text="The cost for this product, including VAT. Used for profit calculations in the economy system.",
),
),
]

View file

@ -432,6 +432,15 @@ class Product(CreatedUpdatedModel, UUIDModel):
blank=True,
)
cost = models.IntegerField(
default=0,
help_text="The cost for this product, including VAT. Used for profit calculations in the economy system.",
)
comment = models.TextField(
blank=True, help_text="Internal comments for this product."
)
objects = ProductQuerySet.as_manager()
def __str__(self):
@ -442,7 +451,7 @@ class Product(CreatedUpdatedModel, UUIDModel):
raise ValidationError("Products with category Tickets need a ticket_type")
def is_available(self):
""" Is the product available or not?
"""Is the product available or not?
Checks for the following:

View file

@ -11,11 +11,12 @@ from camps.models import Camp
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.core.management.base import BaseCommand
from django.db.models.signals import post_save
from django.utils import timezone
from django.utils.crypto import get_random_string
from economy.models import Chain, Credebtor, Expense, Revenue
from events.models import Routing, Type
from facilities.models import (
Facility,
@ -53,7 +54,6 @@ 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")
@ -65,7 +65,11 @@ class ChainFactory(factory.django.DjangoModelFactory):
model = Chain
name = factory.Faker("company")
slug = factory.LazyAttribute(lambda f: unique_slugify(f.name, Chain.objects.all().values_list("slug", flat=True)))
slug = factory.LazyAttribute(
lambda f: unique_slugify(
f.name, Chain.objects.all().values_list("slug", flat=True)
)
)
class CredebtorFactory(factory.django.DjangoModelFactory):
@ -74,7 +78,11 @@ class CredebtorFactory(factory.django.DjangoModelFactory):
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)))
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")
@ -89,7 +97,9 @@ class ExpenseFactory(factory.django.DjangoModelFactory):
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 = 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])
@ -105,7 +115,9 @@ class RevenueFactory(factory.django.DjangoModelFactory):
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 = 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])
@ -567,7 +579,7 @@ class Command(BaseCommand):
try:
CredebtorFactory.create_batch(50)
except ValidationError:
self.outout("Name conflict, retrying...")
self.output("Name conflict, retrying...")
CredebtorFactory.create_batch(50)
for _ in range(20):
# add 20 more credebtors to random existing chains
@ -856,10 +868,18 @@ class Command(BaseCommand):
capacity=50,
)
locations["food_area"] = EventLocation.objects.create(
name="Food Area", slug="food-area", icon="utensils", camp=camp, capacity=50,
name="Food Area",
slug="food-area",
icon="utensils",
camp=camp,
capacity=50,
)
locations["infodesk"] = EventLocation.objects.create(
name="Infodesk", slug="infodesk", icon="info", camp=camp, capacity=20,
name="Infodesk",
slug="infodesk",
icon="info",
camp=camp,
capacity=20,
)
# add workshop room conflicts (the big root can not be used while either