Blackness.
This commit is contained in:
parent
908d753eb1
commit
bf2f0c7898
1
src/.coverage
Normal file
1
src/.coverage
Normal file
File diff suppressed because one or more lines are too long
122
src/.ropeproject/config.py
Normal file
122
src/.ropeproject/config.py
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
# The default ``config.py``
|
||||||
|
# flake8: noqa
|
||||||
|
|
||||||
|
|
||||||
|
def set_prefs(prefs):
|
||||||
|
"""This function is called before opening the project"""
|
||||||
|
|
||||||
|
# Specify which files and folders to ignore in the project.
|
||||||
|
# Changes to ignored resources are not added to the history and
|
||||||
|
# VCSs. Also they are not returned in `Project.get_files()`.
|
||||||
|
# Note that ``?`` and ``*`` match all characters but slashes.
|
||||||
|
# '*.pyc': matches 'test.pyc' and 'pkg/test.pyc'
|
||||||
|
# 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc'
|
||||||
|
# '.svn': matches 'pkg/.svn' and all of its children
|
||||||
|
# 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o'
|
||||||
|
# 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o'
|
||||||
|
prefs["ignored_resources"] = [
|
||||||
|
"*.pyc",
|
||||||
|
"*~",
|
||||||
|
".ropeproject",
|
||||||
|
".hg",
|
||||||
|
".svn",
|
||||||
|
"_svn",
|
||||||
|
".git",
|
||||||
|
".tox",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Specifies which files should be considered python files. It is
|
||||||
|
# useful when you have scripts inside your project. Only files
|
||||||
|
# ending with ``.py`` are considered to be python files by
|
||||||
|
# default.
|
||||||
|
# prefs['python_files'] = ['*.py']
|
||||||
|
|
||||||
|
# Custom source folders: By default rope searches the project
|
||||||
|
# for finding source folders (folders that should be searched
|
||||||
|
# for finding modules). You can add paths to that list. Note
|
||||||
|
# that rope guesses project source folders correctly most of the
|
||||||
|
# time; use this if you have any problems.
|
||||||
|
# The folders should be relative to project root and use '/' for
|
||||||
|
# separating folders regardless of the platform rope is running on.
|
||||||
|
# 'src/my_source_folder' for instance.
|
||||||
|
# prefs.add('source_folders', 'src')
|
||||||
|
|
||||||
|
# You can extend python path for looking up modules
|
||||||
|
# prefs.add('python_path', '~/python/')
|
||||||
|
|
||||||
|
# Should rope save object information or not.
|
||||||
|
prefs["save_objectdb"] = True
|
||||||
|
prefs["compress_objectdb"] = False
|
||||||
|
|
||||||
|
# If `True`, rope analyzes each module when it is being saved.
|
||||||
|
prefs["automatic_soa"] = True
|
||||||
|
# The depth of calls to follow in static object analysis
|
||||||
|
prefs["soa_followed_calls"] = 0
|
||||||
|
|
||||||
|
# If `False` when running modules or unit tests "dynamic object
|
||||||
|
# analysis" is turned off. This makes them much faster.
|
||||||
|
prefs["perform_doa"] = True
|
||||||
|
|
||||||
|
# Rope can check the validity of its object DB when running.
|
||||||
|
prefs["validate_objectdb"] = True
|
||||||
|
|
||||||
|
# How many undos to hold?
|
||||||
|
prefs["max_history_items"] = 32
|
||||||
|
|
||||||
|
# Shows whether to save history across sessions.
|
||||||
|
prefs["save_history"] = True
|
||||||
|
prefs["compress_history"] = False
|
||||||
|
|
||||||
|
# Set the number spaces used for indenting. According to
|
||||||
|
# :PEP:`8`, it is best to use 4 spaces. Since most of rope's
|
||||||
|
# unit-tests use 4 spaces it is more reliable, too.
|
||||||
|
prefs["indent_size"] = 4
|
||||||
|
|
||||||
|
# Builtin and c-extension modules that are allowed to be imported
|
||||||
|
# and inspected by rope.
|
||||||
|
prefs["extension_modules"] = []
|
||||||
|
|
||||||
|
# Add all standard c-extensions to extension_modules list.
|
||||||
|
prefs["import_dynload_stdmods"] = True
|
||||||
|
|
||||||
|
# If `True` modules with syntax errors are considered to be empty.
|
||||||
|
# The default value is `False`; When `False` syntax errors raise
|
||||||
|
# `rope.base.exceptions.ModuleSyntaxError` exception.
|
||||||
|
prefs["ignore_syntax_errors"] = False
|
||||||
|
|
||||||
|
# If `True`, rope ignores unresolvable imports. Otherwise, they
|
||||||
|
# appear in the importing namespace.
|
||||||
|
prefs["ignore_bad_imports"] = False
|
||||||
|
|
||||||
|
# If `True`, rope will insert new module imports as
|
||||||
|
# `from <package> import <module>` by default.
|
||||||
|
prefs["prefer_module_from_imports"] = False
|
||||||
|
|
||||||
|
# If `True`, rope will transform a comma list of imports into
|
||||||
|
# multiple separate import statements when organizing
|
||||||
|
# imports.
|
||||||
|
prefs["split_imports"] = False
|
||||||
|
|
||||||
|
# If `True`, rope will remove all top-level import statements and
|
||||||
|
# reinsert them at the top of the module when making changes.
|
||||||
|
prefs["pull_imports_to_top"] = True
|
||||||
|
|
||||||
|
# If `True`, rope will sort imports alphabetically by module name instead of
|
||||||
|
# alphabetically by import statement, with from imports after normal
|
||||||
|
# imports.
|
||||||
|
prefs["sort_imports_alphabetically"] = False
|
||||||
|
|
||||||
|
# Location of implementation of rope.base.oi.type_hinting.interfaces.ITypeHintingFactory
|
||||||
|
# In general case, you don't have to change this value, unless you're an rope expert.
|
||||||
|
# Change this value to inject you own implementations of interfaces
|
||||||
|
# listed in module rope.base.oi.type_hinting.providers.interfaces
|
||||||
|
# For example, you can add you own providers for Django Models, or disable the search
|
||||||
|
# type-hinting in a class hierarchy, etc.
|
||||||
|
prefs[
|
||||||
|
"type_hinting_factory"
|
||||||
|
] = "rope.base.oi.type_hinting.factory.default_type_hinting_factory"
|
||||||
|
|
||||||
|
|
||||||
|
def project_opened(project):
|
||||||
|
"""This function is called after opening the project"""
|
||||||
|
# Do whatever you like here!
|
BIN
src/.ropeproject/globalnames
Normal file
BIN
src/.ropeproject/globalnames
Normal file
Binary file not shown.
BIN
src/.ropeproject/history
Normal file
BIN
src/.ropeproject/history
Normal file
Binary file not shown.
BIN
src/.ropeproject/objectdb
Normal file
BIN
src/.ropeproject/objectdb
Normal file
Binary file not shown.
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class BackofficeConfig(AppConfig):
|
class BackofficeConfig(AppConfig):
|
||||||
name = 'backoffice'
|
name = "backoffice"
|
||||||
|
|
|
@ -5,6 +5,7 @@ class OrgaTeamPermissionMixin(RaisePermissionRequiredMixin):
|
||||||
"""
|
"""
|
||||||
Permission mixin for views used by Orga Team
|
Permission mixin for views used by Orga Team
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permission_required = ("camps.backoffice_permission", "camps.orgateam_permission")
|
permission_required = ("camps.backoffice_permission", "camps.orgateam_permission")
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,13 +13,18 @@ class EconomyTeamPermissionMixin(RaisePermissionRequiredMixin):
|
||||||
"""
|
"""
|
||||||
Permission mixin for views used by Economy Team
|
Permission mixin for views used by Economy Team
|
||||||
"""
|
"""
|
||||||
permission_required = ("camps.backoffice_permission", "camps.economyteam_permission")
|
|
||||||
|
permission_required = (
|
||||||
|
"camps.backoffice_permission",
|
||||||
|
"camps.economyteam_permission",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InfoTeamPermissionMixin(RaisePermissionRequiredMixin):
|
class InfoTeamPermissionMixin(RaisePermissionRequiredMixin):
|
||||||
"""
|
"""
|
||||||
Permission mixin for views used by Info Team/InfoDesk
|
Permission mixin for views used by Info Team/InfoDesk
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permission_required = ("camps.backoffice_permission", "camps.infoteam_permission")
|
permission_required = ("camps.backoffice_permission", "camps.infoteam_permission")
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,5 +32,8 @@ class ContentTeamPermissionMixin(RaisePermissionRequiredMixin):
|
||||||
"""
|
"""
|
||||||
Permission mixin for views used by Content Team
|
Permission mixin for views used by Content Team
|
||||||
"""
|
"""
|
||||||
permission_required = ("camps.backoffice_permission", "program.contentteam_permission")
|
|
||||||
|
|
||||||
|
permission_required = (
|
||||||
|
"camps.backoffice_permission",
|
||||||
|
"program.contentteam_permission",
|
||||||
|
)
|
||||||
|
|
|
@ -2,93 +2,156 @@ from django.urls import path, include
|
||||||
from .views import *
|
from .views import *
|
||||||
|
|
||||||
|
|
||||||
app_name = 'backoffice'
|
app_name = "backoffice"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', BackofficeIndexView.as_view(), name='index'),
|
path("", BackofficeIndexView.as_view(), name="index"),
|
||||||
# infodesk
|
# infodesk
|
||||||
path('product_handout/', ProductHandoutView.as_view(), name='product_handout'),
|
path("product_handout/", ProductHandoutView.as_view(), name="product_handout"),
|
||||||
path('badge_handout/', BadgeHandoutView.as_view(), name='badge_handout'),
|
path("badge_handout/", BadgeHandoutView.as_view(), name="badge_handout"),
|
||||||
path('ticket_checkin/', TicketCheckinView.as_view(), name='ticket_checkin'),
|
path("ticket_checkin/", TicketCheckinView.as_view(), name="ticket_checkin"),
|
||||||
|
|
||||||
# public names
|
# public names
|
||||||
path('public_credit_names/', ApproveNamesView.as_view(), name='public_credit_names'),
|
path(
|
||||||
|
"public_credit_names/", ApproveNamesView.as_view(), name="public_credit_names"
|
||||||
|
),
|
||||||
# merchandise orders
|
# merchandise orders
|
||||||
path('merchandise_orders/', MerchandiseOrdersView.as_view(), name='merchandise_orders'),
|
path(
|
||||||
path('merchandise_to_order/', MerchandiseToOrderView.as_view(), name='merchandise_to_order'),
|
"merchandise_orders/",
|
||||||
|
MerchandiseOrdersView.as_view(),
|
||||||
|
name="merchandise_orders",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"merchandise_to_order/",
|
||||||
|
MerchandiseToOrderView.as_view(),
|
||||||
|
name="merchandise_to_order",
|
||||||
|
),
|
||||||
# village orders
|
# village orders
|
||||||
path('village_orders/', VillageOrdersView.as_view(), name='village_orders'),
|
path("village_orders/", VillageOrdersView.as_view(), name="village_orders"),
|
||||||
path('village_to_order/', VillageToOrderView.as_view(), name='village_to_order'),
|
path("village_to_order/", VillageToOrderView.as_view(), name="village_to_order"),
|
||||||
|
|
||||||
# manage proposals
|
# manage proposals
|
||||||
path('manage_proposals/', include([
|
path(
|
||||||
path('', ManageProposalsView.as_view(), name='manage_proposals'),
|
"manage_proposals/",
|
||||||
path('speakers/<uuid:pk>/', SpeakerProposalManageView.as_view(), name='speakerproposal_manage'),
|
include(
|
||||||
path('events/<uuid:pk>/', EventProposalManageView.as_view(), name='eventproposal_manage'),
|
[
|
||||||
])),
|
path("", ManageProposalsView.as_view(), name="manage_proposals"),
|
||||||
|
path(
|
||||||
|
"speakers/<uuid:pk>/",
|
||||||
|
SpeakerProposalManageView.as_view(),
|
||||||
|
name="speakerproposal_manage",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"events/<uuid:pk>/",
|
||||||
|
EventProposalManageView.as_view(),
|
||||||
|
name="eventproposal_manage",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
# economy
|
# economy
|
||||||
path('economy/',
|
path(
|
||||||
include([
|
"economy/",
|
||||||
|
include(
|
||||||
|
[
|
||||||
# chains & credebtors
|
# chains & credebtors
|
||||||
path('chains/',
|
|
||||||
include([
|
|
||||||
path(
|
path(
|
||||||
'',
|
"chains/",
|
||||||
ChainListView.as_view(),
|
include(
|
||||||
name='chain_list'
|
[
|
||||||
),
|
path("", ChainListView.as_view(), name="chain_list"),
|
||||||
path('<slug:chain_slug>/',
|
|
||||||
include([
|
|
||||||
path(
|
path(
|
||||||
'',
|
"<slug:chain_slug>/",
|
||||||
|
include(
|
||||||
|
[
|
||||||
|
path(
|
||||||
|
"",
|
||||||
ChainDetailView.as_view(),
|
ChainDetailView.as_view(),
|
||||||
name='chain_detail'
|
name="chain_detail",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'<slug:credebtor_slug>/',
|
"<slug:credebtor_slug>/",
|
||||||
CredebtorDetailView.as_view(),
|
CredebtorDetailView.as_view(),
|
||||||
name='credebtor_detail'
|
name="credebtor_detail",
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
# expenses
|
# expenses
|
||||||
path('expenses/',
|
path(
|
||||||
include([
|
"expenses/",
|
||||||
path('', ExpenseListView.as_view(), name='expense_list'),
|
include(
|
||||||
path('<uuid:pk>/', ExpenseDetailView.as_view(), name='expense_detail'),
|
[
|
||||||
]),
|
path("", ExpenseListView.as_view(), name="expense_list"),
|
||||||
|
path(
|
||||||
|
"<uuid:pk>/",
|
||||||
|
ExpenseDetailView.as_view(),
|
||||||
|
name="expense_detail",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
# revenues
|
# revenues
|
||||||
path('revenues/',
|
path(
|
||||||
include([
|
"revenues/",
|
||||||
path('', RevenueListView.as_view(), name='revenue_list'),
|
include(
|
||||||
path('<uuid:pk>/', RevenueDetailView.as_view(), name='revenue_detail'),
|
[
|
||||||
]),
|
path("", RevenueListView.as_view(), name="revenue_list"),
|
||||||
|
path(
|
||||||
|
"<uuid:pk>/",
|
||||||
|
RevenueDetailView.as_view(),
|
||||||
|
name="revenue_detail",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
# reimbursements
|
# reimbursements
|
||||||
path('reimbursements/',
|
path(
|
||||||
include([
|
"reimbursements/",
|
||||||
path('', ReimbursementListView.as_view(), name='reimbursement_list'),
|
include(
|
||||||
path('<uuid:pk>/',
|
[
|
||||||
include([
|
path(
|
||||||
path('', ReimbursementDetailView.as_view(), name='reimbursement_detail'),
|
"",
|
||||||
path('update/', ReimbursementUpdateView.as_view(), name='reimbursement_update'),
|
ReimbursementListView.as_view(),
|
||||||
path('delete/', ReimbursementDeleteView.as_view(), name='reimbursement_delete'),
|
name="reimbursement_list",
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
path('create/', ReimbursementCreateUserSelectView.as_view(), name='reimbursement_create_userselect'),
|
path(
|
||||||
path('create/<int:user_id>/', ReimbursementCreateView.as_view(), name='reimbursement_create'),
|
"<uuid:pk>/",
|
||||||
]),
|
include(
|
||||||
|
[
|
||||||
|
path(
|
||||||
|
"",
|
||||||
|
ReimbursementDetailView.as_view(),
|
||||||
|
name="reimbursement_detail",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"update/",
|
||||||
|
ReimbursementUpdateView.as_view(),
|
||||||
|
name="reimbursement_update",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"delete/",
|
||||||
|
ReimbursementDeleteView.as_view(),
|
||||||
|
name="reimbursement_delete",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"create/",
|
||||||
|
ReimbursementCreateUserSelectView.as_view(),
|
||||||
|
name="reimbursement_create_userselect",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"create/<int:user_id>/",
|
||||||
|
ReimbursementCreateView.as_view(),
|
||||||
|
name="reimbursement_create",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,8 @@ class BackofficeIndexView(CampViewMixin, RaisePermissionRequiredMixin, TemplateV
|
||||||
"""
|
"""
|
||||||
The Backoffice index view only requires camps.backoffice_permission so we use RaisePermissionRequiredMixin directly
|
The Backoffice index view only requires camps.backoffice_permission so we use RaisePermissionRequiredMixin directly
|
||||||
"""
|
"""
|
||||||
permission_required = ("camps.backoffice_permission")
|
|
||||||
|
permission_required = "camps.backoffice_permission"
|
||||||
template_name = "index.html"
|
template_name = "index.html"
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,13 +43,13 @@ class ProductHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
||||||
handed_out=False,
|
handed_out=False,
|
||||||
order__paid=True,
|
order__paid=True,
|
||||||
order__refunded=False,
|
order__refunded=False,
|
||||||
order__cancelled=False
|
order__cancelled=False,
|
||||||
).order_by('order')
|
).order_by("order")
|
||||||
|
|
||||||
|
|
||||||
class BadgeHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
class BadgeHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
||||||
template_name = "badge_handout.html"
|
template_name = "badge_handout.html"
|
||||||
context_object_name = 'tickets'
|
context_object_name = "tickets"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
shoptickets = ShopTicket.objects.filter(badge_handed_out=False)
|
shoptickets = ShopTicket.objects.filter(badge_handed_out=False)
|
||||||
|
@ -59,7 +60,7 @@ class BadgeHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
||||||
|
|
||||||
class TicketCheckinView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
class TicketCheckinView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
||||||
template_name = "ticket_checkin.html"
|
template_name = "ticket_checkin.html"
|
||||||
context_object_name = 'tickets'
|
context_object_name = "tickets"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
shoptickets = ShopTicket.objects.filter(checked_in=False)
|
shoptickets = ShopTicket.objects.filter(checked_in=False)
|
||||||
|
@ -70,30 +71,31 @@ class TicketCheckinView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
||||||
|
|
||||||
class ApproveNamesView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
class ApproveNamesView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
||||||
template_name = "approve_public_credit_names.html"
|
template_name = "approve_public_credit_names.html"
|
||||||
context_object_name = 'profiles'
|
context_object_name = "profiles"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
return Profile.objects.filter(public_credit_name_approved=False).exclude(public_credit_name='')
|
return Profile.objects.filter(public_credit_name_approved=False).exclude(
|
||||||
|
public_credit_name=""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ManageProposalsView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class ManageProposalsView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
"""
|
"""
|
||||||
This view shows a list of pending SpeakerProposal and EventProposals.
|
This view shows a list of pending SpeakerProposal and EventProposals.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
template_name = "manage_proposals.html"
|
template_name = "manage_proposals.html"
|
||||||
context_object_name = 'speakerproposals'
|
context_object_name = "speakerproposals"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
return SpeakerProposal.objects.filter(
|
return SpeakerProposal.objects.filter(
|
||||||
camp=self.camp,
|
camp=self.camp, proposal_status=SpeakerProposal.PROPOSAL_PENDING
|
||||||
proposal_status=SpeakerProposal.PROPOSAL_PENDING
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['eventproposals'] = EventProposal.objects.filter(
|
context["eventproposals"] = EventProposal.objects.filter(
|
||||||
track__camp=self.camp,
|
track__camp=self.camp, proposal_status=EventProposal.PROPOSAL_PENDING
|
||||||
proposal_status=EventProposal.PROPOSAL_PENDING
|
|
||||||
)
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -102,6 +104,7 @@ class ProposalManageBaseView(CampViewMixin, ContentTeamPermissionMixin, UpdateVi
|
||||||
"""
|
"""
|
||||||
This class contains the shared logic between SpeakerProposalManageView and EventProposalManageView
|
This class contains the shared logic between SpeakerProposalManageView and EventProposalManageView
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fields = []
|
fields = []
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -109,21 +112,24 @@ class ProposalManageBaseView(CampViewMixin, ContentTeamPermissionMixin, UpdateVi
|
||||||
We have two submit buttons in this form, Approve and Reject
|
We have two submit buttons in this form, Approve and Reject
|
||||||
"""
|
"""
|
||||||
logger.debug(form.data)
|
logger.debug(form.data)
|
||||||
if 'approve' in form.data:
|
if "approve" in form.data:
|
||||||
# approve button was pressed
|
# approve button was pressed
|
||||||
form.instance.mark_as_approved(self.request)
|
form.instance.mark_as_approved(self.request)
|
||||||
elif 'reject' in form.data:
|
elif "reject" in form.data:
|
||||||
# reject button was pressed
|
# reject button was pressed
|
||||||
form.instance.mark_as_rejected(self.request)
|
form.instance.mark_as_rejected(self.request)
|
||||||
else:
|
else:
|
||||||
messages.error(self.request, "Unknown submit action")
|
messages.error(self.request, "Unknown submit action")
|
||||||
return redirect(reverse('backoffice:manage_proposals', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse("backoffice:manage_proposals", kwargs={"camp_slug": self.camp.slug})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SpeakerProposalManageView(ProposalManageBaseView):
|
class SpeakerProposalManageView(ProposalManageBaseView):
|
||||||
"""
|
"""
|
||||||
This view allows an admin to approve/reject SpeakerProposals
|
This view allows an admin to approve/reject SpeakerProposals
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = SpeakerProposal
|
model = SpeakerProposal
|
||||||
template_name = "manage_speakerproposal.html"
|
template_name = "manage_speakerproposal.html"
|
||||||
|
|
||||||
|
@ -132,6 +138,7 @@ class EventProposalManageView(ProposalManageBaseView):
|
||||||
"""
|
"""
|
||||||
This view allows an admin to approve/reject EventProposals
|
This view allows an admin to approve/reject EventProposals
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = EventProposal
|
model = EventProposal
|
||||||
template_name = "manage_eventproposal.html"
|
template_name = "manage_eventproposal.html"
|
||||||
|
|
||||||
|
@ -140,34 +147,34 @@ class MerchandiseOrdersView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
||||||
template_name = "orders_merchandise.html"
|
template_name = "orders_merchandise.html"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
camp_prefix = 'BornHack {}'.format(timezone.now().year)
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
||||||
|
|
||||||
return OrderProductRelation.objects.filter(
|
return (
|
||||||
|
OrderProductRelation.objects.filter(
|
||||||
handed_out=False,
|
handed_out=False,
|
||||||
order__paid=True,
|
order__paid=True,
|
||||||
order__refunded=False,
|
order__refunded=False,
|
||||||
order__cancelled=False,
|
order__cancelled=False,
|
||||||
product__category__name='Merchandise',
|
product__category__name="Merchandise",
|
||||||
).filter(
|
)
|
||||||
product__name__startswith=camp_prefix
|
.filter(product__name__startswith=camp_prefix)
|
||||||
).order_by('order')
|
.order_by("order")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MerchandiseToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
class MerchandiseToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
||||||
template_name = "merchandise_to_order.html"
|
template_name = "merchandise_to_order.html"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
camp_prefix = 'BornHack {}'.format(timezone.now().year)
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
||||||
|
|
||||||
order_relations = OrderProductRelation.objects.filter(
|
order_relations = OrderProductRelation.objects.filter(
|
||||||
handed_out=False,
|
handed_out=False,
|
||||||
order__paid=True,
|
order__paid=True,
|
||||||
order__refunded=False,
|
order__refunded=False,
|
||||||
order__cancelled=False,
|
order__cancelled=False,
|
||||||
product__category__name='Merchandise',
|
product__category__name="Merchandise",
|
||||||
).filter(
|
).filter(product__name__startswith=camp_prefix)
|
||||||
product__name__startswith=camp_prefix
|
|
||||||
)
|
|
||||||
|
|
||||||
merchandise_orders = {}
|
merchandise_orders = {}
|
||||||
for relation in order_relations:
|
for relation in order_relations:
|
||||||
|
@ -178,7 +185,7 @@ class MerchandiseToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateVie
|
||||||
merchandise_orders[relation.product.name] = relation.quantity
|
merchandise_orders[relation.product.name] = relation.quantity
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['merchandise'] = merchandise_orders
|
context["merchandise"] = merchandise_orders
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,34 +193,34 @@ class VillageOrdersView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
||||||
template_name = "orders_village.html"
|
template_name = "orders_village.html"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
camp_prefix = 'BornHack {}'.format(timezone.now().year)
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
||||||
|
|
||||||
return OrderProductRelation.objects.filter(
|
return (
|
||||||
|
OrderProductRelation.objects.filter(
|
||||||
handed_out=False,
|
handed_out=False,
|
||||||
order__paid=True,
|
order__paid=True,
|
||||||
order__refunded=False,
|
order__refunded=False,
|
||||||
order__cancelled=False,
|
order__cancelled=False,
|
||||||
product__category__name='Villages',
|
product__category__name="Villages",
|
||||||
).filter(
|
)
|
||||||
product__name__startswith=camp_prefix
|
.filter(product__name__startswith=camp_prefix)
|
||||||
).order_by('order')
|
.order_by("order")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
||||||
template_name = "village_to_order.html"
|
template_name = "village_to_order.html"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
camp_prefix = 'BornHack {}'.format(timezone.now().year)
|
camp_prefix = "BornHack {}".format(timezone.now().year)
|
||||||
|
|
||||||
order_relations = OrderProductRelation.objects.filter(
|
order_relations = OrderProductRelation.objects.filter(
|
||||||
handed_out=False,
|
handed_out=False,
|
||||||
order__paid=True,
|
order__paid=True,
|
||||||
order__refunded=False,
|
order__refunded=False,
|
||||||
order__cancelled=False,
|
order__cancelled=False,
|
||||||
product__category__name='Villages',
|
product__category__name="Villages",
|
||||||
).filter(
|
).filter(product__name__startswith=camp_prefix)
|
||||||
product__name__startswith=camp_prefix
|
|
||||||
)
|
|
||||||
|
|
||||||
village_orders = {}
|
village_orders = {}
|
||||||
for relation in order_relations:
|
for relation in order_relations:
|
||||||
|
@ -224,7 +231,7 @@ class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
||||||
village_orders[relation.product.name] = relation.quantity
|
village_orders[relation.product.name] = relation.quantity
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['village'] = village_orders
|
context["village"] = village_orders
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,27 +241,28 @@ class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
||||||
|
|
||||||
class ChainListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
class ChainListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||||
model = Chain
|
model = Chain
|
||||||
template_name = 'chain_list_backoffice.html'
|
template_name = "chain_list_backoffice.html"
|
||||||
|
|
||||||
|
|
||||||
class ChainDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
class ChainDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
||||||
model = Chain
|
model = Chain
|
||||||
template_name = 'chain_detail_backoffice.html'
|
template_name = "chain_detail_backoffice.html"
|
||||||
slug_url_kwarg = 'chain_slug'
|
slug_url_kwarg = "chain_slug"
|
||||||
|
|
||||||
|
|
||||||
class CredebtorDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
class CredebtorDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
||||||
model = Credebtor
|
model = Credebtor
|
||||||
template_name = 'credebtor_detail_backoffice.html'
|
template_name = "credebtor_detail_backoffice.html"
|
||||||
slug_url_kwarg = 'credebtor_slug'
|
slug_url_kwarg = "credebtor_slug"
|
||||||
|
|
||||||
|
|
||||||
################################
|
################################
|
||||||
########### EXPENSES ###########
|
########### EXPENSES ###########
|
||||||
|
|
||||||
|
|
||||||
class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||||
model = Expense
|
model = Expense
|
||||||
template_name = 'expense_list_backoffice.html'
|
template_name = "expense_list_backoffice.html"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -268,46 +276,53 @@ class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||||
Include unapproved expenses seperately
|
Include unapproved expenses seperately
|
||||||
"""
|
"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['unapproved_expenses'] = Expense.objects.filter(camp=self.camp, approved__isnull=True)
|
context["unapproved_expenses"] = Expense.objects.filter(
|
||||||
|
camp=self.camp, approved__isnull=True
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ExpenseDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
class ExpenseDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
||||||
model = Expense
|
model = Expense
|
||||||
template_name = 'expense_detail_backoffice.html'
|
template_name = "expense_detail_backoffice.html"
|
||||||
fields = ['notes']
|
fields = ["notes"]
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
"""
|
"""
|
||||||
We have two submit buttons in this form, Approve and Reject
|
We have two submit buttons in this form, Approve and Reject
|
||||||
"""
|
"""
|
||||||
expense = form.save()
|
expense = form.save()
|
||||||
if 'approve' in form.data:
|
if "approve" in form.data:
|
||||||
# approve button was pressed
|
# approve button was pressed
|
||||||
expense.approve(self.request)
|
expense.approve(self.request)
|
||||||
elif 'reject' in form.data:
|
elif "reject" in form.data:
|
||||||
# reject button was pressed
|
# reject button was pressed
|
||||||
expense.reject(self.request)
|
expense.reject(self.request)
|
||||||
else:
|
else:
|
||||||
messages.error(self.request, "Unknown submit action")
|
messages.error(self.request, "Unknown submit action")
|
||||||
return redirect(reverse('backoffice:expense_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse("backoffice:expense_list", kwargs={"camp_slug": self.camp.slug})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
########### REIMBURSEMENTS ###########
|
########### REIMBURSEMENTS ###########
|
||||||
|
|
||||||
|
|
||||||
class ReimbursementListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
class ReimbursementListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||||
model = Reimbursement
|
model = Reimbursement
|
||||||
template_name = 'reimbursement_list_backoffice.html'
|
template_name = "reimbursement_list_backoffice.html"
|
||||||
|
|
||||||
|
|
||||||
class ReimbursementDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
class ReimbursementDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
||||||
model = Reimbursement
|
model = Reimbursement
|
||||||
template_name = 'reimbursement_detail_backoffice.html'
|
template_name = "reimbursement_detail_backoffice.html"
|
||||||
|
|
||||||
|
|
||||||
class ReimbursementCreateUserSelectView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
class ReimbursementCreateUserSelectView(
|
||||||
template_name = 'reimbursement_create_userselect.html'
|
CampViewMixin, EconomyTeamPermissionMixin, ListView
|
||||||
|
):
|
||||||
|
template_name = "reimbursement_create_userselect.html"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = User.objects.filter(
|
queryset = User.objects.filter(
|
||||||
|
@ -316,20 +331,22 @@ class ReimbursementCreateUserSelectView(CampViewMixin, EconomyTeamPermissionMixi
|
||||||
reimbursement__isnull=True,
|
reimbursement__isnull=True,
|
||||||
paid_by_bornhack=False,
|
paid_by_bornhack=False,
|
||||||
approved=True,
|
approved=True,
|
||||||
).values_list('user', flat=True).distinct()
|
)
|
||||||
|
.values_list("user", flat=True)
|
||||||
|
.distinct()
|
||||||
)
|
)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateView):
|
class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateView):
|
||||||
model = Reimbursement
|
model = Reimbursement
|
||||||
template_name = 'reimbursement_create.html'
|
template_name = "reimbursement_create.html"
|
||||||
fields = ['notes', 'paid']
|
fields = ["notes", "paid"]
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
""" Get the user from kwargs """
|
""" Get the user from kwargs """
|
||||||
print("inside dispatch() with method %s" % request.method)
|
print("inside dispatch() with method %s" % request.method)
|
||||||
self.reimbursement_user = get_object_or_404(User, pk=kwargs['user_id'])
|
self.reimbursement_user = get_object_or_404(User, pk=kwargs["user_id"])
|
||||||
|
|
||||||
# get response now so we have self.camp available below
|
# get response now so we have self.camp available below
|
||||||
response = super().dispatch(request, *args, **kwargs)
|
response = super().dispatch(request, *args, **kwargs)
|
||||||
|
@ -339,21 +356,27 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
# does this user have any approved and un-reimbursed expenses?
|
# does this user have any approved and un-reimbursed expenses?
|
||||||
if not self.reimbursement_user.expenses.filter(reimbursement__isnull=True, approved=True, paid_by_bornhack=False):
|
if not self.reimbursement_user.expenses.filter(
|
||||||
messages.error(request, "This user has no approved and unreimbursed expenses!")
|
reimbursement__isnull=True, approved=True, paid_by_bornhack=False
|
||||||
return(redirect(reverse('backoffice:index', kwargs={'camp_slug': self.camp.slug})))
|
):
|
||||||
|
messages.error(
|
||||||
|
request, "This user has no approved and unreimbursed expenses!"
|
||||||
|
)
|
||||||
|
return redirect(
|
||||||
|
reverse("backoffice:index", kwargs={"camp_slug": self.camp.slug})
|
||||||
|
)
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['expenses'] = Expense.objects.filter(
|
context["expenses"] = Expense.objects.filter(
|
||||||
user=self.reimbursement_user,
|
user=self.reimbursement_user,
|
||||||
approved=True,
|
approved=True,
|
||||||
reimbursement__isnull=True,
|
reimbursement__isnull=True,
|
||||||
paid_by_bornhack=False,
|
paid_by_bornhack=False,
|
||||||
)
|
)
|
||||||
context['total_amount'] = context['expenses'].aggregate(Sum('amount'))
|
context["total_amount"] = context["expenses"].aggregate(Sum("amount"))
|
||||||
context['reimbursement_user'] = self.reimbursement_user
|
context["reimbursement_user"] = self.reimbursement_user
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -361,17 +384,34 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
|
||||||
Set user and camp for the Reimbursement before saving
|
Set user and camp for the Reimbursement before saving
|
||||||
"""
|
"""
|
||||||
# get the expenses for this user
|
# get the expenses for this user
|
||||||
expenses = Expense.objects.filter(user=self.reimbursement_user, approved=True, reimbursement__isnull=True, paid_by_bornhack=False)
|
expenses = Expense.objects.filter(
|
||||||
|
user=self.reimbursement_user,
|
||||||
|
approved=True,
|
||||||
|
reimbursement__isnull=True,
|
||||||
|
paid_by_bornhack=False,
|
||||||
|
)
|
||||||
if not expenses:
|
if not expenses:
|
||||||
messages.error(self.request, "No expenses found")
|
messages.error(self.request, "No expenses found")
|
||||||
return redirect(reverse('backoffice:reimbursement_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse(
|
||||||
|
"backoffice:reimbursement_list",
|
||||||
|
kwargs={"camp_slug": self.camp.slug},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# get the Economy team for this camp
|
# get the Economy team for this camp
|
||||||
try:
|
try:
|
||||||
economyteam = Team.objects.get(camp=self.camp, name=settings.ECONOMYTEAM_NAME)
|
economyteam = Team.objects.get(
|
||||||
|
camp=self.camp, name=settings.ECONOMYTEAM_NAME
|
||||||
|
)
|
||||||
except Team.DoesNotExist:
|
except Team.DoesNotExist:
|
||||||
messages.error(self.request, "No economy team found")
|
messages.error(self.request, "No economy team found")
|
||||||
return redirect(reverse('backoffice:reimbursement_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse(
|
||||||
|
"backoffice:reimbursement_list",
|
||||||
|
kwargs={"camp_slug": self.camp.slug},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# create reimbursement in database
|
# create reimbursement in database
|
||||||
reimbursement = form.save(commit=False)
|
reimbursement = form.save(commit=False)
|
||||||
|
@ -387,50 +427,83 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
|
||||||
|
|
||||||
# create expense for this reimbursement
|
# create expense for this reimbursement
|
||||||
expense = Expense()
|
expense = Expense()
|
||||||
expense.camp=self.camp
|
expense.camp = self.camp
|
||||||
expense.user=self.request.user
|
expense.user = self.request.user
|
||||||
expense.amount=reimbursement.amount
|
expense.amount = reimbursement.amount
|
||||||
expense.description="Payment of reimbursement %s to %s" % (reimbursement.pk, reimbursement.reimbursement_user)
|
expense.description = "Payment of reimbursement %s to %s" % (
|
||||||
expense.paid_by_bornhack=True
|
reimbursement.pk,
|
||||||
expense.responsible_team=economyteam
|
reimbursement.reimbursement_user,
|
||||||
expense.approved=True
|
)
|
||||||
expense.reimbursement=reimbursement
|
expense.paid_by_bornhack = True
|
||||||
expense.invoice_date=timezone.now()
|
expense.responsible_team = economyteam
|
||||||
expense.creditor=Credebtor.objects.get(name='Reimbursement')
|
expense.approved = True
|
||||||
expense.invoice.save("na.jpg", File(open(os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/na.jpg"), "rb")))
|
expense.reimbursement = reimbursement
|
||||||
|
expense.invoice_date = timezone.now()
|
||||||
|
expense.creditor = Credebtor.objects.get(name="Reimbursement")
|
||||||
|
expense.invoice.save(
|
||||||
|
"na.jpg",
|
||||||
|
File(
|
||||||
|
open(
|
||||||
|
os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/na.jpg"),
|
||||||
|
"rb",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
expense.save()
|
expense.save()
|
||||||
|
|
||||||
messages.success(self.request, "Reimbursement %s has been created with invoice_date %s" % (reimbursement.pk, timezone.now()))
|
messages.success(
|
||||||
return redirect(reverse('backoffice:reimbursement_detail', kwargs={'camp_slug': self.camp.slug, 'pk': reimbursement.pk}))
|
self.request,
|
||||||
|
"Reimbursement %s has been created with invoice_date %s"
|
||||||
|
% (reimbursement.pk, timezone.now()),
|
||||||
|
)
|
||||||
|
return redirect(
|
||||||
|
reverse(
|
||||||
|
"backoffice:reimbursement_detail",
|
||||||
|
kwargs={"camp_slug": self.camp.slug, "pk": reimbursement.pk},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ReimbursementUpdateView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
class ReimbursementUpdateView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
||||||
model = Reimbursement
|
model = Reimbursement
|
||||||
template_name = 'reimbursement_form.html'
|
template_name = "reimbursement_form.html"
|
||||||
fields = ['notes', 'paid']
|
fields = ["notes", "paid"]
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('backoffice:reimbursement_detail', kwargs={'camp_slug': self.camp.slug, 'pk': self.get_object().pk})
|
return reverse(
|
||||||
|
"backoffice:reimbursement_detail",
|
||||||
|
kwargs={"camp_slug": self.camp.slug, "pk": self.get_object().pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ReimbursementDeleteView(CampViewMixin, EconomyTeamPermissionMixin, DeleteView):
|
class ReimbursementDeleteView(CampViewMixin, EconomyTeamPermissionMixin, DeleteView):
|
||||||
model = Reimbursement
|
model = Reimbursement
|
||||||
template_name = 'reimbursement_delete.html'
|
template_name = "reimbursement_delete.html"
|
||||||
fields = ['notes', 'paid']
|
fields = ["notes", "paid"]
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
response = super().dispatch(request, *args, **kwargs)
|
response = super().dispatch(request, *args, **kwargs)
|
||||||
if self.get_object().paid:
|
if self.get_object().paid:
|
||||||
messages.error(request, "This reimbursement has already been paid so it cannot be deleted")
|
messages.error(
|
||||||
return redirect(reverse('backoffice:reimbursement_list', kwargs={'camp_slug': self.camp.slug}))
|
request,
|
||||||
|
"This reimbursement has already been paid so it cannot be deleted",
|
||||||
|
)
|
||||||
|
return redirect(
|
||||||
|
reverse(
|
||||||
|
"backoffice:reimbursement_list",
|
||||||
|
kwargs={"camp_slug": self.camp.slug},
|
||||||
|
)
|
||||||
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
################################
|
################################
|
||||||
########### REVENUES ###########
|
########### REVENUES ###########
|
||||||
|
|
||||||
|
|
||||||
class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||||
model = Revenue
|
model = Revenue
|
||||||
template_name = 'revenue_list_backoffice.html'
|
template_name = "revenue_list_backoffice.html"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -444,27 +517,30 @@ class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||||
Include unapproved revenues seperately
|
Include unapproved revenues seperately
|
||||||
"""
|
"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['unapproved_revenues'] = Revenue.objects.filter(camp=self.camp, approved__isnull=True)
|
context["unapproved_revenues"] = Revenue.objects.filter(
|
||||||
|
camp=self.camp, approved__isnull=True
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
||||||
model = Revenue
|
model = Revenue
|
||||||
template_name = 'revenue_detail_backoffice.html'
|
template_name = "revenue_detail_backoffice.html"
|
||||||
fields = ['notes']
|
fields = ["notes"]
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
"""
|
"""
|
||||||
We have two submit buttons in this form, Approve and Reject
|
We have two submit buttons in this form, Approve and Reject
|
||||||
"""
|
"""
|
||||||
revenue = form.save()
|
revenue = form.save()
|
||||||
if 'approve' in form.data:
|
if "approve" in form.data:
|
||||||
# approve button was pressed
|
# approve button was pressed
|
||||||
revenue.approve(self.request)
|
revenue.approve(self.request)
|
||||||
elif 'reject' in form.data:
|
elif "reject" in form.data:
|
||||||
# reject button was pressed
|
# reject button was pressed
|
||||||
revenue.reject(self.request)
|
revenue.reject(self.request)
|
||||||
else:
|
else:
|
||||||
messages.error(self.request, "Unknown submit action")
|
messages.error(self.request, "Unknown submit action")
|
||||||
return redirect(reverse('backoffice:revenue_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug})
|
||||||
|
)
|
||||||
|
|
|
@ -9,5 +9,5 @@ class ProductCategoryAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(Product)
|
@admin.register(Product)
|
||||||
class ProductAdmin(admin.ModelAdmin):
|
class ProductAdmin(admin.ModelAdmin):
|
||||||
list_display = ['name', 'price', 'category', 'in_stock']
|
list_display = ["name", "price", "category", "in_stock"]
|
||||||
list_editable = ['in_stock']
|
list_editable = ["in_stock"]
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class BarConfig(AppConfig):
|
class BarConfig(AppConfig):
|
||||||
name = 'bar'
|
name = "bar"
|
||||||
|
|
|
@ -10,36 +10,55 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0022_camp_colour")]
|
||||||
('camps', '0022_camp_colour'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Product',
|
name="Product",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=255)),
|
"id",
|
||||||
('price', models.IntegerField()),
|
models.AutoField(
|
||||||
('in_stock', models.BooleanField(default=True)),
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=255)),
|
||||||
|
("price", models.IntegerField()),
|
||||||
|
("in_stock", models.BooleanField(default=True)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='ProductCategory',
|
name="ProductCategory",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"id",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.AutoField(
|
||||||
('name', models.CharField(max_length=255)),
|
auto_created=True,
|
||||||
('camp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camps.Camp')),
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
("name", models.CharField(max_length=255)),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="camps.Camp"
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"abstract": False},
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='product',
|
model_name="product",
|
||||||
name='category',
|
name="category",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bar.ProductCategory'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="bar.ProductCategory"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,22 +8,20 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("bar", "0001_initial")]
|
||||||
('bar', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.AlterModelOptions(name="product", options={"ordering": ("name",)}),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='product',
|
name="productcategory", options={"ordering": ("name",)}
|
||||||
options={'ordering': ('name',)},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='productcategory',
|
|
||||||
options={'ordering': ('name',)},
|
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='product',
|
model_name="product",
|
||||||
name='category',
|
name="category",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='bar.ProductCategory'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="products",
|
||||||
|
to="bar.ProductCategory",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,19 +8,23 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("bar", "0002_auto_20170916_2128")]
|
||||||
('bar', '0002_auto_20170916_2128'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='product',
|
model_name="product",
|
||||||
name='category',
|
name="category",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='products', to='bar.ProductCategory'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="products",
|
||||||
|
to="bar.ProductCategory",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='productcategory',
|
model_name="productcategory",
|
||||||
name='camp',
|
name="camp",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='camps.Camp'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT, to="camps.Camp"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,7 @@ from utils.models import CampRelatedModel
|
||||||
|
|
||||||
class ProductCategory(CampRelatedModel):
|
class ProductCategory(CampRelatedModel):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
camp = models.ForeignKey('camps.Camp', on_delete=models.PROTECT)
|
camp = models.ForeignKey("camps.Camp", on_delete=models.PROTECT)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -17,9 +17,7 @@ class Product(models.Model):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
price = models.IntegerField()
|
price = models.IntegerField()
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
ProductCategory,
|
ProductCategory, related_name="products", on_delete=models.PROTECT
|
||||||
related_name="products",
|
|
||||||
on_delete=models.PROTECT
|
|
||||||
)
|
)
|
||||||
in_stock = models.BooleanField(default=True)
|
in_stock = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,3 @@ from channels.routing import get_default_application
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bornhack.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bornhack.settings")
|
||||||
django.setup()
|
django.setup()
|
||||||
application = get_default_application()
|
application = get_default_application()
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@ from channels.auth import AuthMiddlewareStack
|
||||||
from program.consumers import ScheduleConsumer
|
from program.consumers import ScheduleConsumer
|
||||||
|
|
||||||
|
|
||||||
application = ProtocolTypeRouter({
|
application = ProtocolTypeRouter(
|
||||||
|
{
|
||||||
"websocket": AuthMiddlewareStack(
|
"websocket": AuthMiddlewareStack(
|
||||||
URLRouter([
|
URLRouter([url(r"^schedule/", ScheduleConsumer)])
|
||||||
url(r"^schedule/", ScheduleConsumer)
|
|
||||||
])
|
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -11,19 +11,17 @@ from program.schema import ProgramQuery
|
||||||
class CampNode(DjangoObjectType):
|
class CampNode(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Camp
|
model = Camp
|
||||||
interfaces = (relay.Node, )
|
interfaces = (relay.Node,)
|
||||||
filter_fields = {
|
filter_fields = {"title": ["icontains", "iexact"]}
|
||||||
'title': ['icontains', 'iexact'],
|
|
||||||
}
|
|
||||||
only_fields = (
|
only_fields = (
|
||||||
'title',
|
"title",
|
||||||
'slug',
|
"slug",
|
||||||
'tagline',
|
"tagline",
|
||||||
'shortslug',
|
"shortslug",
|
||||||
'buildup',
|
"buildup",
|
||||||
'camp',
|
"camp",
|
||||||
'teardown',
|
"teardown",
|
||||||
'colour',
|
"colour",
|
||||||
)
|
)
|
||||||
|
|
||||||
def resolve_buildup(self, info):
|
def resolve_buildup(self, info):
|
||||||
|
|
|
@ -2,6 +2,7 @@ import os
|
||||||
|
|
||||||
from .environment_settings import *
|
from .environment_settings import *
|
||||||
|
|
||||||
|
|
||||||
def local_dir(entry):
|
def local_dir(entry):
|
||||||
return os.path.join(os.path.dirname(os.path.dirname(__file__)), entry)
|
return os.path.join(os.path.dirname(os.path.dirname(__file__)), entry)
|
||||||
|
|
||||||
|
|
|
@ -19,206 +19,115 @@ from villages.views import *
|
||||||
admin.site.login = login_required(admin.site.login)
|
admin.site.login = login_required(admin.site.login)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('profile/', include('allauth.urls')),
|
path("profile/", include("allauth.urls")),
|
||||||
path('profile/', include('allauth_2fa.urls')),
|
path("profile/", include("allauth_2fa.urls")),
|
||||||
path('profile/', include('profiles.urls', namespace='profiles')),
|
path("profile/", include("profiles.urls", namespace="profiles")),
|
||||||
|
path("tickets/", include("tickets.urls", namespace="tickets")),
|
||||||
|
path("shop/", include("shop.urls", namespace="shop")),
|
||||||
|
path("news/", include("news.urls", namespace="news")),
|
||||||
path(
|
path(
|
||||||
'tickets/',
|
"contact/", TemplateView.as_view(template_name="contact.html"), name="contact"
|
||||||
include('tickets.urls', namespace='tickets')
|
),
|
||||||
|
path("conduct/", TemplateView.as_view(template_name="coc.html"), name="conduct"),
|
||||||
|
path("login/", LoginView.as_view(), name="account_login"),
|
||||||
|
path("logout/", LogoutView.as_view(), name="account_logout"),
|
||||||
|
path(
|
||||||
|
"privacy-policy/",
|
||||||
|
TemplateView.as_view(template_name="legal/privacy_policy.html"),
|
||||||
|
name="privacy-policy",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'shop/',
|
"general-terms-and-conditions/",
|
||||||
include('shop.urls', namespace='shop')
|
TemplateView.as_view(template_name="legal/general_terms_and_conditions.html"),
|
||||||
|
name="general-terms",
|
||||||
),
|
),
|
||||||
path(
|
path("admin/", admin.site.urls),
|
||||||
'news/',
|
|
||||||
include('news.urls', namespace='news')
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'contact/',
|
|
||||||
TemplateView.as_view(template_name='contact.html'),
|
|
||||||
name='contact'
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'conduct/',
|
|
||||||
TemplateView.as_view(template_name='coc.html'),
|
|
||||||
name='conduct'
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'login/',
|
|
||||||
LoginView.as_view(),
|
|
||||||
name='account_login',
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'logout/',
|
|
||||||
LogoutView.as_view(),
|
|
||||||
name='account_logout',
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'privacy-policy/',
|
|
||||||
TemplateView.as_view(template_name='legal/privacy_policy.html'),
|
|
||||||
name='privacy-policy'
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'general-terms-and-conditions/',
|
|
||||||
TemplateView.as_view(template_name='legal/general_terms_and_conditions.html'),
|
|
||||||
name='general-terms'
|
|
||||||
),
|
|
||||||
path('admin/', admin.site.urls),
|
|
||||||
|
|
||||||
# We don't need CSRF checks for the API
|
# We don't need CSRF checks for the API
|
||||||
path('api/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
|
path("api/", csrf_exempt(GraphQLView.as_view(graphiql=True))),
|
||||||
|
path("camps/", CampListView.as_view(), name="camp_list"),
|
||||||
path(
|
path("token/", include("tokens.urls", namespace="tokens")),
|
||||||
'camps/',
|
|
||||||
CampListView.as_view(),
|
|
||||||
name='camp_list'
|
|
||||||
),
|
|
||||||
|
|
||||||
path(
|
|
||||||
'token/',
|
|
||||||
include('tokens.urls', namespace='tokens'),
|
|
||||||
),
|
|
||||||
|
|
||||||
# camp redirect views here
|
# camp redirect views here
|
||||||
|
|
||||||
path(
|
path(
|
||||||
'',
|
"",
|
||||||
CampRedirectView.as_view(),
|
CampRedirectView.as_view(),
|
||||||
kwargs={'page': 'camp_detail'},
|
kwargs={"page": "camp_detail"},
|
||||||
name='camp_detail_redirect',
|
name="camp_detail_redirect",
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
'program/',
|
"program/",
|
||||||
CampRedirectView.as_view(),
|
CampRedirectView.as_view(),
|
||||||
kwargs={'page': 'schedule_index'},
|
kwargs={"page": "schedule_index"},
|
||||||
name='schedule_index_redirect',
|
name="schedule_index_redirect",
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
'info/',
|
"info/",
|
||||||
CampRedirectView.as_view(),
|
CampRedirectView.as_view(),
|
||||||
kwargs={'page': 'info'},
|
kwargs={"page": "info"},
|
||||||
name='info_redirect',
|
name="info_redirect",
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
'sponsors/',
|
"sponsors/",
|
||||||
CampRedirectView.as_view(),
|
CampRedirectView.as_view(),
|
||||||
kwargs={'page': 'sponsors'},
|
kwargs={"page": "sponsors"},
|
||||||
name='sponsors_redirect',
|
name="sponsors_redirect",
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
'villages/',
|
"villages/",
|
||||||
CampRedirectView.as_view(),
|
CampRedirectView.as_view(),
|
||||||
kwargs={'page': 'village_list'},
|
kwargs={"page": "village_list"},
|
||||||
name='village_list_redirect',
|
name="village_list_redirect",
|
||||||
),
|
),
|
||||||
|
path("people/", PeopleView.as_view(), name="people"),
|
||||||
path(
|
|
||||||
'people/',
|
|
||||||
PeopleView.as_view(),
|
|
||||||
name='people',
|
|
||||||
),
|
|
||||||
|
|
||||||
# camp specific urls below here
|
# camp specific urls below here
|
||||||
|
|
||||||
path(
|
path(
|
||||||
'<slug:camp_slug>/', include([
|
"<slug:camp_slug>/",
|
||||||
|
include(
|
||||||
|
[
|
||||||
|
path("", CampDetailView.as_view(), name="camp_detail"),
|
||||||
|
path("info/", CampInfoView.as_view(), name="info"),
|
||||||
|
path("program/", include("program.urls", namespace="program")),
|
||||||
|
path("sponsors/", SponsorsView.as_view(), name="sponsors"),
|
||||||
|
path("bar/menu/", MenuView.as_view(), name="menu"),
|
||||||
path(
|
path(
|
||||||
'',
|
"villages/",
|
||||||
CampDetailView.as_view(),
|
include(
|
||||||
name='camp_detail'
|
[
|
||||||
),
|
path("", VillageListView.as_view(), name="village_list"),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
'info/',
|
"create/",
|
||||||
CampInfoView.as_view(),
|
|
||||||
name='info'
|
|
||||||
),
|
|
||||||
|
|
||||||
path(
|
|
||||||
'program/',
|
|
||||||
include('program.urls', namespace='program'),
|
|
||||||
),
|
|
||||||
|
|
||||||
path(
|
|
||||||
'sponsors/',
|
|
||||||
SponsorsView.as_view(),
|
|
||||||
name='sponsors'
|
|
||||||
),
|
|
||||||
|
|
||||||
path(
|
|
||||||
'bar/menu/',
|
|
||||||
MenuView.as_view(),
|
|
||||||
name='menu'
|
|
||||||
),
|
|
||||||
|
|
||||||
path(
|
|
||||||
'villages/', include([
|
|
||||||
path(
|
|
||||||
'',
|
|
||||||
VillageListView.as_view(),
|
|
||||||
name='village_list'
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'create/',
|
|
||||||
VillageCreateView.as_view(),
|
VillageCreateView.as_view(),
|
||||||
name='village_create'
|
name="village_create",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'<slug:slug>/delete/',
|
"<slug:slug>/delete/",
|
||||||
VillageDeleteView.as_view(),
|
VillageDeleteView.as_view(),
|
||||||
name='village_delete'
|
name="village_delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'<slug:slug>/edit/',
|
"<slug:slug>/edit/",
|
||||||
VillageUpdateView.as_view(),
|
VillageUpdateView.as_view(),
|
||||||
name='village_update'
|
name="village_update",
|
||||||
),
|
),
|
||||||
# this has to be the last url in the list
|
# this has to be the last url in the list
|
||||||
path(
|
path(
|
||||||
'<slug:slug>/',
|
"<slug:slug>/",
|
||||||
VillageDetailView.as_view(),
|
VillageDetailView.as_view(),
|
||||||
name='village_detail'
|
name="village_detail",
|
||||||
),
|
),
|
||||||
])
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
|
||||||
'teams/',
|
|
||||||
include('teams.urls', namespace='teams')
|
|
||||||
),
|
),
|
||||||
|
path("teams/", include("teams.urls", namespace="teams")),
|
||||||
path(
|
path("rideshare/", include("rideshare.urls", namespace="rideshare")),
|
||||||
'rideshare/',
|
path("backoffice/", include("backoffice.urls", namespace="backoffice")),
|
||||||
include('rideshare.urls', namespace='rideshare')
|
path("feedback/", FeedbackCreate.as_view(), name="feedback"),
|
||||||
|
path("economy/", include("economy.urls", namespace="economy")),
|
||||||
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
|
||||||
'backoffice/',
|
|
||||||
include('backoffice.urls', namespace='backoffice')
|
|
||||||
),
|
),
|
||||||
|
|
||||||
path(
|
|
||||||
'feedback/',
|
|
||||||
FeedbackCreate.as_view(),
|
|
||||||
name='feedback'
|
|
||||||
),
|
|
||||||
|
|
||||||
path(
|
|
||||||
'economy/',
|
|
||||||
include('economy.urls', namespace='economy'),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
urlpatterns = [
|
|
||||||
path('__debug__/', include(debug_toolbar.urls)),
|
|
||||||
] + urlpatterns
|
|
||||||
|
|
||||||
|
urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
|
||||||
|
|
|
@ -5,4 +5,3 @@ from . import models
|
||||||
@admin.register(models.Camp)
|
@admin.register(models.Camp)
|
||||||
class CampModelAdmin(admin.ModelAdmin):
|
class CampModelAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -7,19 +7,15 @@ def camp(request):
|
||||||
Return it after adding the slug to request.session along with a "camps"
|
Return it after adding the slug to request.session along with a "camps"
|
||||||
queryset containing all camps (used to build the menu and such)
|
queryset containing all camps (used to build the menu and such)
|
||||||
"""
|
"""
|
||||||
if request.resolver_match and 'camp_slug' in request.resolver_match.kwargs:
|
if request.resolver_match and "camp_slug" in request.resolver_match.kwargs:
|
||||||
try:
|
try:
|
||||||
camp = Camp.objects.get(slug=request.resolver_match.kwargs['camp_slug'])
|
camp = Camp.objects.get(slug=request.resolver_match.kwargs["camp_slug"])
|
||||||
request.session['campslug'] = camp.slug
|
request.session["campslug"] = camp.slug
|
||||||
except Camp.DoesNotExist:
|
except Camp.DoesNotExist:
|
||||||
request.session['campslug'] = None
|
request.session["campslug"] = None
|
||||||
camp = None
|
camp = None
|
||||||
else:
|
else:
|
||||||
request.session['campslug'] = None
|
request.session["campslug"] = None
|
||||||
camp = None
|
camp = None
|
||||||
|
|
||||||
return {
|
return {"camps": Camp.objects.all().order_by("-camp"), "camp": camp}
|
||||||
'camps': Camp.objects.all().order_by('-camp'),
|
|
||||||
'camp': camp
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -6,64 +6,58 @@ import os
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Creates html files needed for a camp'
|
help = "Creates html files needed for a camp"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('camp_slug', type=str)
|
parser.add_argument("camp_slug", type=str)
|
||||||
|
|
||||||
def output(self, message):
|
def output(self, message):
|
||||||
self.stdout.write('{}: {}'.format(
|
self.stdout.write(
|
||||||
timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
|
"{}: {}".format(timezone.now().strftime("%Y-%m-%d %H:%M:%S"), message)
|
||||||
message
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def local_dir(self, entry):
|
def local_dir(self, entry):
|
||||||
return os.path.join(
|
return os.path.join(settings.DJANGO_BASE_PATH, entry)
|
||||||
settings.DJANGO_BASE_PATH,
|
|
||||||
entry
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
|
||||||
# files to create, relative to DJANGO_BASE_PATH
|
# files to create, relative to DJANGO_BASE_PATH
|
||||||
files = [
|
files = ["camps/templates/{camp_slug}_camp_detail.html"]
|
||||||
'camps/templates/{camp_slug}_camp_detail.html',
|
|
||||||
]
|
|
||||||
|
|
||||||
# directories to create, relative to DJANGO_BASE_PATH
|
# directories to create, relative to DJANGO_BASE_PATH
|
||||||
dirs = [
|
dirs = ["static_src/img/{camp_slug}/logo"]
|
||||||
'static_src/img/{camp_slug}/logo'
|
|
||||||
]
|
|
||||||
|
|
||||||
camp_slug = options['camp_slug']
|
camp_slug = options["camp_slug"]
|
||||||
|
|
||||||
for _file in files:
|
for _file in files:
|
||||||
path = self.local_dir(_file.format(camp_slug=camp_slug))
|
path = self.local_dir(_file.format(camp_slug=camp_slug))
|
||||||
if os.path.isfile(_file):
|
if os.path.isfile(_file):
|
||||||
self.output('File {} exists...'.format(path))
|
self.output("File {} exists...".format(path))
|
||||||
else:
|
else:
|
||||||
self.output('Creating {}'.format(path))
|
self.output("Creating {}".format(path))
|
||||||
with open(path, mode='w', encoding='utf-8') as f:
|
with open(path, mode="w", encoding="utf-8") as f:
|
||||||
f.write(_file.format(camp_slug=camp_slug))
|
f.write(_file.format(camp_slug=camp_slug))
|
||||||
|
|
||||||
for _dir in dirs:
|
for _dir in dirs:
|
||||||
path = self.local_dir(_file.format(camp_slug=camp_slug))
|
path = self.local_dir(_file.format(camp_slug=camp_slug))
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
self.output('Path {} exists...'.format(path))
|
self.output("Path {} exists...".format(path))
|
||||||
else:
|
else:
|
||||||
self.output('Creating {}'.format(path))
|
self.output("Creating {}".format(path))
|
||||||
os.mkdir(path, mode=0o644)
|
os.mkdir(path, mode=0o644)
|
||||||
|
|
||||||
self.output('All there is left is to create:')
|
self.output("All there is left is to create:")
|
||||||
self.output(
|
self.output(
|
||||||
self.local_dir(
|
self.local_dir(
|
||||||
'static_src/img/{camp_slug}/logo/{camp_slug}-logo-large.png'.format(camp_slug=camp_slug)
|
"static_src/img/{camp_slug}/logo/{camp_slug}-logo-large.png".format(
|
||||||
|
camp_slug=camp_slug
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.output(
|
self.output(
|
||||||
self.local_dir(
|
self.local_dir(
|
||||||
'static_src/img/{camp_slug}/logo/{camp_slug}-logo-small.png'.format(camp_slug=camp_slug)
|
"static_src/img/{camp_slug}/logo/{camp_slug}-logo-small.png".format(
|
||||||
|
camp_slug=camp_slug
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -8,71 +8,196 @@ from django.conf import settings
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Camp',
|
name="Camp",
|
||||||
fields=[
|
fields=[
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, serialize=False, editable=False, primary_key=True)),
|
(
|
||||||
('name', models.CharField(max_length=255, help_text='Name of the camp, ie. Bornhack.', verbose_name='Name')),
|
"uuid",
|
||||||
('start', models.DateTimeField(help_text='When the camp starts.', unique=True, verbose_name='Start date')),
|
models.UUIDField(
|
||||||
('end', models.DateTimeField(help_text='When the camp ends.', unique=True, verbose_name='End date')),
|
default=uuid.uuid4,
|
||||||
|
serialize=False,
|
||||||
|
editable=False,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
help_text="Name of the camp, ie. Bornhack.",
|
||||||
|
verbose_name="Name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"start",
|
||||||
|
models.DateTimeField(
|
||||||
|
help_text="When the camp starts.",
|
||||||
|
unique=True,
|
||||||
|
verbose_name="Start date",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"end",
|
||||||
|
models.DateTimeField(
|
||||||
|
help_text="When the camp ends.",
|
||||||
|
unique=True,
|
||||||
|
verbose_name="End date",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"verbose_name_plural": "Camps", "verbose_name": "Camp"},
|
||||||
'verbose_name_plural': 'Camps',
|
|
||||||
'verbose_name': 'Camp',
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Day',
|
name="Day",
|
||||||
fields=[
|
fields=[
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, serialize=False, editable=False, primary_key=True)),
|
(
|
||||||
('date', models.DateField(help_text='What date?', verbose_name='Date')),
|
"uuid",
|
||||||
('camp', models.ForeignKey(on_delete=models.PROTECT, to='camps.Camp', help_text='Which camp does this day belong to.', verbose_name='Camp')),
|
models.UUIDField(
|
||||||
|
default=uuid.uuid4,
|
||||||
|
serialize=False,
|
||||||
|
editable=False,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("date", models.DateField(help_text="What date?", verbose_name="Date")),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
to="camps.Camp",
|
||||||
|
help_text="Which camp does this day belong to.",
|
||||||
|
verbose_name="Camp",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"verbose_name_plural": "Days", "verbose_name": "Day"},
|
||||||
'verbose_name_plural': 'Days',
|
|
||||||
'verbose_name': 'Day',
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Expense',
|
name="Expense",
|
||||||
fields=[
|
fields=[
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, serialize=False, editable=False, primary_key=True)),
|
(
|
||||||
('description', models.CharField(max_length=255, help_text='What this expense covers.', verbose_name='Description')),
|
"uuid",
|
||||||
('amount', models.DecimalField(max_digits=7, help_text='The amount of the expense.', verbose_name='Amount', decimal_places=2)),
|
models.UUIDField(
|
||||||
('currency', models.CharField(max_length=3, choices=[('btc', 'BTC'), ('dkk', 'DKK'), ('eur', 'EUR'), ('sek', 'SEK')], help_text='What currency the amount is in.', verbose_name='Currency')),
|
default=uuid.uuid4,
|
||||||
('camp', models.ForeignKey(on_delete=models.PROTECT, to='camps.Camp', help_text='The camp to which this expense relates to.', verbose_name='Camp')),
|
serialize=False,
|
||||||
('covered_by', models.ForeignKey(on_delete=models.PROTECT, to=settings.AUTH_USER_MODEL, blank=True, help_text='Which user, if any, covered this expense.', verbose_name='Covered by', null=True)),
|
editable=False,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
help_text="What this expense covers.",
|
||||||
|
verbose_name="Description",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"amount",
|
||||||
|
models.DecimalField(
|
||||||
|
max_digits=7,
|
||||||
|
help_text="The amount of the expense.",
|
||||||
|
verbose_name="Amount",
|
||||||
|
decimal_places=2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"currency",
|
||||||
|
models.CharField(
|
||||||
|
max_length=3,
|
||||||
|
choices=[
|
||||||
|
("btc", "BTC"),
|
||||||
|
("dkk", "DKK"),
|
||||||
|
("eur", "EUR"),
|
||||||
|
("sek", "SEK"),
|
||||||
],
|
],
|
||||||
options={
|
help_text="What currency the amount is in.",
|
||||||
'verbose_name_plural': 'Expenses',
|
verbose_name="Currency",
|
||||||
'verbose_name': 'Expense',
|
),
|
||||||
},
|
),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
to="camps.Camp",
|
||||||
|
help_text="The camp to which this expense relates to.",
|
||||||
|
verbose_name="Camp",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"covered_by",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
blank=True,
|
||||||
|
help_text="Which user, if any, covered this expense.",
|
||||||
|
verbose_name="Covered by",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={"verbose_name_plural": "Expenses", "verbose_name": "Expense"},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Signup',
|
name="Signup",
|
||||||
fields=[
|
fields=[
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, serialize=False, editable=False, primary_key=True)),
|
(
|
||||||
('cost', models.DecimalField(default=1500.0, decimal_places=2, help_text='What the user should/is willing to pay for this signup.', verbose_name='Cost', max_digits=7)),
|
"uuid",
|
||||||
('paid', models.BooleanField(help_text='Whether the user has paid.', verbose_name='Paid?', default=False)),
|
models.UUIDField(
|
||||||
('camp', models.ForeignKey(on_delete=models.PROTECT, to='camps.Camp', help_text='The camp that has been signed up for.', verbose_name='Camp')),
|
default=uuid.uuid4,
|
||||||
('user', models.ForeignKey(on_delete=models.PROTECT, to=settings.AUTH_USER_MODEL, help_text='The user that has signed up.', verbose_name='User')),
|
serialize=False,
|
||||||
|
editable=False,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"cost",
|
||||||
|
models.DecimalField(
|
||||||
|
default=1500.0,
|
||||||
|
decimal_places=2,
|
||||||
|
help_text="What the user should/is willing to pay for this signup.",
|
||||||
|
verbose_name="Cost",
|
||||||
|
max_digits=7,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"paid",
|
||||||
|
models.BooleanField(
|
||||||
|
help_text="Whether the user has paid.",
|
||||||
|
verbose_name="Paid?",
|
||||||
|
default=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
to="camps.Camp",
|
||||||
|
help_text="The camp that has been signed up for.",
|
||||||
|
verbose_name="Camp",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
help_text="The user that has signed up.",
|
||||||
|
verbose_name="User",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"verbose_name_plural": "Signups", "verbose_name": "Signup"},
|
||||||
'verbose_name_plural': 'Signups',
|
|
||||||
'verbose_name': 'Signup',
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,14 +8,18 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0001_initial")]
|
||||||
('camps', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='day',
|
model_name="day",
|
||||||
name='camp',
|
name="camp",
|
||||||
field=models.ForeignKey(help_text='Which camp does this day belong to.', on_delete=django.db.models.deletion.CASCADE, related_name='days', to='camps.Camp', verbose_name='Camp'),
|
field=models.ForeignKey(
|
||||||
|
help_text="Which camp does this day belong to.",
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="days",
|
||||||
|
to="camps.Camp",
|
||||||
|
verbose_name="Camp",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,20 +7,10 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0002_auto_20160117_1718")]
|
||||||
('camps', '0002_auto_20160117_1718'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="signup", name="camp"),
|
||||||
model_name='signup',
|
migrations.RemoveField(model_name="signup", name="user"),
|
||||||
name='camp',
|
migrations.DeleteModel(name="Signup"),
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='signup',
|
|
||||||
name='user',
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='Signup',
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,14 +7,16 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0003_auto_20160422_2019")]
|
||||||
('camps', '0003_auto_20160422_2019'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='ticket_sale_open',
|
name="ticket_sale_open",
|
||||||
field=models.BooleanField(default=False, help_text='Whether tickets are for sale or not.', verbose_name='Ticket sale open?'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Whether tickets are for sale or not.",
|
||||||
|
verbose_name="Ticket sale open?",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,18 +7,17 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0004_camp_ticket_sale_open")]
|
||||||
('camps', '0004_camp_ticket_sale_open'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="camp", name="ticket_sale_open"),
|
||||||
model_name='camp',
|
|
||||||
name='ticket_sale_open',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='shop_open',
|
name="shop_open",
|
||||||
field=models.BooleanField(default=False, help_text='Whether the shop is open or not.', verbose_name='Shop open?'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Whether the shop is open or not.",
|
||||||
|
verbose_name="Shop open?",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,13 +7,15 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0005_auto_20160510_2011")]
|
||||||
('camps', '0005_auto_20160510_2011'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='day',
|
name="day",
|
||||||
options={'ordering': ['date'], 'verbose_name': 'Day', 'verbose_name_plural': 'Days'},
|
options={
|
||||||
),
|
"ordering": ["date"],
|
||||||
|
"verbose_name": "Day",
|
||||||
|
"verbose_name_plural": "Days",
|
||||||
|
},
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,81 +13,101 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('camps', '0006_auto_20160804_1705'),
|
("camps", "0006_auto_20160804_1705"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="day", name="camp"),
|
||||||
model_name='day',
|
migrations.RemoveField(model_name="camp", name="shop_open"),
|
||||||
name='camp',
|
migrations.RemoveField(model_name="expense", name="amount"),
|
||||||
),
|
migrations.RemoveField(model_name="expense", name="camp"),
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="expense", name="covered_by"),
|
||||||
model_name='camp',
|
migrations.RemoveField(model_name="expense", name="currency"),
|
||||||
name='shop_open',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='expense',
|
|
||||||
name='amount',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='expense',
|
|
||||||
name='camp',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='expense',
|
|
||||||
name='covered_by',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='expense',
|
|
||||||
name='currency',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='slug',
|
name="slug",
|
||||||
field=models.SlugField(default='', help_text=b'The url slug to use for this camp', verbose_name=b'Url Slug'),
|
field=models.SlugField(
|
||||||
|
default="",
|
||||||
|
help_text=b"The url slug to use for this camp",
|
||||||
|
verbose_name=b"Url Slug",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='dkk_amount',
|
name="dkk_amount",
|
||||||
field=models.DecimalField(decimal_places=2, default=0, help_text=b'The DKK amount of the expense.', max_digits=7, verbose_name=b'DKK Amount'),
|
field=models.DecimalField(
|
||||||
|
decimal_places=2,
|
||||||
|
default=0,
|
||||||
|
help_text=b"The DKK amount of the expense.",
|
||||||
|
max_digits=7,
|
||||||
|
verbose_name=b"DKK Amount",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='payment_time',
|
name="payment_time",
|
||||||
field=models.DateTimeField(default=datetime.datetime(2016, 12, 12, 18, 3, 10, 378604, tzinfo=utc), help_text=b'The date and time this expense was paid.', verbose_name=b'Expense date/time'),
|
field=models.DateTimeField(
|
||||||
|
default=datetime.datetime(2016, 12, 12, 18, 3, 10, 378604, tzinfo=utc),
|
||||||
|
help_text=b"The date and time this expense was paid.",
|
||||||
|
verbose_name=b"Expense date/time",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='receipt',
|
name="receipt",
|
||||||
field=models.ImageField(default='', help_text=b'Upload a scan or image of the receipt', upload_to=b'', verbose_name=b'Image of receipt'),
|
field=models.ImageField(
|
||||||
|
default="",
|
||||||
|
help_text=b"Upload a scan or image of the receipt",
|
||||||
|
upload_to=b"",
|
||||||
|
verbose_name=b"Image of receipt",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='refund_paid',
|
name="refund_paid",
|
||||||
field=models.BooleanField(default=False, help_text=b'Has this expense been refunded to the user?', verbose_name=b'Refund paid?'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text=b"Has this expense been refunded to the user?",
|
||||||
|
verbose_name=b"Refund paid?",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='refund_user',
|
name="refund_user",
|
||||||
field=models.ForeignKey(blank=True, help_text=b'Which user, if any, covered this expense and should be refunded.', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name=b'Refund user'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text=b"Which user, if any, covered this expense and should be refunded.",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name=b"Refund user",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='end',
|
name="end",
|
||||||
field=models.DateTimeField(help_text=b'When the camp ends.', verbose_name=b'End date'),
|
field=models.DateTimeField(
|
||||||
|
help_text=b"When the camp ends.", verbose_name=b"End date"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='name',
|
name="name",
|
||||||
field=models.CharField(help_text=b'Name of the camp, ie. Bornhack 2016.', max_length=255, verbose_name=b'Name'),
|
field=models.CharField(
|
||||||
|
help_text=b"Name of the camp, ie. Bornhack 2016.",
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=b"Name",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='start',
|
name="start",
|
||||||
field=models.DateTimeField(help_text=b'When the camp starts.', verbose_name=b'Start date'),
|
field=models.DateTimeField(
|
||||||
|
help_text=b"When the camp starts.", verbose_name=b"Start date"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,12 +8,8 @@ from django.db import migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('program', '0010_auto_20161212_1809'),
|
("program", "0010_auto_20161212_1809"),
|
||||||
('camps', '0007_auto_20161212_1803'),
|
("camps", "0007_auto_20161212_1803"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [migrations.DeleteModel(name="Day")]
|
||||||
migrations.DeleteModel(
|
|
||||||
name='Day',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
|
@ -9,31 +9,31 @@ from django.utils.timezone import utc
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0008_delete_day")]
|
||||||
('camps', '0008_delete_day'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.RenameField(model_name="camp", old_name="end", new_name="camp_end"),
|
||||||
migrations.RenameField(
|
migrations.RenameField(
|
||||||
model_name='camp',
|
model_name="camp", old_name="start", new_name="camp_start"
|
||||||
old_name='end',
|
|
||||||
new_name='camp_end',
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='camp',
|
|
||||||
old_name='start',
|
|
||||||
new_name='camp_start',
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='buildup_start',
|
name="buildup_start",
|
||||||
field=models.DateTimeField(default=datetime.datetime(2016, 12, 20, 16, 45, 39, 609630, tzinfo=utc), help_text=b'When the camp buildup starts.', verbose_name=b'Buildup Start date'),
|
field=models.DateTimeField(
|
||||||
|
default=datetime.datetime(2016, 12, 20, 16, 45, 39, 609630, tzinfo=utc),
|
||||||
|
help_text=b"When the camp buildup starts.",
|
||||||
|
verbose_name=b"Buildup Start date",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='teardown_end',
|
name="teardown_end",
|
||||||
field=models.DateTimeField(default=datetime.datetime(2016, 12, 20, 16, 45, 44, 532143, tzinfo=utc), help_text=b'When the camp teardown ends.', verbose_name=b'Start date'),
|
field=models.DateTimeField(
|
||||||
|
default=datetime.datetime(2016, 12, 20, 16, 45, 44, 532143, tzinfo=utc),
|
||||||
|
help_text=b"When the camp teardown ends.",
|
||||||
|
verbose_name=b"Start date",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,25 +7,30 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0009_auto_20161220_1645")]
|
||||||
('camps', '0009_auto_20161220_1645'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="camp", name="name"),
|
||||||
model_name='camp',
|
|
||||||
name='name',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='tagline',
|
name="tagline",
|
||||||
field=models.CharField(default='', help_text=b'Tagline of the camp, ie. "Initial Commit"', max_length=255, verbose_name=b'Tagline'),
|
field=models.CharField(
|
||||||
|
default="",
|
||||||
|
help_text=b'Tagline of the camp, ie. "Initial Commit"',
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=b"Tagline",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='title',
|
name="title",
|
||||||
field=models.CharField(default='', help_text=b'Title of the camp, ie. Bornhack 2016.', max_length=255, verbose_name=b'Title'),
|
field=models.CharField(
|
||||||
|
default="",
|
||||||
|
help_text=b"Title of the camp, ie. Bornhack 2016.",
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=b"Title",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,21 +7,29 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0010_auto_20161220_1714")]
|
||||||
('camps', '0010_auto_20161220_1714'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='logo_large',
|
name="logo_large",
|
||||||
field=models.CharField(default='', help_text=b'The filename of the large logo to use on the frontpage of this camp', max_length=100, verbose_name=b'Large logo for this camp'),
|
field=models.CharField(
|
||||||
|
default="",
|
||||||
|
help_text=b"The filename of the large logo to use on the frontpage of this camp",
|
||||||
|
max_length=100,
|
||||||
|
verbose_name=b"Large logo for this camp",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='logo_small',
|
name="logo_small",
|
||||||
field=models.CharField(default='', help_text=b'The filename of the small logo to use in the top of the page for this camp', max_length=100, verbose_name=b'Small logo for this camp'),
|
field=models.CharField(
|
||||||
|
default="",
|
||||||
|
help_text=b"The filename of the small logo to use in the top of the page for this camp",
|
||||||
|
max_length=100,
|
||||||
|
verbose_name=b"Small logo for this camp",
|
||||||
|
),
|
||||||
preserve_default=False,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,17 +7,9 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0011_auto_20161228_1750")]
|
||||||
('camps', '0011_auto_20161228_1750'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="camp", name="logo_large"),
|
||||||
model_name='camp',
|
migrations.RemoveField(model_name="camp", name="logo_small"),
|
||||||
name='logo_large',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='camp',
|
|
||||||
name='logo_small',
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,40 +8,36 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0012_auto_20161228_2312")]
|
||||||
('camps', '0012_auto_20161228_2312'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="camp", name="buildup_start"),
|
||||||
model_name='camp',
|
migrations.RemoveField(model_name="camp", name="camp_end"),
|
||||||
name='buildup_start',
|
migrations.RemoveField(model_name="camp", name="camp_start"),
|
||||||
|
migrations.RemoveField(model_name="camp", name="teardown_end"),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="camp",
|
||||||
|
name="buildup",
|
||||||
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp buildup period.",
|
||||||
|
null=True,
|
||||||
|
verbose_name=b"Buildup Period",
|
||||||
),
|
),
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='camp',
|
|
||||||
name='camp_end',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='camp',
|
|
||||||
name='camp_start',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='camp',
|
|
||||||
name='teardown_end',
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='buildup',
|
name="camp",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp buildup period.', null=True, verbose_name=b'Buildup Period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp period.", null=True, verbose_name=b"Camp Period"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='camp',
|
name="teardown",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp period.', null=True, verbose_name=b'Camp Period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp teardown period.",
|
||||||
|
null=True,
|
||||||
|
verbose_name=b"Teardown period",
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
|
||||||
model_name='camp',
|
|
||||||
name='teardown',
|
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp teardown period.', null=True, verbose_name=b'Teardown period'),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,24 +9,34 @@ from django.utils import timezone
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0013_auto_20161229_2201")]
|
||||||
('camps', '0013_auto_20161229_2201'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='buildup',
|
name="buildup",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp buildup period.', verbose_name=b'Buildup Period', default=(timezone.now(),None)),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp buildup period.",
|
||||||
|
verbose_name=b"Buildup Period",
|
||||||
|
default=(timezone.now(), None),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='camp',
|
name="camp",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp period.', verbose_name=b'Camp Period', default=(timezone.now(),None)),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp period.",
|
||||||
|
verbose_name=b"Camp Period",
|
||||||
|
default=(timezone.now(), None),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='teardown',
|
name="teardown",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp teardown period.', verbose_name=b'Teardown period', default=(timezone.now(),None)),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp teardown period.",
|
||||||
|
verbose_name=b"Teardown period",
|
||||||
|
default=(timezone.now(), None),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,16 +7,9 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0014_auto_20161229_2202")]
|
||||||
('camps', '0014_auto_20161229_2202'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(model_name="expense", name="refund_user"),
|
||||||
model_name='expense',
|
migrations.DeleteModel(name="Expense"),
|
||||||
name='refund_user',
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='Expense',
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,14 +7,16 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0015_auto_20170116_1634")]
|
||||||
('camps', '0015_auto_20170116_1634'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='description',
|
name="description",
|
||||||
field=models.TextField(default=b'', help_text=b'Description of the camp, shown on the camp frontpage. HTML and markdown supported.', verbose_name=b'Description'),
|
field=models.TextField(
|
||||||
|
default=b"",
|
||||||
|
help_text=b"Description of the camp, shown on the camp frontpage. HTML and markdown supported.",
|
||||||
|
verbose_name=b"Description",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,13 +7,6 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0016_camp_description")]
|
||||||
('camps', '0016_camp_description'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [migrations.RemoveField(model_name="camp", name="description")]
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='camp',
|
|
||||||
name='description',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
|
@ -8,24 +8,28 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0017_remove_camp_description")]
|
||||||
('camps', '0017_remove_camp_description'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='buildup',
|
name="buildup",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp buildup period.', verbose_name=b'Buildup Period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp buildup period.", verbose_name=b"Buildup Period"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='camp',
|
name="camp",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp period.', verbose_name=b'Camp Period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp period.", verbose_name=b"Camp Period"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='teardown',
|
name="teardown",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp teardown period.', verbose_name=b'Teardown period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text=b"The camp teardown period.", verbose_name=b"Teardown period"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,39 +8,53 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0018_auto_20170128_1841")]
|
||||||
('camps', '0018_auto_20170128_1841'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='buildup',
|
name="buildup",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='The camp buildup period.', verbose_name='Buildup Period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text="The camp buildup period.", verbose_name="Buildup Period"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='camp',
|
name="camp",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='The camp period.', verbose_name='Camp Period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text="The camp period.", verbose_name="Camp Period"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='slug',
|
name="slug",
|
||||||
field=models.SlugField(help_text='The url slug to use for this camp', verbose_name='Url Slug'),
|
field=models.SlugField(
|
||||||
|
help_text="The url slug to use for this camp", verbose_name="Url Slug"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='tagline',
|
name="tagline",
|
||||||
field=models.CharField(help_text='Tagline of the camp, ie. "Initial Commit"', max_length=255, verbose_name='Tagline'),
|
field=models.CharField(
|
||||||
|
help_text='Tagline of the camp, ie. "Initial Commit"',
|
||||||
|
max_length=255,
|
||||||
|
verbose_name="Tagline",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='teardown',
|
name="teardown",
|
||||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='The camp teardown period.', verbose_name='Teardown period'),
|
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||||
|
help_text="The camp teardown period.", verbose_name="Teardown period"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='title',
|
name="title",
|
||||||
field=models.CharField(help_text='Title of the camp, ie. Bornhack 2016.', max_length=255, verbose_name='Title'),
|
field=models.CharField(
|
||||||
|
help_text="Title of the camp, ie. Bornhack 2016.",
|
||||||
|
max_length=255,
|
||||||
|
verbose_name="Title",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,14 +7,15 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0019_auto_20170131_1849")]
|
||||||
('camps', '0019_auto_20170131_1849'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='read_only',
|
name="read_only",
|
||||||
field=models.BooleanField(default=False, help_text='Whether the camp is read only (i.e. in the past)'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Whether the camp is read only (i.e. in the past)",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,13 +7,15 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0020_camp_read_only")]
|
||||||
('camps', '0020_camp_read_only'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='camp',
|
name="camp",
|
||||||
options={'ordering': ['-title'], 'verbose_name': 'Camp', 'verbose_name_plural': 'Camps'},
|
options={
|
||||||
),
|
"ordering": ["-title"],
|
||||||
|
"verbose_name": "Camp",
|
||||||
|
"verbose_name_plural": "Camps",
|
||||||
|
},
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,15 +7,18 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0021_auto_20170711_2247")]
|
||||||
('camps', '0021_auto_20170711_2247'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='colour',
|
name="colour",
|
||||||
field=models.CharField(default='#000000', help_text='The primary colour for the camp in hex', max_length=7, verbose_name='Colour'),
|
field=models.CharField(
|
||||||
preserve_default=False,
|
default="#000000",
|
||||||
|
help_text="The primary colour for the camp in hex",
|
||||||
|
max_length=7,
|
||||||
|
verbose_name="Colour",
|
||||||
),
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,14 +7,16 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0022_camp_colour")]
|
||||||
('camps', '0022_camp_colour'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='shortslug',
|
name="shortslug",
|
||||||
field=models.SlugField(blank=True, help_text='Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.', verbose_name='Short Slug'),
|
field=models.SlugField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.",
|
||||||
|
verbose_name="Short Slug",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,19 +4,16 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def populate_camp_shortslugs(apps, schema_editor):
|
def populate_camp_shortslugs(apps, schema_editor):
|
||||||
Camp = apps.get_model('camps', 'Camp')
|
Camp = apps.get_model("camps", "Camp")
|
||||||
for camp in Camp.objects.all():
|
for camp in Camp.objects.all():
|
||||||
if not camp.shortslug:
|
if not camp.shortslug:
|
||||||
camp.shortslug = camp.slug
|
camp.shortslug = camp.slug
|
||||||
camp.save()
|
camp.save()
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [("camps", "0023_camp_shortslug")]
|
||||||
('camps', '0023_camp_shortslug'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(populate_camp_shortslugs),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
operations = [migrations.RunPython(populate_camp_shortslugs)]
|
||||||
|
|
|
@ -7,14 +7,15 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0024_populate_camp_shortslugs")]
|
||||||
('camps', '0024_populate_camp_shortslugs'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='shortslug',
|
name="shortslug",
|
||||||
field=models.SlugField(help_text='Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.', verbose_name='Short Slug'),
|
field=models.SlugField(
|
||||||
|
help_text="Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.",
|
||||||
|
verbose_name="Short Slug",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,19 +5,23 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0025_auto_20180318_1250")]
|
||||||
('camps', '0025_auto_20180318_1250'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='call_for_participation_open',
|
name="call_for_participation_open",
|
||||||
field=models.BooleanField(default=False, help_text='Check if the Call for Participation is open for this camp'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Check if the Call for Participation is open for this camp",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='call_for_sponsors_open',
|
name="call_for_sponsors_open",
|
||||||
field=models.BooleanField(default=False, help_text='Check if the Call for Sponsors is open for this camp'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Check if the Call for Sponsors is open for this camp",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,19 +5,21 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0026_auto_20180506_1633")]
|
||||||
('camps', '0026_auto_20180506_1633'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='call_for_participation',
|
name="call_for_participation",
|
||||||
field=models.TextField(blank=True, help_text='The CFP markdown for this Camp'),
|
field=models.TextField(
|
||||||
|
blank=True, help_text="The CFP markdown for this Camp"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='call_for_sponsors',
|
name="call_for_sponsors",
|
||||||
field=models.TextField(blank=True, help_text='The CFS markdown for this Camp'),
|
field=models.TextField(
|
||||||
|
blank=True, help_text="The CFS markdown for this Camp"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,19 +5,25 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0027_auto_20180525_1019")]
|
||||||
('camps', '0027_auto_20180525_1019'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='call_for_participation',
|
name="call_for_participation",
|
||||||
field=models.TextField(blank=True, default='The Call For Participation for this Camp has not been written yet', help_text='The CFP markdown for this Camp'),
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
default="The Call For Participation for this Camp has not been written yet",
|
||||||
|
help_text="The CFP markdown for this Camp",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='call_for_sponsors',
|
name="call_for_sponsors",
|
||||||
field=models.TextField(blank=True, default='The Call For Sponsors for this Camp has not been written yet', help_text='The CFS markdown for this Camp'),
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
default="The Call For Sponsors for this Camp has not been written yet",
|
||||||
|
help_text="The CFS markdown for this Camp",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,13 +5,16 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0028_auto_20180525_1025")]
|
||||||
('camps', '0028_auto_20180525_1025'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='camp',
|
name="camp",
|
||||||
options={'ordering': ['-title'], 'permissions': (('infodesk_permission', 'Infodesk permission'),), 'verbose_name': 'Camp', 'verbose_name_plural': 'Camps'},
|
options={
|
||||||
),
|
"ordering": ["-title"],
|
||||||
|
"permissions": (("infodesk_permission", "Infodesk permission"),),
|
||||||
|
"verbose_name": "Camp",
|
||||||
|
"verbose_name_plural": "Camps",
|
||||||
|
},
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,14 +5,15 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0029_auto_20180815_2018")]
|
||||||
('camps', '0029_auto_20180815_2018'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='camp',
|
model_name="camp",
|
||||||
name='light_text',
|
name="light_text",
|
||||||
field=models.BooleanField(default=True, help_text='Check if this camps colour requires white text, uncheck if black text is better'),
|
field=models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
help_text="Check if this camps colour requires white text, uncheck if black text is better",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,24 +5,41 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0030_camp_light_text")]
|
||||||
('camps', '0030_camp_light_text'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Permission',
|
name="Permission",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'permissions': (('backoffice_permission', 'BackOffice access'), ('orgateam_permission', 'Orga Team permissions set'), ('infoteam_permission', 'Info Team permissions set'), ('economyteam_permission', 'Economy Team permissions set'), ('contentteam_permission', 'Content Team permissions set'), ('expense_create_permission', 'Expense Create permission')),
|
"permissions": (
|
||||||
'default_permissions': (),
|
("backoffice_permission", "BackOffice access"),
|
||||||
'managed': False,
|
("orgateam_permission", "Orga Team permissions set"),
|
||||||
|
("infoteam_permission", "Info Team permissions set"),
|
||||||
|
("economyteam_permission", "Economy Team permissions set"),
|
||||||
|
("contentteam_permission", "Content Team permissions set"),
|
||||||
|
("expense_create_permission", "Expense Create permission"),
|
||||||
|
),
|
||||||
|
"default_permissions": (),
|
||||||
|
"managed": False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='camp',
|
name="camp",
|
||||||
options={'ordering': ['-title'], 'verbose_name': 'Camp', 'verbose_name_plural': 'Camps'},
|
options={
|
||||||
|
"ordering": ["-title"],
|
||||||
|
"verbose_name": "Camp",
|
||||||
|
"verbose_name_plural": "Camps",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,13 +5,23 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0031_auto_20180830_0014")]
|
||||||
('camps', '0031_auto_20180830_0014'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='permission',
|
name="permission",
|
||||||
options={'default_permissions': (), 'managed': False, 'permissions': (('backoffice_permission', 'BackOffice access'), ('orgateam_permission', 'Orga Team permissions set'), ('infoteam_permission', 'Info Team permissions set'), ('economyteam_permission', 'Economy Team permissions set'), ('contentteam_permission', 'Content Team permissions set'), ('expense_create_permission', 'Expense Create permission'), ('revenue_create_permission', 'Revenue Create permission'))},
|
options={
|
||||||
|
"default_permissions": (),
|
||||||
|
"managed": False,
|
||||||
|
"permissions": (
|
||||||
|
("backoffice_permission", "BackOffice access"),
|
||||||
|
("orgateam_permission", "Orga Team permissions set"),
|
||||||
|
("infoteam_permission", "Info Team permissions set"),
|
||||||
|
("economyteam_permission", "Economy Team permissions set"),
|
||||||
|
("contentteam_permission", "Content Team permissions set"),
|
||||||
|
("expense_create_permission", "Expense Create permission"),
|
||||||
|
("revenue_create_permission", "Revenue Create permission"),
|
||||||
),
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,7 +21,7 @@ class CampViewMixin(object):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
# do we have a camp_filter on this model
|
# do we have a camp_filter on this model
|
||||||
if not hasattr(self.model, 'camp_filter'):
|
if not hasattr(self.model, "camp_filter"):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
# get the camp_filter from the model
|
# get the camp_filter from the model
|
||||||
|
@ -36,14 +36,14 @@ class CampViewMixin(object):
|
||||||
filter_dict = {_filter: self.camp}
|
filter_dict = {_filter: self.camp}
|
||||||
|
|
||||||
# get pk from kwargs if we have it
|
# get pk from kwargs if we have it
|
||||||
if hasattr(self, 'pk_url_kwarg'):
|
if hasattr(self, "pk_url_kwarg"):
|
||||||
pk = self.kwargs.get(self.pk_url_kwarg)
|
pk = self.kwargs.get(self.pk_url_kwarg)
|
||||||
if pk is not None:
|
if pk is not None:
|
||||||
# We should also filter for the pk of the object
|
# We should also filter for the pk of the object
|
||||||
filter_dict['pk'] = pk
|
filter_dict["pk"] = pk
|
||||||
|
|
||||||
# get slug from kwargs if we have it
|
# get slug from kwargs if we have it
|
||||||
if hasattr(self, 'slug_url_kwarg'):
|
if hasattr(self, "slug_url_kwarg"):
|
||||||
slug = self.kwargs.get(self.slug_url_kwarg)
|
slug = self.kwargs.get(self.slug_url_kwarg)
|
||||||
if slug is not None and (pk is None or self.query_pk_and_slug):
|
if slug is not None and (pk is None or self.query_pk_and_slug):
|
||||||
# we should also filter for the slug of the object
|
# we should also filter for the slug of the object
|
||||||
|
@ -57,4 +57,3 @@ class CampViewMixin(object):
|
||||||
|
|
||||||
# no camp_filter returned any results, return an empty queryset
|
# no camp_filter returned any results, return an empty queryset
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ from datetime import timedelta
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,9 +16,10 @@ class Permission(models.Model):
|
||||||
"""
|
"""
|
||||||
An unmanaged field-less model which holds our non-model permissions (such as team permission sets)
|
An unmanaged field-less model which holds our non-model permissions (such as team permission sets)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
managed = False
|
managed = False
|
||||||
default_permissions=()
|
default_permissions = ()
|
||||||
permissions = (
|
permissions = (
|
||||||
("backoffice_permission", "BackOffice access"),
|
("backoffice_permission", "BackOffice access"),
|
||||||
("orgateam_permission", "Orga Team permissions set"),
|
("orgateam_permission", "Orga Team permissions set"),
|
||||||
|
@ -31,102 +33,94 @@ class Permission(models.Model):
|
||||||
|
|
||||||
class Camp(CreatedUpdatedModel, UUIDModel):
|
class Camp(CreatedUpdatedModel, UUIDModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Camp'
|
verbose_name = "Camp"
|
||||||
verbose_name_plural = 'Camps'
|
verbose_name_plural = "Camps"
|
||||||
ordering = ['-title']
|
ordering = ["-title"]
|
||||||
|
|
||||||
title = models.CharField(
|
title = models.CharField(
|
||||||
verbose_name='Title',
|
verbose_name="Title",
|
||||||
help_text='Title of the camp, ie. Bornhack 2016.',
|
help_text="Title of the camp, ie. Bornhack 2016.",
|
||||||
max_length=255,
|
max_length=255,
|
||||||
)
|
)
|
||||||
|
|
||||||
tagline = models.CharField(
|
tagline = models.CharField(
|
||||||
verbose_name='Tagline',
|
verbose_name="Tagline",
|
||||||
help_text='Tagline of the camp, ie. "Initial Commit"',
|
help_text='Tagline of the camp, ie. "Initial Commit"',
|
||||||
max_length=255,
|
max_length=255,
|
||||||
)
|
)
|
||||||
|
|
||||||
slug = models.SlugField(
|
slug = models.SlugField(
|
||||||
verbose_name='Url Slug',
|
verbose_name="Url Slug", help_text="The url slug to use for this camp"
|
||||||
help_text='The url slug to use for this camp'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
shortslug = models.SlugField(
|
shortslug = models.SlugField(
|
||||||
verbose_name='Short Slug',
|
verbose_name="Short Slug",
|
||||||
help_text='Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.',
|
help_text="Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.",
|
||||||
)
|
)
|
||||||
|
|
||||||
buildup = DateTimeRangeField(
|
buildup = DateTimeRangeField(
|
||||||
verbose_name='Buildup Period',
|
verbose_name="Buildup Period", help_text="The camp buildup period."
|
||||||
help_text='The camp buildup period.',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
camp = DateTimeRangeField(
|
camp = DateTimeRangeField(verbose_name="Camp Period", help_text="The camp period.")
|
||||||
verbose_name='Camp Period',
|
|
||||||
help_text='The camp period.',
|
|
||||||
)
|
|
||||||
|
|
||||||
teardown = DateTimeRangeField(
|
teardown = DateTimeRangeField(
|
||||||
verbose_name='Teardown period',
|
verbose_name="Teardown period", help_text="The camp teardown period."
|
||||||
help_text='The camp teardown period.',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
read_only = models.BooleanField(
|
read_only = models.BooleanField(
|
||||||
help_text='Whether the camp is read only (i.e. in the past)',
|
help_text="Whether the camp is read only (i.e. in the past)", default=False
|
||||||
default=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
colour = models.CharField(
|
colour = models.CharField(
|
||||||
verbose_name='Colour',
|
verbose_name="Colour",
|
||||||
help_text='The primary colour for the camp in hex',
|
help_text="The primary colour for the camp in hex",
|
||||||
max_length=7
|
max_length=7,
|
||||||
)
|
)
|
||||||
|
|
||||||
light_text = models.BooleanField(
|
light_text = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
help_text='Check if this camps colour requires white text, uncheck if black text is better',
|
help_text="Check if this camps colour requires white text, uncheck if black text is better",
|
||||||
)
|
)
|
||||||
|
|
||||||
call_for_participation_open = models.BooleanField(
|
call_for_participation_open = models.BooleanField(
|
||||||
help_text='Check if the Call for Participation is open for this camp',
|
help_text="Check if the Call for Participation is open for this camp",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
call_for_participation = models.TextField(
|
call_for_participation = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='The CFP markdown for this Camp',
|
help_text="The CFP markdown for this Camp",
|
||||||
default='The Call For Participation for this Camp has not been written yet',
|
default="The Call For Participation for this Camp has not been written yet",
|
||||||
)
|
)
|
||||||
|
|
||||||
call_for_sponsors_open = models.BooleanField(
|
call_for_sponsors_open = models.BooleanField(
|
||||||
help_text='Check if the Call for Sponsors is open for this camp',
|
help_text="Check if the Call for Sponsors is open for this camp", default=False
|
||||||
default=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
call_for_sponsors = models.TextField(
|
call_for_sponsors = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='The CFS markdown for this Camp',
|
help_text="The CFS markdown for this Camp",
|
||||||
default='The Call For Sponsors for this Camp has not been written yet',
|
default="The Call For Sponsors for this Camp has not been written yet",
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('camp_detail', kwargs={'camp_slug': self.slug})
|
return reverse("camp_detail", kwargs={"camp_slug": self.slug})
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
''' Make sure the dates make sense - meaning no overlaps and buildup before camp before teardown '''
|
""" Make sure the dates make sense - meaning no overlaps and buildup before camp before teardown """
|
||||||
errors = []
|
errors = []
|
||||||
# check for overlaps buildup vs. camp
|
# check for overlaps buildup vs. camp
|
||||||
if self.buildup.upper > self.camp.lower:
|
if self.buildup.upper > self.camp.lower:
|
||||||
msg = "End of buildup must not be after camp start"
|
msg = "End of buildup must not be after camp start"
|
||||||
errors.append(ValidationError({'buildup', msg}))
|
errors.append(ValidationError({"buildup", msg}))
|
||||||
errors.append(ValidationError({'camp', msg}))
|
errors.append(ValidationError({"camp", msg}))
|
||||||
|
|
||||||
# check for overlaps camp vs. teardown
|
# check for overlaps camp vs. teardown
|
||||||
if self.camp.upper > self.teardown.lower:
|
if self.camp.upper > self.teardown.lower:
|
||||||
msg = "End of camp must not be after teardown start"
|
msg = "End of camp must not be after teardown start"
|
||||||
errors.append(ValidationError({'camp', msg}))
|
errors.append(ValidationError({"camp", msg}))
|
||||||
errors.append(ValidationError({'teardown', msg}))
|
errors.append(ValidationError({"teardown", msg}))
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
@ -137,40 +131,48 @@ class Camp(CreatedUpdatedModel, UUIDModel):
|
||||||
@property
|
@property
|
||||||
def event_types(self):
|
def event_types(self):
|
||||||
""" Return all event types with at least one event in this camp """
|
""" Return all event types with at least one event in this camp """
|
||||||
return EventType.objects.filter(event__instances__isnull=False, event__camp=self).distinct()
|
return EventType.objects.filter(
|
||||||
|
event__instances__isnull=False, event__camp=self
|
||||||
|
).distinct()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def event_locations(self):
|
def event_locations(self):
|
||||||
''' Return all event locations with at least one event in this camp'''
|
""" Return all event locations with at least one event in this camp"""
|
||||||
return EventLocation.objects.filter(eventinstances__isnull=False, camp=self).distinct()
|
return EventLocation.objects.filter(
|
||||||
|
eventinstances__isnull=False, camp=self
|
||||||
|
).distinct()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def logo_small(self):
|
def logo_small(self):
|
||||||
return 'img/%(slug)s/logo/%(slug)s-logo-s.png' % {'slug': self.slug}
|
return "img/%(slug)s/logo/%(slug)s-logo-s.png" % {"slug": self.slug}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def logo_small_svg(self):
|
def logo_small_svg(self):
|
||||||
return 'img/%(slug)s/logo/%(slug)s-logo-small.svg' % {'slug': self.slug}
|
return "img/%(slug)s/logo/%(slug)s-logo-small.svg" % {"slug": self.slug}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def logo_large(self):
|
def logo_large(self):
|
||||||
return 'img/%(slug)s/logo/%(slug)s-logo-l.png' % {'slug': self.slug}
|
return "img/%(slug)s/logo/%(slug)s-logo-l.png" % {"slug": self.slug}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def logo_large_svg(self):
|
def logo_large_svg(self):
|
||||||
return 'img/%(slug)s/logo/%(slug)s-logo-large.svg' % {'slug': self.slug}
|
return "img/%(slug)s/logo/%(slug)s-logo-large.svg" % {"slug": self.slug}
|
||||||
|
|
||||||
def get_days(self, camppart):
|
def get_days(self, camppart):
|
||||||
'''
|
"""
|
||||||
Returns a list of DateTimeTZRanges representing the days during the specified part of the camp.
|
Returns a list of DateTimeTZRanges representing the days during the specified part of the camp.
|
||||||
'''
|
"""
|
||||||
if not hasattr(self, camppart):
|
if not hasattr(self, camppart):
|
||||||
logger.error("nonexistant field/attribute")
|
logger.error("nonexistant field/attribute")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
field = getattr(self, camppart)
|
field = getattr(self, camppart)
|
||||||
|
|
||||||
if not hasattr(field, '__class__') or not hasattr(field.__class__, '__name__') or not field.__class__.__name__ == 'DateTimeTZRange':
|
if (
|
||||||
|
not hasattr(field, "__class__")
|
||||||
|
or not hasattr(field.__class__, "__name__")
|
||||||
|
or not field.__class__.__name__ == "DateTimeTZRange"
|
||||||
|
):
|
||||||
logger.error("this attribute is not a datetimetzrange field: %s" % field)
|
logger.error("this attribute is not a datetimetzrange field: %s" % field)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -182,45 +184,44 @@ class Camp(CreatedUpdatedModel, UUIDModel):
|
||||||
days.append(
|
days.append(
|
||||||
DateTimeTZRange(
|
DateTimeTZRange(
|
||||||
field.lower,
|
field.lower,
|
||||||
(field.lower+timedelta(days=i+1)).replace(hour=0)
|
(field.lower + timedelta(days=i + 1)).replace(hour=0),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif i == daycount-1:
|
elif i == daycount - 1:
|
||||||
# on the last day use actual end time instead of midnight
|
# on the last day use actual end time instead of midnight
|
||||||
days.append(
|
days.append(
|
||||||
DateTimeTZRange(
|
DateTimeTZRange(
|
||||||
(field.lower+timedelta(days=i)).replace(hour=0),
|
(field.lower + timedelta(days=i)).replace(hour=0),
|
||||||
field.lower+timedelta(days=i+1)
|
field.lower + timedelta(days=i + 1),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# neither first nor last day, goes from midnight to midnight
|
# neither first nor last day, goes from midnight to midnight
|
||||||
days.append(
|
days.append(
|
||||||
DateTimeTZRange(
|
DateTimeTZRange(
|
||||||
(field.lower+timedelta(days=i)).replace(hour=0),
|
(field.lower + timedelta(days=i)).replace(hour=0),
|
||||||
(field.lower+timedelta(days=i+1)).replace(hour=0)
|
(field.lower + timedelta(days=i + 1)).replace(hour=0),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return days
|
return days
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def buildup_days(self):
|
def buildup_days(self):
|
||||||
'''
|
"""
|
||||||
Returns a list of DateTimeTZRanges representing the days during the buildup.
|
Returns a list of DateTimeTZRanges representing the days during the buildup.
|
||||||
'''
|
"""
|
||||||
return self.get_days('buildup')
|
return self.get_days("buildup")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def camp_days(self):
|
def camp_days(self):
|
||||||
'''
|
"""
|
||||||
Returns a list of DateTimeTZRanges representing the days during the camp.
|
Returns a list of DateTimeTZRanges representing the days during the camp.
|
||||||
'''
|
"""
|
||||||
return self.get_days('camp')
|
return self.get_days("camp")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def teardown_days(self):
|
def teardown_days(self):
|
||||||
'''
|
"""
|
||||||
Returns a list of DateTimeTZRanges representing the days during the buildup.
|
Returns a list of DateTimeTZRanges representing the days during the buildup.
|
||||||
'''
|
"""
|
||||||
return self.get_days('teardown')
|
return self.get_days("teardown")
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,9 @@ class CampPropertyListFilter(admin.SimpleListFilter):
|
||||||
SimpleListFilter to filter models by camp when camp is
|
SimpleListFilter to filter models by camp when camp is
|
||||||
a property and not a real model field.
|
a property and not a real model field.
|
||||||
"""
|
"""
|
||||||
title = 'Camp'
|
|
||||||
parameter_name = 'camp'
|
title = "Camp"
|
||||||
|
parameter_name = "camp"
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
# get the current queryset
|
# get the current queryset
|
||||||
|
|
|
@ -6,36 +6,37 @@ from .mixins import CampViewMixin
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
class CampRedirectView(CampViewMixin, View):
|
class CampRedirectView(CampViewMixin, View):
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
camp = Camp.objects.get(
|
camp = Camp.objects.get(camp__contains=now)
|
||||||
camp__contains=now
|
logger.debug(
|
||||||
|
"Redirecting to camp '%s' for page '%s' because it is now!"
|
||||||
|
% (camp.slug, kwargs["page"])
|
||||||
)
|
)
|
||||||
logger.debug("Redirecting to camp '%s' for page '%s' because it is now!" % (camp.slug, kwargs['page']))
|
return redirect(kwargs["page"], camp_slug=camp.slug)
|
||||||
return redirect(kwargs['page'], camp_slug=camp.slug)
|
|
||||||
except Camp.DoesNotExist:
|
except Camp.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# no ongoing camp, find the closest camp in the past
|
# no ongoing camp, find the closest camp in the past
|
||||||
try:
|
try:
|
||||||
prevcamp = Camp.objects.filter(
|
prevcamp = (
|
||||||
camp__endswith__lt=now
|
Camp.objects.filter(camp__endswith__lt=now).order_by("-camp").first()
|
||||||
).order_by('-camp').first()
|
)
|
||||||
except Camp.DoesNotExist:
|
except Camp.DoesNotExist:
|
||||||
prevcamp = None
|
prevcamp = None
|
||||||
|
|
||||||
# find the closest upcoming camp
|
# find the closest upcoming camp
|
||||||
try:
|
try:
|
||||||
nextcamp = Camp.objects.filter(
|
nextcamp = (
|
||||||
camp__startswith__gt=now
|
Camp.objects.filter(camp__startswith__gt=now).order_by("camp").first()
|
||||||
).order_by('camp').first()
|
)
|
||||||
except Camp.DoesNotExist:
|
except Camp.DoesNotExist:
|
||||||
nextcamp = None
|
nextcamp = None
|
||||||
|
|
||||||
|
@ -59,19 +60,18 @@ class CampRedirectView(CampViewMixin, View):
|
||||||
camp = prevcamp
|
camp = prevcamp
|
||||||
|
|
||||||
# do the redirect
|
# do the redirect
|
||||||
return redirect(kwargs['page'], camp_slug=camp.slug)
|
return redirect(kwargs["page"], camp_slug=camp.slug)
|
||||||
|
|
||||||
|
|
||||||
class CampDetailView(DetailView):
|
class CampDetailView(DetailView):
|
||||||
model = Camp
|
model = Camp
|
||||||
slug_url_kwarg = 'camp_slug'
|
slug_url_kwarg = "camp_slug"
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
return '%s_camp_detail.html' % self.get_object().slug
|
return "%s_camp_detail.html" % self.get_object().slug
|
||||||
|
|
||||||
|
|
||||||
class CampListView(ListView):
|
class CampListView(ListView):
|
||||||
model = Camp
|
model = Camp
|
||||||
template_name = 'camp_list.html'
|
template_name = "camp_list.html"
|
||||||
queryset = Camp.objects.all().order_by('camp')
|
queryset = Camp.objects.all().order_by("camp")
|
||||||
|
|
||||||
|
|
|
@ -5,71 +5,108 @@ from .models import Chain, Credebtor, Expense, Reimbursement, Revenue
|
||||||
|
|
||||||
### chains and credebtors
|
### chains and credebtors
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Chain)
|
@admin.register(Chain)
|
||||||
class ChainAdmin(admin.ModelAdmin):
|
class ChainAdmin(admin.ModelAdmin):
|
||||||
list_filter = ['name']
|
list_filter = ["name"]
|
||||||
list_display = ['name', 'notes']
|
list_display = ["name", "notes"]
|
||||||
search_fields = ['name', 'notes']
|
search_fields = ["name", "notes"]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Credebtor)
|
@admin.register(Credebtor)
|
||||||
class ChainAdmin(admin.ModelAdmin):
|
class ChainAdmin(admin.ModelAdmin):
|
||||||
list_filter = ['chain', 'name']
|
list_filter = ["chain", "name"]
|
||||||
list_display = ['chain', 'name', 'notes']
|
list_display = ["chain", "name", "notes"]
|
||||||
search_fields = ['chain', 'name', 'notes']
|
search_fields = ["chain", "name", "notes"]
|
||||||
|
|
||||||
|
|
||||||
### expenses
|
### expenses
|
||||||
|
|
||||||
|
|
||||||
def approve_expenses(modeladmin, request, queryset):
|
def approve_expenses(modeladmin, request, queryset):
|
||||||
for expense in queryset.all():
|
for expense in queryset.all():
|
||||||
expense.approve(request)
|
expense.approve(request)
|
||||||
|
|
||||||
|
|
||||||
approve_expenses.short_description = "Approve Expenses"
|
approve_expenses.short_description = "Approve Expenses"
|
||||||
|
|
||||||
|
|
||||||
def reject_expenses(modeladmin, request, queryset):
|
def reject_expenses(modeladmin, request, queryset):
|
||||||
for expense in queryset.all():
|
for expense in queryset.all():
|
||||||
expense.reject(request)
|
expense.reject(request)
|
||||||
|
|
||||||
|
|
||||||
reject_expenses.short_description = "Reject Expenses"
|
reject_expenses.short_description = "Reject Expenses"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Expense)
|
@admin.register(Expense)
|
||||||
class ExpenseAdmin(admin.ModelAdmin):
|
class ExpenseAdmin(admin.ModelAdmin):
|
||||||
list_filter = ['camp', 'creditor__chain', 'creditor', 'responsible_team', 'approved', 'user']
|
list_filter = [
|
||||||
list_display = ['user', 'description', 'invoice_date', 'amount', 'camp', 'creditor', 'responsible_team', 'approved', 'reimbursement']
|
"camp",
|
||||||
search_fields = ['description', 'amount', 'uuid']
|
"creditor__chain",
|
||||||
|
"creditor",
|
||||||
|
"responsible_team",
|
||||||
|
"approved",
|
||||||
|
"user",
|
||||||
|
]
|
||||||
|
list_display = [
|
||||||
|
"user",
|
||||||
|
"description",
|
||||||
|
"invoice_date",
|
||||||
|
"amount",
|
||||||
|
"camp",
|
||||||
|
"creditor",
|
||||||
|
"responsible_team",
|
||||||
|
"approved",
|
||||||
|
"reimbursement",
|
||||||
|
]
|
||||||
|
search_fields = ["description", "amount", "uuid"]
|
||||||
actions = [approve_expenses, reject_expenses]
|
actions = [approve_expenses, reject_expenses]
|
||||||
|
|
||||||
|
|
||||||
### revenues
|
### revenues
|
||||||
|
|
||||||
|
|
||||||
def approve_revenues(modeladmin, request, queryset):
|
def approve_revenues(modeladmin, request, queryset):
|
||||||
for revenue in queryset.all():
|
for revenue in queryset.all():
|
||||||
revenue.approve(request)
|
revenue.approve(request)
|
||||||
|
|
||||||
|
|
||||||
approve_revenues.short_description = "Approve Revenues"
|
approve_revenues.short_description = "Approve Revenues"
|
||||||
|
|
||||||
|
|
||||||
def reject_revenues(modeladmin, request, queryset):
|
def reject_revenues(modeladmin, request, queryset):
|
||||||
for revenue in queryset.all():
|
for revenue in queryset.all():
|
||||||
revenue.reject(request)
|
revenue.reject(request)
|
||||||
|
|
||||||
|
|
||||||
reject_revenues.short_description = "Reject Revenues"
|
reject_revenues.short_description = "Reject Revenues"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Revenue)
|
@admin.register(Revenue)
|
||||||
class RevenueAdmin(admin.ModelAdmin):
|
class RevenueAdmin(admin.ModelAdmin):
|
||||||
list_filter = ['camp', 'responsible_team', 'approved', 'user']
|
list_filter = ["camp", "responsible_team", "approved", "user"]
|
||||||
list_display = ['user', 'description', 'invoice_date', 'amount', 'camp', 'responsible_team', 'approved']
|
list_display = [
|
||||||
search_fields = ['description', 'amount', 'user']
|
"user",
|
||||||
|
"description",
|
||||||
|
"invoice_date",
|
||||||
|
"amount",
|
||||||
|
"camp",
|
||||||
|
"responsible_team",
|
||||||
|
"approved",
|
||||||
|
]
|
||||||
|
search_fields = ["description", "amount", "user"]
|
||||||
actions = [approve_revenues, reject_revenues]
|
actions = [approve_revenues, reject_revenues]
|
||||||
|
|
||||||
|
|
||||||
### reimbursements
|
### reimbursements
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Reimbursement)
|
@admin.register(Reimbursement)
|
||||||
class ReimbursementAdmin(admin.ModelAdmin):
|
class ReimbursementAdmin(admin.ModelAdmin):
|
||||||
def get_amount(self, obj):
|
def get_amount(self, obj):
|
||||||
return obj.amount
|
return obj.amount
|
||||||
list_filter = ['camp', 'user', 'reimbursement_user', 'paid']
|
|
||||||
list_display = ['camp', 'user', 'reimbursement_user', 'paid', 'notes', 'get_amount']
|
|
||||||
search_fields = ['user__username', 'reimbursement_user__username', 'notes']
|
|
||||||
|
|
||||||
|
list_filter = ["camp", "user", "reimbursement_user", "paid"]
|
||||||
|
list_display = ["camp", "user", "reimbursement_user", "paid", "notes", "get_amount"]
|
||||||
|
search_fields = ["user__username", "reimbursement_user__username", "notes"]
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class EconomyConfig(AppConfig):
|
class EconomyConfig(AppConfig):
|
||||||
name = 'economy'
|
name = "economy"
|
||||||
|
|
|
@ -6,6 +6,7 @@ from utils.email import add_outgoing_email
|
||||||
|
|
||||||
# expense emails
|
# expense emails
|
||||||
|
|
||||||
|
|
||||||
def send_accountingsystem_expense_email(expense):
|
def send_accountingsystem_expense_email(expense):
|
||||||
"""
|
"""
|
||||||
Sends an email to the accountingsystem with the invoice as an attachment,
|
Sends an email to the accountingsystem with the invoice as an attachment,
|
||||||
|
@ -44,8 +45,10 @@ def send_expense_rejected_email(expense):
|
||||||
to_recipients=[expense.user.emailaddress_set.get(primary=True).email],
|
to_recipients=[expense.user.emailaddress_set.get(primary=True).email],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# revenue emails
|
# revenue emails
|
||||||
|
|
||||||
|
|
||||||
def send_accountingsystem_revenue_email(revenue):
|
def send_accountingsystem_revenue_email(revenue):
|
||||||
"""
|
"""
|
||||||
Sends an email to the accountingsystem with the invoice as an attachment,
|
Sends an email to the accountingsystem with the invoice as an attachment,
|
||||||
|
@ -83,4 +86,3 @@ def send_revenue_rejected_email(revenue):
|
||||||
subject="Your revenue for %s has been rejected." % revenue.camp.title,
|
subject="Your revenue for %s has been rejected." % revenue.camp.title,
|
||||||
to_recipients=[revenue.user.emailaddress_set.get(primary=True).email],
|
to_recipients=[revenue.user.emailaddress_set.get(primary=True).email],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,12 @@ class CleanInvoiceForm(forms.ModelForm):
|
||||||
We have to define this form explicitly because we want our ImageField to accept PDF files as well as images,
|
We have to define this form explicitly because we want our ImageField to accept PDF files as well as images,
|
||||||
and we cannot change the clean_* methods with an autogenerated form from inside views.py
|
and we cannot change the clean_* methods with an autogenerated form from inside views.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
invoice = forms.FileField()
|
invoice = forms.FileField()
|
||||||
|
|
||||||
def clean_invoice(self):
|
def clean_invoice(self):
|
||||||
# get the uploaded file from cleaned_data
|
# get the uploaded file from cleaned_data
|
||||||
uploaded_file = self.cleaned_data['invoice']
|
uploaded_file = self.cleaned_data["invoice"]
|
||||||
# is this a valid image?
|
# is this a valid image?
|
||||||
try:
|
try:
|
||||||
# create an ImageField instance
|
# create an ImageField instance
|
||||||
|
@ -36,23 +37,41 @@ class CleanInvoiceForm(forms.ModelForm):
|
||||||
class ExpenseCreateForm(CleanInvoiceForm):
|
class ExpenseCreateForm(CleanInvoiceForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Expense
|
model = Expense
|
||||||
fields = ['description', 'amount', 'invoice_date', 'invoice', 'paid_by_bornhack', 'responsible_team']
|
fields = [
|
||||||
|
"description",
|
||||||
|
"amount",
|
||||||
|
"invoice_date",
|
||||||
|
"invoice",
|
||||||
|
"paid_by_bornhack",
|
||||||
|
"responsible_team",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ExpenseUpdateForm(forms.ModelForm):
|
class ExpenseUpdateForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Expense
|
model = Expense
|
||||||
fields = ['description', 'amount', 'invoice_date', 'paid_by_bornhack', 'responsible_team']
|
fields = [
|
||||||
|
"description",
|
||||||
|
"amount",
|
||||||
|
"invoice_date",
|
||||||
|
"paid_by_bornhack",
|
||||||
|
"responsible_team",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class RevenueCreateForm(CleanInvoiceForm):
|
class RevenueCreateForm(CleanInvoiceForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Revenue
|
model = Revenue
|
||||||
fields = ['description', 'amount', 'invoice_date', 'invoice', 'responsible_team']
|
fields = [
|
||||||
|
"description",
|
||||||
|
"amount",
|
||||||
|
"invoice_date",
|
||||||
|
"invoice",
|
||||||
|
"responsible_team",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class RevenueUpdateForm(forms.ModelForm):
|
class RevenueUpdateForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Revenue
|
model = Revenue
|
||||||
fields = ['description', 'amount', 'invoice_date', 'responsible_team']
|
fields = ["description", "amount", "invoice_date", "responsible_team"]
|
||||||
|
|
||||||
|
|
|
@ -12,58 +12,168 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('teams', '0049_auto_20180815_1119'),
|
("teams", "0049_auto_20180815_1119"),
|
||||||
('camps', '0031_auto_20180830_0014'),
|
("camps", "0031_auto_20180830_0014"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Expense',
|
name="Expense",
|
||||||
fields=[
|
fields=[
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"uuid",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.UUIDField(
|
||||||
('amount', models.DecimalField(decimal_places=2, help_text='The amount of this expense in DKK. Must match the amount on the invoice uploaded below.', max_digits=12)),
|
default=uuid.uuid4,
|
||||||
('description', models.CharField(help_text='A short description of this expense. Please keep it meningful as it helps the Economy team a lot when categorising expenses. 200 characters or fewer.', max_length=200)),
|
editable=False,
|
||||||
('paid_by_bornhack', models.BooleanField(default=True, help_text='Leave checked if this expense was paid by BornHack. Uncheck if you need a reimbursement for this expense.')),
|
primary_key=True,
|
||||||
('invoice', models.ImageField(help_text='The invoice for this expense. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted.', upload_to='expenses/')),
|
serialize=False,
|
||||||
('approved', models.NullBooleanField(default=None, help_text='True if this expense has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.')),
|
),
|
||||||
('notes', models.TextField(blank=True, help_text='Economy Team notes for this expense. Only visible to the Economy team and the submitting user.')),
|
),
|
||||||
('camp', models.ForeignKey(help_text='The camp to which this expense belongs', on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='camps.Camp')),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"amount",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=2,
|
||||||
|
help_text="The amount of this expense in DKK. Must match the amount on the invoice uploaded below.",
|
||||||
|
max_digits=12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.CharField(
|
||||||
|
help_text="A short description of this expense. Please keep it meningful as it helps the Economy team a lot when categorising expenses. 200 characters or fewer.",
|
||||||
|
max_length=200,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"paid_by_bornhack",
|
||||||
|
models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
help_text="Leave checked if this expense was paid by BornHack. Uncheck if you need a reimbursement for this expense.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"invoice",
|
||||||
|
models.ImageField(
|
||||||
|
help_text="The invoice for this expense. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted.",
|
||||||
|
upload_to="expenses/",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"approved",
|
||||||
|
models.NullBooleanField(
|
||||||
|
default=None,
|
||||||
|
help_text="True if this expense has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"notes",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Economy Team notes for this expense. Only visible to the Economy team and the submitting user.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The camp to which this expense belongs",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="expenses",
|
||||||
|
to="camps.Camp",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"abstract": False},
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Reimbursement',
|
name="Reimbursement",
|
||||||
fields=[
|
fields=[
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"uuid",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.UUIDField(
|
||||||
('notes', models.TextField(blank=True, help_text='Economy Team notes for this reimbursement. Only visible to the Economy team and the related user.')),
|
default=uuid.uuid4,
|
||||||
('paid', models.BooleanField(default=False, help_text='Check when this reimbursement has been paid to the user')),
|
editable=False,
|
||||||
('camp', models.ForeignKey(help_text='The camp to which this reimbursement belongs', on_delete=django.db.models.deletion.PROTECT, related_name='reimbursements', to='camps.Camp')),
|
primary_key=True,
|
||||||
('reimbursement_user', models.ForeignKey(help_text='The user this reimbursement belongs to.', on_delete=django.db.models.deletion.PROTECT, related_name='reimbursements', to=settings.AUTH_USER_MODEL)),
|
serialize=False,
|
||||||
('user', models.ForeignKey(help_text='The user who created this reimbursement.', on_delete=django.db.models.deletion.PROTECT, related_name='created_reimbursements', to=settings.AUTH_USER_MODEL)),
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"notes",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Economy Team notes for this reimbursement. Only visible to the Economy team and the related user.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"paid",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Check when this reimbursement has been paid to the user",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The camp to which this reimbursement belongs",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="reimbursements",
|
||||||
|
to="camps.Camp",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"reimbursement_user",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The user this reimbursement belongs to.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="reimbursements",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The user who created this reimbursement.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="created_reimbursements",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"abstract": False},
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='reimbursement',
|
name="reimbursement",
|
||||||
field=models.ForeignKey(blank=True, help_text='The reimbursement for this expense, if any. This is a dual-purpose field. If expense.paid_by_bornhack is true then expense.reimbursement references the reimbursement which this expense is created to cover. If expense.paid_by_bornhack is false then expense.reimbursement references the reimbursement which reimbursed this expense.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='economy.Reimbursement'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="The reimbursement for this expense, if any. This is a dual-purpose field. If expense.paid_by_bornhack is true then expense.reimbursement references the reimbursement which this expense is created to cover. If expense.paid_by_bornhack is false then expense.reimbursement references the reimbursement which reimbursed this expense.",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="expenses",
|
||||||
|
to="economy.Reimbursement",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='responsible_team',
|
name="responsible_team",
|
||||||
field=models.ForeignKey(help_text='The team to which this Expense belongs. A team responsible will need to approve the expense. When in doubt pick the Economy team.', on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='teams.Team'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The team to which this Expense belongs. A team responsible will need to approve the expense. When in doubt pick the Economy team.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="expenses",
|
||||||
|
to="teams.Team",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='user',
|
name="user",
|
||||||
field=models.ForeignKey(help_text='The user to which this expense belongs', on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(
|
||||||
|
help_text="The user to which this expense belongs",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="expenses",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,32 +9,101 @@ import uuid
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('shop', '0057_order_notes'),
|
("shop", "0057_order_notes"),
|
||||||
('teams', '0049_auto_20180815_1119'),
|
("teams", "0049_auto_20180815_1119"),
|
||||||
('camps', '0031_auto_20180830_0014'),
|
("camps", "0031_auto_20180830_0014"),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('economy', '0001_initial'),
|
("economy", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Revenue',
|
name="Revenue",
|
||||||
fields=[
|
fields=[
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"uuid",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.UUIDField(
|
||||||
('amount', models.DecimalField(decimal_places=2, help_text='The amount of this revenue in DKK. Must match the amount on the documentation uploaded below.', max_digits=12)),
|
default=uuid.uuid4,
|
||||||
('description', models.CharField(help_text='A short description of this revenue. Please keep it meningful as it helps the Economy team a lot when categorising revenue. 200 characters or fewer.', max_length=200)),
|
editable=False,
|
||||||
('invoice', models.ImageField(help_text='The invoice file for this revenue. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted, as well as PDF.', upload_to='revenues/')),
|
primary_key=True,
|
||||||
('approved', models.NullBooleanField(default=None, help_text='True if this Revenue has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.')),
|
serialize=False,
|
||||||
('notes', models.TextField(blank=True, help_text='Economy Team notes for this revenue. Only visible to the Economy team and the submitting user.')),
|
|
||||||
('camp', models.ForeignKey(help_text='The camp to which this revenue belongs', on_delete=django.db.models.deletion.PROTECT, related_name='revenues', to='camps.Camp')),
|
|
||||||
('invoice_fk', models.ForeignKey(help_text='The Invoice object to which this Revenue object relates. Can be None if this revenue does not have a related BornHack Invoice.', on_delete=django.db.models.deletion.PROTECT, related_name='revenues', to='shop.Invoice')),
|
|
||||||
('responsible_team', models.ForeignKey(help_text='The team to which this revenue belongs. When in doubt pick the Economy team.', on_delete=django.db.models.deletion.PROTECT, related_name='revenues', to='teams.Team')),
|
|
||||||
('user', models.ForeignKey(help_text='The user who submitted this revenue', on_delete=django.db.models.deletion.PROTECT, related_name='revenues', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"amount",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=2,
|
||||||
|
help_text="The amount of this revenue in DKK. Must match the amount on the documentation uploaded below.",
|
||||||
|
max_digits=12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.CharField(
|
||||||
|
help_text="A short description of this revenue. Please keep it meningful as it helps the Economy team a lot when categorising revenue. 200 characters or fewer.",
|
||||||
|
max_length=200,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"invoice",
|
||||||
|
models.ImageField(
|
||||||
|
help_text="The invoice file for this revenue. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted, as well as PDF.",
|
||||||
|
upload_to="revenues/",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"approved",
|
||||||
|
models.NullBooleanField(
|
||||||
|
default=None,
|
||||||
|
help_text="True if this Revenue has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"notes",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Economy Team notes for this revenue. Only visible to the Economy team and the submitting user.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The camp to which this revenue belongs",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="revenues",
|
||||||
|
to="camps.Camp",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"invoice_fk",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The Invoice object to which this Revenue object relates. Can be None if this revenue does not have a related BornHack Invoice.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="revenues",
|
||||||
|
to="shop.Invoice",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"responsible_team",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The team to which this revenue belongs. When in doubt pick the Economy team.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="revenues",
|
||||||
|
to="teams.Team",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The user who submitted this revenue",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="revenues",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={"abstract": False},
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,14 +6,19 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0002_revenue")]
|
||||||
('economy', '0002_revenue'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='revenue',
|
model_name="revenue",
|
||||||
name='invoice_fk',
|
name="invoice_fk",
|
||||||
field=models.ForeignKey(blank=True, help_text='The Invoice object to which this Revenue object relates. Can be None if this revenue does not have a related BornHack Invoice.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='revenues', to='shop.Invoice'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="The Invoice object to which this Revenue object relates. Can be None if this revenue does not have a related BornHack Invoice.",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="revenues",
|
||||||
|
to="shop.Invoice",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,14 +7,17 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0003_auto_20180917_1933")]
|
||||||
('economy', '0003_auto_20180917_1933'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='reimbursement',
|
model_name="reimbursement",
|
||||||
name='user',
|
name="user",
|
||||||
field=models.ForeignKey(help_text='The economy team member who created this reimbursement.', on_delete=django.db.models.deletion.PROTECT, related_name='created_reimbursements', to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(
|
||||||
|
help_text="The economy team member who created this reimbursement.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="created_reimbursements",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,19 +5,25 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0004_auto_20181120_1835")]
|
||||||
('economy', '0004_auto_20181120_1835'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateTimeField(blank=True, help_text='The invoice date for this Expense. This must match the invoice date on the documentation uploaded below.', null=True),
|
field=models.DateTimeField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The invoice date for this Expense. This must match the invoice date on the documentation uploaded below.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='revenue',
|
model_name="revenue",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateTimeField(blank=True, help_text='The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below.', null=True),
|
field=models.DateTimeField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,19 +5,25 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0005_auto_20190120_1532")]
|
||||||
('economy', '0005_auto_20190120_1532'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateField(blank=True, help_text='The invoice date for this Expense. This must match the invoice date on the documentation uploaded below.', null=True),
|
field=models.DateField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The invoice date for this Expense. This must match the invoice date on the documentation uploaded below.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='revenue',
|
model_name="revenue",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateField(blank=True, help_text='The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below.', null=True),
|
field=models.DateField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,19 +5,25 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0006_auto_20190120_1642")]
|
||||||
('economy', '0006_auto_20190120_1642'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateField(blank=True, help_text='The invoice date for this Expense. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.', null=True),
|
field=models.DateField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The invoice date for this Expense. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='revenue',
|
model_name="revenue",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateField(blank=True, help_text='The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.', null=True),
|
field=models.DateField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,63 +7,136 @@ import uuid
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0007_auto_20190327_0936")]
|
||||||
('economy', '0007_auto_20190327_0936'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Chain',
|
name="Chain",
|
||||||
fields=[
|
fields=[
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"uuid",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.UUIDField(
|
||||||
('name', models.CharField(help_text='A short name for this Chain, like "Netto" or "XL Byg". 100 characters or fewer.', max_length=100, unique=True)),
|
default=uuid.uuid4,
|
||||||
('slug', models.SlugField(help_text='The url slug for this Chain. Leave blank to auto generate a slug.', unique=True)),
|
editable=False,
|
||||||
('notes', models.TextField(blank=True, help_text='Any notes for this Chain. Will be shown to anyone creating Expenses or Revenues for this Chain.')),
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
help_text='A short name for this Chain, like "Netto" or "XL Byg". 100 characters or fewer.',
|
||||||
|
max_length=100,
|
||||||
|
unique=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
models.SlugField(
|
||||||
|
help_text="The url slug for this Chain. Leave blank to auto generate a slug.",
|
||||||
|
unique=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"notes",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Any notes for this Chain. Will be shown to anyone creating Expenses or Revenues for this Chain.",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"ordering": ["name"]},
|
||||||
'ordering': ['name'],
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Credebtor',
|
name="Credebtor",
|
||||||
fields=[
|
fields=[
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"uuid",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.UUIDField(
|
||||||
('name', models.CharField(help_text='The name of this Credebtor, like "XL Byg Rønne" or "Netto Gelsted". 100 characters or fewer.', max_length=100, unique=True)),
|
default=uuid.uuid4,
|
||||||
('slug', models.SlugField(help_text='The url slug for this Credebtor. Leave blank to auto generate a slug.')),
|
editable=False,
|
||||||
('address', models.TextField(help_text='The address of this Credebtor.')),
|
primary_key=True,
|
||||||
('notes', models.TextField(blank=True, help_text='Any notes for this Credebtor. Shown when creating an Expense or Revenue for this Credebtor.')),
|
serialize=False,
|
||||||
('chain', models.ForeignKey(help_text='The Chain to which this Credebtor belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='credebtors', to='economy.Chain')),
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
help_text='The name of this Credebtor, like "XL Byg Rønne" or "Netto Gelsted". 100 characters or fewer.',
|
||||||
|
max_length=100,
|
||||||
|
unique=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
models.SlugField(
|
||||||
|
help_text="The url slug for this Credebtor. Leave blank to auto generate a slug."
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"address",
|
||||||
|
models.TextField(help_text="The address of this Credebtor."),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"notes",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Any notes for this Credebtor. Shown when creating an Expense or Revenue for this Credebtor.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"chain",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The Chain to which this Credebtor belongs.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="credebtors",
|
||||||
|
to="economy.Chain",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"ordering": ["name"]},
|
||||||
'ordering': ['name'],
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateField(help_text='The invoice date for this Expense. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.'),
|
field=models.DateField(
|
||||||
|
help_text="The invoice date for this Expense. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD."
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='revenue',
|
model_name="revenue",
|
||||||
name='invoice_date',
|
name="invoice_date",
|
||||||
field=models.DateField(help_text='The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.'),
|
field=models.DateField(
|
||||||
|
help_text="The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD."
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='creditor',
|
name="creditor",
|
||||||
field=models.ForeignKey(help_text='The Creditor to which this expense belongs', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='economy.Credebtor'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The Creditor to which this expense belongs",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="expenses",
|
||||||
|
to="economy.Credebtor",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='revenue',
|
model_name="revenue",
|
||||||
name='debtor',
|
name="debtor",
|
||||||
field=models.ForeignKey(help_text='The Debtor to which this revenue belongs', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='revenues', to='economy.Credebtor'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The Debtor to which this revenue belongs",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="revenues",
|
||||||
|
to="economy.Credebtor",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='credebtor',
|
name="credebtor", unique_together={("chain", "slug")}
|
||||||
unique_together={('chain', 'slug')},
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,19 +5,24 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0008_auto_20190327_1721")]
|
||||||
('economy', '0008_auto_20190327_1721'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='chain',
|
model_name="chain",
|
||||||
name='slug',
|
name="slug",
|
||||||
field=models.SlugField(blank=True, help_text='The url slug for this Chain. Leave blank to auto generate a slug.', unique=True),
|
field=models.SlugField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The url slug for this Chain. Leave blank to auto generate a slug.",
|
||||||
|
unique=True,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='credebtor',
|
model_name="credebtor",
|
||||||
name='slug',
|
name="slug",
|
||||||
field=models.SlugField(blank=True, help_text='The url slug for this Credebtor. Leave blank to auto generate a slug.'),
|
field=models.SlugField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The url slug for this Credebtor. Leave blank to auto generate a slug.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,19 +6,27 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("economy", "0009_auto_20190328_0715")]
|
||||||
('economy', '0009_auto_20190328_0715'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='expense',
|
model_name="expense",
|
||||||
name='creditor',
|
name="creditor",
|
||||||
field=models.ForeignKey(help_text='The Creditor to which this expense belongs', on_delete=django.db.models.deletion.PROTECT, related_name='expenses', to='economy.Credebtor'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The Creditor to which this expense belongs",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="expenses",
|
||||||
|
to="economy.Credebtor",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='revenue',
|
model_name="revenue",
|
||||||
name='debtor',
|
name="debtor",
|
||||||
field=models.ForeignKey(help_text='The Debtor to which this revenue belongs', on_delete=django.db.models.deletion.PROTECT, related_name='revenues', to='economy.Credebtor'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The Debtor to which this revenue belongs",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="revenues",
|
||||||
|
to="economy.Credebtor",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,14 +10,16 @@ from django.utils.text import slugify
|
||||||
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
||||||
from .email import *
|
from .email import *
|
||||||
|
|
||||||
|
|
||||||
class ChainManager(models.Manager):
|
class ChainManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
ChainManager adds 'expenses_total' and 'revenues_total' to the Chain qs
|
ChainManager adds 'expenses_total' and 'revenues_total' to the Chain qs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = super().get_queryset()
|
qs = super().get_queryset()
|
||||||
qs = qs.annotate(expenses_total=models.Sum('credebtors__expenses__amount'))
|
qs = qs.annotate(expenses_total=models.Sum("credebtors__expenses__amount"))
|
||||||
qs = qs.annotate(revenues_total=models.Sum('credebtors__revenues__amount'))
|
qs = qs.annotate(revenues_total=models.Sum("credebtors__revenues__amount"))
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,25 +28,26 @@ class Chain(CreatedUpdatedModel, UUIDModel):
|
||||||
A chain of Credebtors. Used to group when several Creditors/Debtors
|
A chain of Credebtors. Used to group when several Creditors/Debtors
|
||||||
belong to the same Chain/company, like XL Byg stores or Netto stores.
|
belong to the same Chain/company, like XL Byg stores or Netto stores.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ["name"]
|
||||||
|
|
||||||
objects = ChainManager()
|
objects = ChainManager()
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
unique=True,
|
unique=True,
|
||||||
help_text='A short name for this Chain, like "Netto" or "XL Byg". 100 characters or fewer.'
|
help_text='A short name for this Chain, like "Netto" or "XL Byg". 100 characters or fewer.',
|
||||||
)
|
)
|
||||||
|
|
||||||
slug = models.SlugField(
|
slug = models.SlugField(
|
||||||
unique=True,
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='The url slug for this Chain. Leave blank to auto generate a slug.'
|
help_text="The url slug for this Chain. Leave blank to auto generate a slug.",
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = models.TextField(
|
notes = models.TextField(
|
||||||
help_text='Any notes for this Chain. Will be shown to anyone creating Expenses or Revenues for this Chain.',
|
help_text="Any notes for this Chain. Will be shown to anyone creating Expenses or Revenues for this Chain.",
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,10 +72,11 @@ 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
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = super().get_queryset()
|
qs = super().get_queryset()
|
||||||
qs = qs.annotate(expenses_total=models.Sum('expenses__amount'))
|
qs = qs.annotate(expenses_total=models.Sum("expenses__amount"))
|
||||||
qs = qs.annotate(revenues_total=models.Sum('revenues__amount'))
|
qs = qs.annotate(revenues_total=models.Sum("revenues__amount"))
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,37 +87,36 @@ class Credebtor(CreatedUpdatedModel, UUIDModel):
|
||||||
The model is used for both creditors and debtors, since there is a
|
The model is used for both creditors and debtors, since there is a
|
||||||
lot of overlap between them.
|
lot of overlap between them.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ["name"]
|
||||||
unique_together=('chain', 'slug')
|
unique_together = ("chain", "slug")
|
||||||
|
|
||||||
objects = CredebtorManager()
|
objects = CredebtorManager()
|
||||||
|
|
||||||
chain = models.ForeignKey(
|
chain = models.ForeignKey(
|
||||||
'economy.Chain',
|
"economy.Chain",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='credebtors',
|
related_name="credebtors",
|
||||||
help_text='The Chain to which this Credebtor belongs.',
|
help_text="The Chain to which this Credebtor belongs.",
|
||||||
)
|
)
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
unique=True,
|
unique=True,
|
||||||
help_text='The name of this Credebtor, like "XL Byg Rønne" or "Netto Gelsted". 100 characters or fewer.'
|
help_text='The name of this Credebtor, like "XL Byg Rønne" or "Netto Gelsted". 100 characters or fewer.',
|
||||||
)
|
)
|
||||||
|
|
||||||
slug = models.SlugField(
|
slug = models.SlugField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='The url slug for this Credebtor. Leave blank to auto generate a slug.'
|
help_text="The url slug for this Credebtor. Leave blank to auto generate a slug.",
|
||||||
)
|
)
|
||||||
|
|
||||||
address = models.TextField(
|
address = models.TextField(help_text="The address of this Credebtor.")
|
||||||
help_text='The address of this Credebtor.',
|
|
||||||
)
|
|
||||||
|
|
||||||
notes = models.TextField(
|
notes = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Any notes for this Credebtor. Shown when creating an Expense or Revenue for this Credebtor.',
|
help_text="Any notes for this Credebtor. Shown when creating an Expense or Revenue for this Credebtor.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -138,76 +141,77 @@ class Revenue(CampRelatedModel, UUIDModel):
|
||||||
Other Revenue objects (such as money returned from bottle deposits) will
|
Other Revenue objects (such as money returned from bottle deposits) will
|
||||||
not have a related BornHack Invoice object.
|
not have a related BornHack Invoice object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
camp = models.ForeignKey(
|
camp = models.ForeignKey(
|
||||||
'camps.Camp',
|
"camps.Camp",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='revenues',
|
related_name="revenues",
|
||||||
help_text='The camp to which this revenue belongs',
|
help_text="The camp to which this revenue belongs",
|
||||||
)
|
)
|
||||||
|
|
||||||
debtor = models.ForeignKey(
|
debtor = models.ForeignKey(
|
||||||
'economy.Credebtor',
|
"economy.Credebtor",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='revenues',
|
related_name="revenues",
|
||||||
help_text='The Debtor to which this revenue belongs',
|
help_text="The Debtor to which this revenue belongs",
|
||||||
)
|
)
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
'auth.User',
|
"auth.User",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='revenues',
|
related_name="revenues",
|
||||||
help_text='The user who submitted this revenue',
|
help_text="The user who submitted this revenue",
|
||||||
)
|
)
|
||||||
|
|
||||||
amount = models.DecimalField(
|
amount = models.DecimalField(
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
max_digits=12,
|
max_digits=12,
|
||||||
help_text='The amount of this revenue in DKK. Must match the amount on the documentation uploaded below.',
|
help_text="The amount of this revenue in DKK. Must match the amount on the documentation uploaded below.",
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
help_text='A short description of this revenue. Please keep it meningful as it helps the Economy team a lot when categorising revenue. 200 characters or fewer.',
|
help_text="A short description of this revenue. Please keep it meningful as it helps the Economy team a lot when categorising revenue. 200 characters or fewer.",
|
||||||
)
|
)
|
||||||
|
|
||||||
invoice = models.ImageField(
|
invoice = models.ImageField(
|
||||||
help_text='The invoice file for this revenue. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted, as well as PDF.',
|
help_text="The invoice file for this revenue. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted, as well as PDF.",
|
||||||
upload_to='revenues/',
|
upload_to="revenues/",
|
||||||
)
|
)
|
||||||
|
|
||||||
invoice_date = models.DateField(
|
invoice_date = models.DateField(
|
||||||
help_text='The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.',
|
help_text="The invoice date for this Revenue. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD."
|
||||||
)
|
)
|
||||||
|
|
||||||
invoice_fk = models.ForeignKey(
|
invoice_fk = models.ForeignKey(
|
||||||
'shop.Invoice',
|
"shop.Invoice",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='revenues',
|
related_name="revenues",
|
||||||
help_text='The Invoice object to which this Revenue object relates. Can be None if this revenue does not have a related BornHack Invoice.',
|
help_text="The Invoice object to which this Revenue object relates. Can be None if this revenue does not have a related BornHack Invoice.",
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
responsible_team = models.ForeignKey(
|
responsible_team = models.ForeignKey(
|
||||||
'teams.Team',
|
"teams.Team",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='revenues',
|
related_name="revenues",
|
||||||
help_text='The team to which this revenue belongs. When in doubt pick the Economy team.'
|
help_text="The team to which this revenue belongs. When in doubt pick the Economy team.",
|
||||||
)
|
)
|
||||||
|
|
||||||
approved = models.NullBooleanField(
|
approved = models.NullBooleanField(
|
||||||
default=None,
|
default=None,
|
||||||
help_text='True if this Revenue has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.'
|
help_text="True if this Revenue has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.",
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = models.TextField(
|
notes = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Economy Team notes for this revenue. Only visible to the Economy team and the submitting user.'
|
help_text="Economy Team notes for this revenue. Only visible to the Economy team and the submitting user.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.amount < 0:
|
if self.amount < 0:
|
||||||
raise ValidationError('Amount of a Revenue object can not be negative')
|
raise ValidationError("Amount of a Revenue object can not be negative")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def invoice_filename(self):
|
def invoice_filename(self):
|
||||||
|
@ -228,7 +232,10 @@ class Revenue(CampRelatedModel, UUIDModel):
|
||||||
Approving a revenue triggers an email to the economy system, and another email to the user who submitted the revenue
|
Approving a revenue triggers an email to the economy system, and another email to the user who submitted the revenue
|
||||||
"""
|
"""
|
||||||
if request.user == self.user:
|
if request.user == self.user:
|
||||||
messages.error(request, "You cannot approve your own revenues, aka. the anti-stein-bagger defense")
|
messages.error(
|
||||||
|
request,
|
||||||
|
"You cannot approve your own revenues, aka. the anti-stein-bagger defense",
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# mark as approved and save
|
# mark as approved and save
|
||||||
|
@ -262,35 +269,35 @@ class Revenue(CampRelatedModel, UUIDModel):
|
||||||
|
|
||||||
class Expense(CampRelatedModel, UUIDModel):
|
class Expense(CampRelatedModel, UUIDModel):
|
||||||
camp = models.ForeignKey(
|
camp = models.ForeignKey(
|
||||||
'camps.Camp',
|
"camps.Camp",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='expenses',
|
related_name="expenses",
|
||||||
help_text='The camp to which this expense belongs',
|
help_text="The camp to which this expense belongs",
|
||||||
)
|
)
|
||||||
|
|
||||||
creditor = models.ForeignKey(
|
creditor = models.ForeignKey(
|
||||||
'economy.Credebtor',
|
"economy.Credebtor",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='expenses',
|
related_name="expenses",
|
||||||
help_text='The Creditor to which this expense belongs',
|
help_text="The Creditor to which this expense belongs",
|
||||||
)
|
)
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
'auth.User',
|
"auth.User",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='expenses',
|
related_name="expenses",
|
||||||
help_text='The user to which this expense belongs',
|
help_text="The user to which this expense belongs",
|
||||||
)
|
)
|
||||||
|
|
||||||
amount = models.DecimalField(
|
amount = models.DecimalField(
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
max_digits=12,
|
max_digits=12,
|
||||||
help_text='The amount of this expense in DKK. Must match the amount on the invoice uploaded below.',
|
help_text="The amount of this expense in DKK. Must match the amount on the invoice uploaded below.",
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
help_text='A short description of this expense. Please keep it meningful as it helps the Economy team a lot when categorising expenses. 200 characters or fewer.',
|
help_text="A short description of this expense. Please keep it meningful as it helps the Economy team a lot when categorising expenses. 200 characters or fewer.",
|
||||||
)
|
)
|
||||||
|
|
||||||
paid_by_bornhack = models.BooleanField(
|
paid_by_bornhack = models.BooleanField(
|
||||||
|
@ -299,43 +306,43 @@ class Expense(CampRelatedModel, UUIDModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
invoice = models.ImageField(
|
invoice = models.ImageField(
|
||||||
help_text='The invoice for this expense. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted.',
|
help_text="The invoice for this expense. Please make sure the amount on the invoice matches the amount you entered above. All common image formats are accepted.",
|
||||||
upload_to='expenses/',
|
upload_to="expenses/",
|
||||||
)
|
)
|
||||||
|
|
||||||
invoice_date = models.DateField(
|
invoice_date = models.DateField(
|
||||||
help_text='The invoice date for this Expense. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD.',
|
help_text="The invoice date for this Expense. This must match the invoice date on the documentation uploaded below. Format is YYYY-MM-DD."
|
||||||
)
|
)
|
||||||
|
|
||||||
responsible_team = models.ForeignKey(
|
responsible_team = models.ForeignKey(
|
||||||
'teams.Team',
|
"teams.Team",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='expenses',
|
related_name="expenses",
|
||||||
help_text='The team to which this Expense belongs. A team responsible will need to approve the expense. When in doubt pick the Economy team.'
|
help_text="The team to which this Expense belongs. A team responsible will need to approve the expense. When in doubt pick the Economy team.",
|
||||||
)
|
)
|
||||||
|
|
||||||
approved = models.NullBooleanField(
|
approved = models.NullBooleanField(
|
||||||
default=None,
|
default=None,
|
||||||
help_text='True if this expense has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.'
|
help_text="True if this expense has been approved by the responsible team. False if it has been rejected. Blank if noone has decided yet.",
|
||||||
)
|
)
|
||||||
|
|
||||||
reimbursement = models.ForeignKey(
|
reimbursement = models.ForeignKey(
|
||||||
'economy.Reimbursement',
|
"economy.Reimbursement",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='expenses',
|
related_name="expenses",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='The reimbursement for this expense, if any. This is a dual-purpose field. If expense.paid_by_bornhack is true then expense.reimbursement references the reimbursement which this expense is created to cover. If expense.paid_by_bornhack is false then expense.reimbursement references the reimbursement which reimbursed this expense.'
|
help_text="The reimbursement for this expense, if any. This is a dual-purpose field. If expense.paid_by_bornhack is true then expense.reimbursement references the reimbursement which this expense is created to cover. If expense.paid_by_bornhack is false then expense.reimbursement references the reimbursement which reimbursed this expense.",
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = models.TextField(
|
notes = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Economy Team notes for this expense. Only visible to the Economy team and the submitting user.'
|
help_text="Economy Team notes for this expense. Only visible to the Economy team and the submitting user.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.amount < 0:
|
if self.amount < 0:
|
||||||
raise ValidationError('Amount of an expense can not be negative')
|
raise ValidationError("Amount of an expense can not be negative")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def invoice_filename(self):
|
def invoice_filename(self):
|
||||||
|
@ -356,7 +363,10 @@ class Expense(CampRelatedModel, UUIDModel):
|
||||||
Approving an expense triggers an email to the economy system, and another email to the user who submitted the expense in the first place.
|
Approving an expense triggers an email to the economy system, and another email to the user who submitted the expense in the first place.
|
||||||
"""
|
"""
|
||||||
if request.user == self.user:
|
if request.user == self.user:
|
||||||
messages.error(request, "You cannot approve your own expenses, aka. the anti-stein-bagger defense")
|
messages.error(
|
||||||
|
request,
|
||||||
|
"You cannot approve your own expenses, aka. the anti-stein-bagger defense",
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# mark as approved and save
|
# mark as approved and save
|
||||||
|
@ -392,30 +402,31 @@ class Reimbursement(CampRelatedModel, UUIDModel):
|
||||||
"""
|
"""
|
||||||
A reimbursement covers one or more expenses.
|
A reimbursement covers one or more expenses.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
camp = models.ForeignKey(
|
camp = models.ForeignKey(
|
||||||
'camps.Camp',
|
"camps.Camp",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='reimbursements',
|
related_name="reimbursements",
|
||||||
help_text='The camp to which this reimbursement belongs',
|
help_text="The camp to which this reimbursement belongs",
|
||||||
)
|
)
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
'auth.User',
|
"auth.User",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='created_reimbursements',
|
related_name="created_reimbursements",
|
||||||
help_text='The economy team member who created this reimbursement.'
|
help_text="The economy team member who created this reimbursement.",
|
||||||
)
|
)
|
||||||
|
|
||||||
reimbursement_user = models.ForeignKey(
|
reimbursement_user = models.ForeignKey(
|
||||||
'auth.User',
|
"auth.User",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='reimbursements',
|
related_name="reimbursements",
|
||||||
help_text='The user this reimbursement belongs to.'
|
help_text="The user this reimbursement belongs to.",
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = models.TextField(
|
notes = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Economy Team notes for this reimbursement. Only visible to the Economy team and the related user.'
|
help_text="Economy Team notes for this reimbursement. Only visible to the Economy team and the related user.",
|
||||||
)
|
)
|
||||||
|
|
||||||
paid = models.BooleanField(
|
paid = models.BooleanField(
|
||||||
|
@ -439,5 +450,3 @@ class Reimbursement(CampRelatedModel, UUIDModel):
|
||||||
for expense in self.expenses.filter(paid_by_bornhack=False):
|
for expense in self.expenses.filter(paid_by_bornhack=False):
|
||||||
amount += expense.amount
|
amount += expense.amount
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,150 +1,132 @@
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from .views import *
|
from .views import *
|
||||||
|
|
||||||
app_name = 'economy'
|
app_name = "economy"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(
|
path("", EconomyDashboardView.as_view(), name="dashboard"),
|
||||||
'',
|
|
||||||
EconomyDashboardView.as_view(),
|
|
||||||
name='dashboard'
|
|
||||||
),
|
|
||||||
|
|
||||||
# chains
|
# chains
|
||||||
path('chains/',
|
|
||||||
include([
|
|
||||||
path(
|
path(
|
||||||
'',
|
"chains/",
|
||||||
ChainListView.as_view(),
|
include(
|
||||||
name='chain_list',
|
[
|
||||||
|
path("", ChainListView.as_view(), name="chain_list"),
|
||||||
|
path("add/", ChainCreateView.as_view(), name="chain_create"),
|
||||||
|
path(
|
||||||
|
"<slug:chain_slug>/",
|
||||||
|
include(
|
||||||
|
[
|
||||||
|
path(
|
||||||
|
"", CredebtorListView.as_view(), name="credebtor_list"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'add/',
|
"add/",
|
||||||
ChainCreateView.as_view(),
|
|
||||||
name='chain_create',
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'<slug:chain_slug>/',
|
|
||||||
include([
|
|
||||||
path(
|
|
||||||
'',
|
|
||||||
CredebtorListView.as_view(),
|
|
||||||
name='credebtor_list',
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'add/',
|
|
||||||
CredebtorCreateView.as_view(),
|
CredebtorCreateView.as_view(),
|
||||||
name='credebtor_create',
|
name="credebtor_create",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'<slug:credebtor_slug>/',
|
"<slug:credebtor_slug>/",
|
||||||
include([
|
include(
|
||||||
|
[
|
||||||
path(
|
path(
|
||||||
'add_expense/',
|
"add_expense/",
|
||||||
ExpenseCreateView.as_view(),
|
ExpenseCreateView.as_view(),
|
||||||
name='expense_create',
|
name="expense_create",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'add_revenue/',
|
"add_revenue/",
|
||||||
RevenueCreateView.as_view(),
|
RevenueCreateView.as_view(),
|
||||||
name='revenue_create',
|
name="revenue_create",
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
# expenses
|
# expenses
|
||||||
path(
|
path(
|
||||||
'expenses/',
|
"expenses/",
|
||||||
include([
|
include(
|
||||||
|
[
|
||||||
|
path("", ExpenseListView.as_view(), name="expense_list"),
|
||||||
path(
|
path(
|
||||||
'',
|
"<uuid:pk>/",
|
||||||
ExpenseListView.as_view(),
|
include(
|
||||||
name='expense_list',
|
[
|
||||||
|
path(
|
||||||
|
"", ExpenseDetailView.as_view(), name="expense_detail"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'<uuid:pk>/',
|
"update/",
|
||||||
include([
|
|
||||||
path(
|
|
||||||
'',
|
|
||||||
ExpenseDetailView.as_view(),
|
|
||||||
name='expense_detail'
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'update/',
|
|
||||||
ExpenseUpdateView.as_view(),
|
ExpenseUpdateView.as_view(),
|
||||||
name='expense_update'
|
name="expense_update",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'delete/',
|
"delete/",
|
||||||
ExpenseDeleteView.as_view(),
|
ExpenseDeleteView.as_view(),
|
||||||
name='expense_delete'
|
name="expense_delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'invoice/',
|
"invoice/",
|
||||||
ExpenseInvoiceView.as_view(),
|
ExpenseInvoiceView.as_view(),
|
||||||
name='expense_invoice'
|
name="expense_invoice",
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
# reimbursements
|
# reimbursements
|
||||||
path(
|
path(
|
||||||
'reimbursements/',
|
"reimbursements/",
|
||||||
include([
|
include(
|
||||||
|
[
|
||||||
|
path("", ReimbursementListView.as_view(), name="reimbursement_list"),
|
||||||
path(
|
path(
|
||||||
'',
|
"<uuid:pk>/",
|
||||||
ReimbursementListView.as_view(),
|
|
||||||
name='reimbursement_list'
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'<uuid:pk>/',
|
|
||||||
ReimbursementDetailView.as_view(),
|
ReimbursementDetailView.as_view(),
|
||||||
name='reimbursement_detail'
|
name="reimbursement_detail",
|
||||||
|
),
|
||||||
|
]
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
# revenue
|
# revenue
|
||||||
path(
|
path(
|
||||||
'revenues/',
|
"revenues/",
|
||||||
include([
|
include(
|
||||||
|
[
|
||||||
|
path("", RevenueListView.as_view(), name="revenue_list"),
|
||||||
path(
|
path(
|
||||||
'',
|
"<uuid:pk>/",
|
||||||
RevenueListView.as_view(),
|
include(
|
||||||
name='revenue_list'
|
[
|
||||||
|
path(
|
||||||
|
"", RevenueDetailView.as_view(), name="revenue_detail"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'<uuid:pk>/',
|
"update/",
|
||||||
include([
|
|
||||||
path(
|
|
||||||
'',
|
|
||||||
RevenueDetailView.as_view(),
|
|
||||||
name='revenue_detail'
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
'update/',
|
|
||||||
RevenueUpdateView.as_view(),
|
RevenueUpdateView.as_view(),
|
||||||
name='revenue_update'
|
name="revenue_update",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'delete/',
|
"delete/",
|
||||||
RevenueDeleteView.as_view(),
|
RevenueDeleteView.as_view(),
|
||||||
name='revenue_delete'
|
name="revenue_delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
'invoice/',
|
"invoice/",
|
||||||
RevenueInvoiceView.as_view(),
|
RevenueInvoiceView.as_view(),
|
||||||
name='revenue_invoice'
|
name="revenue_invoice",
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
),
|
),
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Type, Routing
|
from .models import Type, Routing
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Type)
|
@admin.register(Type)
|
||||||
class TypeAdmin(admin.ModelAdmin):
|
class TypeAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Routing)
|
@admin.register(Routing)
|
||||||
class RoutingAdmin(admin.ModelAdmin):
|
class RoutingAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class EventsConfig(AppConfig):
|
class EventsConfig(AppConfig):
|
||||||
name = 'events'
|
name = "events"
|
||||||
|
|
|
@ -2,19 +2,27 @@ from django.utils import timezone
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from ircbot.utils import add_irc_message
|
from ircbot.utils import add_irc_message
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
def handle_team_event(eventtype, irc_message=None, irc_timeout=60, email_template=None, email_formatdict=None):
|
def handle_team_event(
|
||||||
|
eventtype,
|
||||||
|
irc_message=None,
|
||||||
|
irc_timeout=60,
|
||||||
|
email_template=None,
|
||||||
|
email_formatdict=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
This method is our basic event handler.
|
This method is our basic event handler.
|
||||||
The type of event determines which teams receive notifications.
|
The type of event determines which teams receive notifications.
|
||||||
TODO: Add some sort of priority to messages
|
TODO: Add some sort of priority to messages
|
||||||
"""
|
"""
|
||||||
#logger.info("Inside handle_team_event, eventtype %s" % eventtype)
|
# logger.info("Inside handle_team_event, eventtype %s" % eventtype)
|
||||||
|
|
||||||
# get event type from database
|
# get event type from database
|
||||||
from .models import Type
|
from .models import Type
|
||||||
|
|
||||||
try:
|
try:
|
||||||
eventtype = Type.objects.get(name=eventtype)
|
eventtype = Type.objects.get(name=eventtype)
|
||||||
except Type.DoesNotExist:
|
except Type.DoesNotExist:
|
||||||
|
@ -24,14 +32,21 @@ def handle_team_event(eventtype, irc_message=None, irc_timeout=60, email_templat
|
||||||
|
|
||||||
if not eventtype.teams:
|
if not eventtype.teams:
|
||||||
# no routes found for this eventtype, do nothing
|
# no routes found for this eventtype, do nothing
|
||||||
#logger.error("No routes round for eventtype %s" % eventtype)
|
# logger.error("No routes round for eventtype %s" % eventtype)
|
||||||
return
|
return
|
||||||
|
|
||||||
# loop over routes (teams) for this eventtype
|
# loop over routes (teams) for this eventtype
|
||||||
for team in eventtype.teams:
|
for team in eventtype.teams:
|
||||||
logger.info("Handling eventtype %s for team %s" % (eventtype, team))
|
logger.info("Handling eventtype %s for team %s" % (eventtype, team))
|
||||||
team_irc_notification(team=team, eventtype=eventtype, irc_message=irc_message, irc_timeout=irc_timeout)
|
team_irc_notification(
|
||||||
team_email_notification(team=team, eventtype=eventtype, email_template=None, email_formatdict=None)
|
team=team,
|
||||||
|
eventtype=eventtype,
|
||||||
|
irc_message=irc_message,
|
||||||
|
irc_timeout=irc_timeout,
|
||||||
|
)
|
||||||
|
team_email_notification(
|
||||||
|
team=team, eventtype=eventtype, email_template=None, email_formatdict=None
|
||||||
|
)
|
||||||
# handle any future notification types here..
|
# handle any future notification types here..
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,14 +69,14 @@ def team_irc_notification(team, eventtype, irc_message=None, irc_timeout=60):
|
||||||
|
|
||||||
# send an IRC message to the the channel for this team
|
# send an IRC message to the the channel for this team
|
||||||
add_irc_message(
|
add_irc_message(
|
||||||
target=team.private_irc_channel_name,
|
target=team.private_irc_channel_name, message=irc_message, timeout=60
|
||||||
message=irc_message,
|
|
||||||
timeout=60
|
|
||||||
)
|
)
|
||||||
logger.info("Added new IRC message for channel %s" % team.irc_channel_name)
|
logger.info("Added new IRC message for channel %s" % team.irc_channel_name)
|
||||||
|
|
||||||
|
|
||||||
def team_email_notification(team, eventtype, email_template=None, email_formatdict=None):
|
def team_email_notification(
|
||||||
|
team, eventtype, email_template=None, email_formatdict=None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Sends email notifications for events to team mailinglists (if possible,
|
Sends email notifications for events to team mailinglists (if possible,
|
||||||
otherwise directly to the team responsibles)
|
otherwise directly to the team responsibles)
|
||||||
|
@ -78,4 +93,3 @@ def team_email_notification(team, eventtype, email_template=None, email_formatdi
|
||||||
recipient_list = [resp.email for resp in team.responsible_members.all()]
|
recipient_list = [resp.email for resp in team.responsible_members.all()]
|
||||||
|
|
||||||
# TODO: actually send the email here
|
# TODO: actually send the email here
|
||||||
|
|
||||||
|
|
|
@ -10,42 +10,62 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("teams", "0025_auto_20180318_1318")]
|
||||||
('teams', '0025_auto_20180318_1318'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Routing',
|
name="Routing",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"id",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={"abstract": False},
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Type',
|
name="Type",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"id",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.AutoField(
|
||||||
('name', models.TextField(help_text='The type of event', unique=True)),
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
("name", models.TextField(help_text="The type of event", unique=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={"abstract": False},
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='routing',
|
model_name="routing",
|
||||||
name='eventtype',
|
name="eventtype",
|
||||||
field=models.ForeignKey(help_text='The type of event to route', on_delete=django.db.models.deletion.PROTECT, related_name='eventroutes', to='events.Type'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The type of event to route",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="eventroutes",
|
||||||
|
to="events.Type",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='routing',
|
model_name="routing",
|
||||||
name='team',
|
name="team",
|
||||||
field=models.ForeignKey(help_text='The team which should receive events of this type.', on_delete=django.db.models.deletion.PROTECT, related_name='eventroutes', to='teams.Team'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The team which should receive events of this type.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="eventroutes",
|
||||||
|
to="teams.Team",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,17 +4,14 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def create_eventtypes(apps, schema_editor):
|
def create_eventtypes(apps, schema_editor):
|
||||||
Type = apps.get_model('events', 'Type')
|
Type = apps.get_model("events", "Type")
|
||||||
Type.objects.create(name='public_credit_name_changed')
|
Type.objects.create(name="public_credit_name_changed")
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("events", "0001_initial")]
|
||||||
('events', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(create_eventtypes),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
operations = [migrations.RunPython(create_eventtypes)]
|
||||||
|
|
|
@ -4,17 +4,14 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def create_eventtype(apps, schema_editor):
|
def create_eventtype(apps, schema_editor):
|
||||||
Type = apps.get_model('events', 'Type')
|
Type = apps.get_model("events", "Type")
|
||||||
Type.objects.create(name='ticket_created')
|
Type.objects.create(name="ticket_created")
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("events", "0002_create_eventtype")]
|
||||||
('events', '0002_create_eventtype'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [migrations.RunPython(create_eventtype)]
|
||||||
migrations.RunPython(create_eventtype),
|
|
||||||
]
|
|
||||||
|
|
|
@ -7,19 +7,23 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("events", "0003_create_another_eventtype")]
|
||||||
('events', '0003_create_another_eventtype'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='type',
|
model_name="type",
|
||||||
name='email_notification',
|
name="email_notification",
|
||||||
field=models.BooleanField(default=False, help_text='Check to send email notifications for this type of event.'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Check to send email notifications for this type of event.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='type',
|
model_name="type",
|
||||||
name='irc_notification',
|
name="irc_notification",
|
||||||
field=models.BooleanField(default=False, help_text='Check to send IRC notifications for this type of event.'),
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Check to send IRC notifications for this type of event.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.db import models
|
||||||
from utils.models import CreatedUpdatedModel
|
from utils.models import CreatedUpdatedModel
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
|
|
||||||
|
|
||||||
class Type(CreatedUpdatedModel):
|
class Type(CreatedUpdatedModel):
|
||||||
"""
|
"""
|
||||||
The events.Type model contains different types of system events which can happen.
|
The events.Type model contains different types of system events which can happen.
|
||||||
|
@ -10,20 +11,19 @@ class Type(CreatedUpdatedModel):
|
||||||
- ticket_created: Whenever a new ShopTicket is created
|
- ticket_created: Whenever a new ShopTicket is created
|
||||||
- public_credit_name_changed: Whenever a user changes public_credit_name in the profile
|
- public_credit_name_changed: Whenever a user changes public_credit_name in the profile
|
||||||
"""
|
"""
|
||||||
name = models.TextField(
|
|
||||||
unique=True,
|
name = models.TextField(unique=True, help_text="The type of event")
|
||||||
help_text='The type of event'
|
|
||||||
)
|
|
||||||
|
|
||||||
irc_notification = models.BooleanField(
|
irc_notification = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
help_text='Check to send IRC notifications for this type of event.',
|
help_text="Check to send IRC notifications for this type of event.",
|
||||||
)
|
)
|
||||||
|
|
||||||
email_notification = models.BooleanField(
|
email_notification = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
help_text='Check to send email notifications for this type of event.',
|
help_text="Check to send email notifications for this type of event.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ class Type(CreatedUpdatedModel):
|
||||||
"""
|
"""
|
||||||
This property returns a queryset with all the teams that should receive this type of events
|
This property returns a queryset with all the teams that should receive this type of events
|
||||||
"""
|
"""
|
||||||
team_ids = Routing.objects.filter(eventtype=self).values_list('team', flat=True)
|
team_ids = Routing.objects.filter(eventtype=self).values_list("team", flat=True)
|
||||||
return Team.objects.filter(pk__in=team_ids)
|
return Team.objects.filter(pk__in=team_ids)
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,20 +42,20 @@ class Routing(CreatedUpdatedModel):
|
||||||
Add a new entry to route events of a certain type to a team.
|
Add a new entry to route events of a certain type to a team.
|
||||||
Several teams can receive the same type of event.
|
Several teams can receive the same type of event.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
eventtype = models.ForeignKey(
|
eventtype = models.ForeignKey(
|
||||||
'events.Type',
|
"events.Type",
|
||||||
related_name='eventroutes',
|
related_name="eventroutes",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
help_text='The type of event to route',
|
help_text="The type of event to route",
|
||||||
)
|
)
|
||||||
|
|
||||||
team = models.ForeignKey(
|
team = models.ForeignKey(
|
||||||
'teams.Team',
|
"teams.Team",
|
||||||
related_name='eventroutes',
|
related_name="eventroutes",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
help_text='The team which should receive events of this type.'
|
help_text="The team which should receive events of this type.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s -> %s" % (self.eventtype, self.team)
|
return "%s -> %s" % (self.eventtype, self.team)
|
||||||
|
|
||||||
|
|
|
@ -5,4 +5,4 @@ from .models import Feedback
|
||||||
|
|
||||||
@admin.register(Feedback)
|
@admin.register(Feedback)
|
||||||
class FeedbackAdmin(admin.ModelAdmin):
|
class FeedbackAdmin(admin.ModelAdmin):
|
||||||
list_display = ('user', 'camp', 'feedback')
|
list_display = ("user", "camp", "feedback")
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class FeedbackConfig(AppConfig):
|
class FeedbackConfig(AppConfig):
|
||||||
name = 'feedback'
|
name = "feedback"
|
||||||
|
|
|
@ -10,22 +10,32 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Feedback',
|
name="Feedback",
|
||||||
fields=[
|
fields=[
|
||||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"uuid",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.UUIDField(
|
||||||
('feedback', models.TextField()),
|
default=uuid.uuid4,
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
editable=False,
|
||||||
],
|
primary_key=True,
|
||||||
options={
|
serialize=False,
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
("feedback", models.TextField()),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={"abstract": False},
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,16 +6,17 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0030_camp_light_text"), ("feedback", "0001_initial")]
|
||||||
('camps', '0030_camp_light_text'),
|
|
||||||
('feedback', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='feedback',
|
model_name="feedback",
|
||||||
name='camp',
|
name="camp",
|
||||||
field=models.ForeignKey(default='30fd754f-dae4-460f-8128-6638fb29ab2d', on_delete=django.db.models.deletion.PROTECT, to='camps.Camp'),
|
field=models.ForeignKey(
|
||||||
preserve_default=False,
|
default="30fd754f-dae4-460f-8128-6638fb29ab2d",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to="camps.Camp",
|
||||||
),
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,6 +4,6 @@ from utils.models import UUIDModel, CreatedUpdatedModel, CampRelatedModel
|
||||||
|
|
||||||
|
|
||||||
class Feedback(CampRelatedModel, UUIDModel):
|
class Feedback(CampRelatedModel, UUIDModel):
|
||||||
camp = models.ForeignKey('camps.Camp', on_delete=models.PROTECT)
|
camp = models.ForeignKey("camps.Camp", on_delete=models.PROTECT)
|
||||||
user = models.ForeignKey('auth.User', on_delete=models.PROTECT)
|
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
|
||||||
feedback = models.TextField()
|
feedback = models.TextField()
|
||||||
|
|
|
@ -11,7 +11,7 @@ from .models import Feedback
|
||||||
|
|
||||||
class FeedbackCreate(LoginRequiredMixin, CampViewMixin, CreateView):
|
class FeedbackCreate(LoginRequiredMixin, CampViewMixin, CreateView):
|
||||||
model = Feedback
|
model = Feedback
|
||||||
fields = ['feedback']
|
fields = ["feedback"]
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
feedback = form.save(commit=False)
|
feedback = form.save(commit=False)
|
||||||
|
@ -20,14 +20,15 @@ class FeedbackCreate(LoginRequiredMixin, CampViewMixin, CreateView):
|
||||||
feedback.save()
|
feedback.save()
|
||||||
thanks_message = "Thank you! Your feedback is highly appreciated!"
|
thanks_message = "Thank you! Your feedback is highly appreciated!"
|
||||||
try:
|
try:
|
||||||
token = Token.objects.get(
|
token = Token.objects.get(camp=self.camp, description="Feedback thanks")
|
||||||
camp=self.camp,
|
thanks_message += " And for your efforts, here is a token: {}".format(
|
||||||
description="Feedback thanks"
|
token.token
|
||||||
)
|
)
|
||||||
thanks_message += " And for your efforts, here is a token: {}".format(token.token)
|
|
||||||
except Token.DoesNotExist:
|
except Token.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
messages.success(self.request, thanks_message)
|
messages.success(self.request, thanks_message)
|
||||||
|
|
||||||
return HttpResponseRedirect(reverse("feedback", kwargs={"camp_slug": self.camp.slug}))
|
return HttpResponseRedirect(
|
||||||
|
reverse("feedback", kwargs={"camp_slug": self.camp.slug})
|
||||||
|
)
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
from .models import (
|
from .models import InfoItem, InfoCategory
|
||||||
InfoItem,
|
|
||||||
InfoCategory
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(InfoItem)
|
@admin.register(InfoItem)
|
||||||
class InfoItemAdmin(VersionAdmin):
|
class InfoItemAdmin(VersionAdmin):
|
||||||
list_filter = ['category', 'category__team__camp',]
|
list_filter = ["category", "category__team__camp"]
|
||||||
list_display = ['headline',]
|
list_display = ["headline"]
|
||||||
|
|
||||||
|
|
||||||
class InfoItemInlineAdmin(admin.StackedInline):
|
class InfoItemInlineAdmin(admin.StackedInline):
|
||||||
model = InfoItem
|
model = InfoItem
|
||||||
list_filter = ['category', 'category__team__camp',]
|
list_filter = ["category", "category__team__camp"]
|
||||||
list_display = ['headline',]
|
list_display = ["headline"]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(InfoCategory)
|
@admin.register(InfoCategory)
|
||||||
class InfoCategorydmin(admin.ModelAdmin):
|
class InfoCategorydmin(admin.ModelAdmin):
|
||||||
list_filter = ['team__camp',]
|
list_filter = ["team__camp"]
|
||||||
list_display = ['headline',]
|
list_display = ["headline"]
|
||||||
search_fields = ['headline', 'body']
|
search_fields = ["headline", "body"]
|
||||||
inlines = [InfoItemInlineAdmin]
|
inlines = [InfoItemInlineAdmin]
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class InfoConfig(AppConfig):
|
class InfoConfig(AppConfig):
|
||||||
name = 'info'
|
name = "info"
|
||||||
|
|
|
@ -10,48 +10,107 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("camps", "0010_auto_20161220_1714")]
|
||||||
('camps', '0010_auto_20161220_1714'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='InfoCategory',
|
name="InfoCategory",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"id",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.AutoField(
|
||||||
('headline', models.CharField(help_text='The headline of this info category', max_length=100)),
|
auto_created=True,
|
||||||
('anchor', models.SlugField(help_text='The HTML anchor to use for this info category.')),
|
primary_key=True,
|
||||||
('weight', models.PositiveIntegerField(help_text='Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically.')),
|
serialize=False,
|
||||||
('camp', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='infocategories', to='camps.Camp')),
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"headline",
|
||||||
|
models.CharField(
|
||||||
|
help_text="The headline of this info category", max_length=100
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"anchor",
|
||||||
|
models.SlugField(
|
||||||
|
help_text="The HTML anchor to use for this info category."
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"weight",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
help_text="Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically."
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"camp",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="infocategories",
|
||||||
|
to="camps.Camp",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"ordering": ["-weight", "headline"]},
|
||||||
'ordering': ['-weight', 'headline'],
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='InfoItem',
|
name="InfoItem",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
"id",
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
models.AutoField(
|
||||||
('headline', models.CharField(help_text='Headline of this info item.', max_length=100)),
|
auto_created=True,
|
||||||
('anchor', models.SlugField(help_text='The HTML anchor to use for this info item.')),
|
primary_key=True,
|
||||||
('body', models.TextField(help_text='Body of this info item. Markdown is supported.')),
|
serialize=False,
|
||||||
('weight', models.PositiveIntegerField(help_text='Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically.')),
|
verbose_name="ID",
|
||||||
('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='infoitems', to='info.InfoCategory')),
|
),
|
||||||
|
),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"headline",
|
||||||
|
models.CharField(
|
||||||
|
help_text="Headline of this info item.", max_length=100
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"anchor",
|
||||||
|
models.SlugField(
|
||||||
|
help_text="The HTML anchor to use for this info item."
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"body",
|
||||||
|
models.TextField(
|
||||||
|
help_text="Body of this info item. Markdown is supported."
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"weight",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
help_text="Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically."
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"category",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="infoitems",
|
||||||
|
to="info.InfoCategory",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={"ordering": ["-weight", "headline"]},
|
||||||
'ordering': ['-weight', 'headline'],
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='infoitem',
|
name="infoitem",
|
||||||
unique_together=set([('headline', 'category'), ('anchor', 'category')]),
|
unique_together=set([("headline", "category"), ("anchor", "category")]),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='infocategory',
|
name="infocategory",
|
||||||
unique_together=set([('headline', 'camp'), ('anchor', 'camp')]),
|
unique_together=set([("headline", "camp"), ("anchor", "camp")]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,23 +7,30 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("info", "0001_initial")]
|
||||||
('info', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='infocategory',
|
name="infocategory",
|
||||||
options={'ordering': ['-weight', 'headline'], 'verbose_name_plural': 'Info Categories'},
|
options={
|
||||||
|
"ordering": ["-weight", "headline"],
|
||||||
|
"verbose_name_plural": "Info Categories",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='infocategory',
|
model_name="infocategory",
|
||||||
name='weight',
|
name="weight",
|
||||||
field=models.PositiveIntegerField(default=100, help_text='Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.'),
|
field=models.PositiveIntegerField(
|
||||||
|
default=100,
|
||||||
|
help_text="Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='infoitem',
|
model_name="infoitem",
|
||||||
name='weight',
|
name="weight",
|
||||||
field=models.PositiveIntegerField(default=100, help_text='Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.'),
|
field=models.PositiveIntegerField(
|
||||||
|
default=100,
|
||||||
|
help_text="Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,17 +7,17 @@ from django.db import migrations
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("info", "0002_auto_20161228_2312")]
|
||||||
('info', '0002_auto_20161228_2312'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='infocategory',
|
name="infocategory",
|
||||||
options={'ordering': ['weight', 'headline'], 'verbose_name_plural': 'Info Categories'},
|
options={
|
||||||
|
"ordering": ["weight", "headline"],
|
||||||
|
"verbose_name_plural": "Info Categories",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='infoitem',
|
name="infoitem", options={"ordering": ["weight", "headline"]}
|
||||||
options={'ordering': ['weight', 'headline']},
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,14 +7,20 @@ import django.db.models.deletion
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('teams', '0042_auto_20180413_1933'),
|
("teams", "0042_auto_20180413_1933"),
|
||||||
('info', '0003_auto_20170218_1148'),
|
("info", "0003_auto_20170218_1148"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='infocategory',
|
model_name="infocategory",
|
||||||
name='team',
|
name="team",
|
||||||
field=models.ForeignKey(blank=True, help_text='The team responsible for this info category.', null=True, on_delete=django.db.models.deletion.PROTECT, to='teams.Team'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="The team responsible for this info category.",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to="teams.Team",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -69,10 +69,6 @@ def add_teams_to_categories(apps, schema_editor):
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("info", "0004_infocategory_team")]
|
||||||
('info', '0004_infocategory_team'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [migrations.RunPython(add_teams_to_categories)]
|
||||||
migrations.RunPython(add_teams_to_categories)
|
|
||||||
]
|
|
||||||
|
|
|
@ -6,22 +6,21 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("info", "0005_add_teams_to_categories")]
|
||||||
('info', '0005_add_teams_to_categories'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='infocategory',
|
model_name="infocategory",
|
||||||
name='team',
|
name="team",
|
||||||
field=models.ForeignKey(blank=True, help_text='The team responsible for this info category.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='info_categories', to='teams.Team'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="The team responsible for this info category.",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="info_categories",
|
||||||
|
to="teams.Team",
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='infocategory',
|
|
||||||
unique_together=set(),
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='infocategory',
|
|
||||||
name='camp',
|
|
||||||
),
|
),
|
||||||
|
migrations.AlterUniqueTogether(name="infocategory", unique_together=set()),
|
||||||
|
migrations.RemoveField(model_name="infocategory", name="camp"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,14 +6,17 @@ import django.db.models.deletion
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [("info", "0006_auto_20180520_1113")]
|
||||||
('info', '0006_auto_20180520_1113'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='infocategory',
|
model_name="infocategory",
|
||||||
name='team',
|
name="team",
|
||||||
field=models.ForeignKey(help_text='The team responsible for this info category.', on_delete=django.db.models.deletion.PROTECT, related_name='info_categories', to='teams.Team'),
|
field=models.ForeignKey(
|
||||||
|
help_text="The team responsible for this info category.",
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="info_categories",
|
||||||
|
to="teams.Team",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,12 +7,11 @@ import reversion
|
||||||
|
|
||||||
class InfoCategory(CampRelatedModel):
|
class InfoCategory(CampRelatedModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['weight', 'headline']
|
ordering = ["weight", "headline"]
|
||||||
verbose_name_plural = "Info Categories"
|
verbose_name_plural = "Info Categories"
|
||||||
|
|
||||||
headline = models.CharField(
|
headline = models.CharField(
|
||||||
max_length=100,
|
max_length=100, help_text="The headline of this info category"
|
||||||
help_text="The headline of this info category"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
anchor = models.SlugField(
|
anchor = models.SlugField(
|
||||||
|
@ -20,62 +19,55 @@ class InfoCategory(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
weight = models.PositiveIntegerField(
|
weight = models.PositiveIntegerField(
|
||||||
help_text='Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.',
|
help_text="Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.",
|
||||||
default=100,
|
default=100,
|
||||||
)
|
)
|
||||||
|
|
||||||
team = models.ForeignKey(
|
team = models.ForeignKey(
|
||||||
'teams.Team',
|
"teams.Team",
|
||||||
help_text='The team responsible for this info category.',
|
help_text="The team responsible for this info category.",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='info_categories'
|
related_name="info_categories",
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if InfoItem.objects.filter(category__team__camp=self.camp, anchor=self.anchor).exists():
|
if InfoItem.objects.filter(
|
||||||
|
category__team__camp=self.camp, anchor=self.anchor
|
||||||
|
).exists():
|
||||||
# this anchor is already in use on an item, so it cannot be used (must be unique on the page)
|
# this anchor is already in use on an item, so it cannot be used (must be unique on the page)
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
{'anchor': 'Anchor is already in use on an info item for this camp'}
|
{"anchor": "Anchor is already in use on an info item for this camp"}
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def camp(self):
|
def camp(self):
|
||||||
return self.team.camp
|
return self.team.camp
|
||||||
|
|
||||||
camp_filter = 'team__camp'
|
camp_filter = "team__camp"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s (%s)' % (self.headline, self.camp)
|
return "%s (%s)" % (self.headline, self.camp)
|
||||||
|
|
||||||
|
|
||||||
# We want to have info items under version control
|
# We want to have info items under version control
|
||||||
@reversion.register()
|
@reversion.register()
|
||||||
class InfoItem(CampRelatedModel):
|
class InfoItem(CampRelatedModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['weight', 'headline']
|
ordering = ["weight", "headline"]
|
||||||
unique_together = (('anchor', 'category'), ('headline', 'category'))
|
unique_together = (("anchor", "category"), ("headline", "category"))
|
||||||
|
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
'info.InfoCategory',
|
"info.InfoCategory", related_name="infoitems", on_delete=models.PROTECT
|
||||||
related_name='infoitems',
|
|
||||||
on_delete=models.PROTECT
|
|
||||||
)
|
)
|
||||||
|
|
||||||
headline = models.CharField(
|
headline = models.CharField(max_length=100, help_text="Headline of this info item.")
|
||||||
max_length=100,
|
|
||||||
help_text="Headline of this info item."
|
|
||||||
)
|
|
||||||
|
|
||||||
anchor = models.SlugField(
|
anchor = models.SlugField(help_text="The HTML anchor to use for this info item.")
|
||||||
help_text="The HTML anchor to use for this info item."
|
|
||||||
)
|
|
||||||
|
|
||||||
body = models.TextField(
|
body = models.TextField(help_text="Body of this info item. Markdown is supported.")
|
||||||
help_text='Body of this info item. Markdown is supported.'
|
|
||||||
)
|
|
||||||
|
|
||||||
weight = models.PositiveIntegerField(
|
weight = models.PositiveIntegerField(
|
||||||
help_text='Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.',
|
help_text="Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.",
|
||||||
default=100,
|
default=100,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,12 +75,19 @@ class InfoItem(CampRelatedModel):
|
||||||
def camp(self):
|
def camp(self):
|
||||||
return self.category.camp
|
return self.category.camp
|
||||||
|
|
||||||
camp_filter = 'category__team__camp'
|
camp_filter = "category__team__camp"
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if hasattr(self, 'category') and InfoCategory.objects.filter(team__camp=self.category.team.camp, anchor=self.anchor).exists():
|
if (
|
||||||
|
hasattr(self, "category")
|
||||||
|
and InfoCategory.objects.filter(
|
||||||
|
team__camp=self.category.team.camp, anchor=self.anchor
|
||||||
|
).exists()
|
||||||
|
):
|
||||||
# this anchor is already in use on a category, so it cannot be used here (they must be unique on the entire page)
|
# this anchor is already in use on a category, so it cannot be used here (they must be unique on the entire page)
|
||||||
raise ValidationError({'anchor': 'Anchor is already in use on an info category for this camp'})
|
raise ValidationError(
|
||||||
|
{"anchor": "Anchor is already in use on an info category for this camp"}
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s (%s)' % (self.headline, self.category)
|
return "%s (%s)" % (self.headline, self.category)
|
||||||
|
|
|
@ -2,13 +2,13 @@ from django.views.generic import ListView
|
||||||
from .models import *
|
from .models import *
|
||||||
from camps.mixins import CampViewMixin
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
|
|
||||||
class CampInfoView(CampViewMixin, ListView):
|
class CampInfoView(CampViewMixin, ListView):
|
||||||
model = InfoCategory
|
model = InfoCategory
|
||||||
template_name = 'info.html'
|
template_name = "info.html"
|
||||||
context_object_name = 'categories'
|
context_object_name = "categories"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super(CampInfoView, self).get_queryset()
|
queryset = super(CampInfoView, self).get_queryset()
|
||||||
# do not show categories with 0 items
|
# do not show categories with 0 items
|
||||||
return queryset.exclude(infoitems__isnull=True)
|
return queryset.exclude(infoitems__isnull=True)
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue