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):
|
||||
name = 'backoffice'
|
||||
name = "backoffice"
|
||||
|
|
|
@ -5,6 +5,7 @@ class OrgaTeamPermissionMixin(RaisePermissionRequiredMixin):
|
|||
"""
|
||||
Permission mixin for views used by Orga Team
|
||||
"""
|
||||
|
||||
permission_required = ("camps.backoffice_permission", "camps.orgateam_permission")
|
||||
|
||||
|
||||
|
@ -12,13 +13,18 @@ class EconomyTeamPermissionMixin(RaisePermissionRequiredMixin):
|
|||
"""
|
||||
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):
|
||||
"""
|
||||
Permission mixin for views used by Info Team/InfoDesk
|
||||
"""
|
||||
|
||||
permission_required = ("camps.backoffice_permission", "camps.infoteam_permission")
|
||||
|
||||
|
||||
|
@ -26,5 +32,8 @@ class ContentTeamPermissionMixin(RaisePermissionRequiredMixin):
|
|||
"""
|
||||
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 *
|
||||
|
||||
|
||||
app_name = 'backoffice'
|
||||
app_name = "backoffice"
|
||||
|
||||
urlpatterns = [
|
||||
path('', BackofficeIndexView.as_view(), name='index'),
|
||||
path("", BackofficeIndexView.as_view(), name="index"),
|
||||
# infodesk
|
||||
path('product_handout/', ProductHandoutView.as_view(), name='product_handout'),
|
||||
path('badge_handout/', BadgeHandoutView.as_view(), name='badge_handout'),
|
||||
path('ticket_checkin/', TicketCheckinView.as_view(), name='ticket_checkin'),
|
||||
|
||||
path("product_handout/", ProductHandoutView.as_view(), name="product_handout"),
|
||||
path("badge_handout/", BadgeHandoutView.as_view(), name="badge_handout"),
|
||||
path("ticket_checkin/", TicketCheckinView.as_view(), name="ticket_checkin"),
|
||||
# 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
|
||||
path('merchandise_orders/', MerchandiseOrdersView.as_view(), name='merchandise_orders'),
|
||||
path('merchandise_to_order/', MerchandiseToOrderView.as_view(), name='merchandise_to_order'),
|
||||
|
||||
path(
|
||||
"merchandise_orders/",
|
||||
MerchandiseOrdersView.as_view(),
|
||||
name="merchandise_orders",
|
||||
),
|
||||
path(
|
||||
"merchandise_to_order/",
|
||||
MerchandiseToOrderView.as_view(),
|
||||
name="merchandise_to_order",
|
||||
),
|
||||
# village orders
|
||||
path('village_orders/', VillageOrdersView.as_view(), name='village_orders'),
|
||||
path('village_to_order/', VillageToOrderView.as_view(), name='village_to_order'),
|
||||
|
||||
path("village_orders/", VillageOrdersView.as_view(), name="village_orders"),
|
||||
path("village_to_order/", VillageToOrderView.as_view(), name="village_to_order"),
|
||||
# manage proposals
|
||||
path('manage_proposals/', include([
|
||||
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'),
|
||||
])),
|
||||
|
||||
path(
|
||||
"manage_proposals/",
|
||||
include(
|
||||
[
|
||||
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
|
||||
path('economy/',
|
||||
include([
|
||||
# chains & credebtors
|
||||
path('chains/',
|
||||
include([
|
||||
path(
|
||||
'',
|
||||
ChainListView.as_view(),
|
||||
name='chain_list'
|
||||
),
|
||||
path('<slug:chain_slug>/',
|
||||
include([
|
||||
path(
|
||||
"economy/",
|
||||
include(
|
||||
[
|
||||
# chains & credebtors
|
||||
path(
|
||||
"chains/",
|
||||
include(
|
||||
[
|
||||
path("", ChainListView.as_view(), name="chain_list"),
|
||||
path(
|
||||
'',
|
||||
ChainDetailView.as_view(),
|
||||
name='chain_detail'
|
||||
"<slug:chain_slug>/",
|
||||
include(
|
||||
[
|
||||
path(
|
||||
"",
|
||||
ChainDetailView.as_view(),
|
||||
name="chain_detail",
|
||||
),
|
||||
path(
|
||||
"<slug:credebtor_slug>/",
|
||||
CredebtorDetailView.as_view(),
|
||||
name="credebtor_detail",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
# expenses
|
||||
path(
|
||||
"expenses/",
|
||||
include(
|
||||
[
|
||||
path("", ExpenseListView.as_view(), name="expense_list"),
|
||||
path(
|
||||
"<uuid:pk>/",
|
||||
ExpenseDetailView.as_view(),
|
||||
name="expense_detail",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
# revenues
|
||||
path(
|
||||
"revenues/",
|
||||
include(
|
||||
[
|
||||
path("", RevenueListView.as_view(), name="revenue_list"),
|
||||
path(
|
||||
"<uuid:pk>/",
|
||||
RevenueDetailView.as_view(),
|
||||
name="revenue_detail",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
# reimbursements
|
||||
path(
|
||||
"reimbursements/",
|
||||
include(
|
||||
[
|
||||
path(
|
||||
"",
|
||||
ReimbursementListView.as_view(),
|
||||
name="reimbursement_list",
|
||||
),
|
||||
path(
|
||||
'<slug:credebtor_slug>/',
|
||||
CredebtorDetailView.as_view(),
|
||||
name='credebtor_detail'
|
||||
"<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",
|
||||
),
|
||||
]
|
||||
),
|
||||
]),
|
||||
),
|
||||
|
||||
# expenses
|
||||
path('expenses/',
|
||||
include([
|
||||
path('', ExpenseListView.as_view(), name='expense_list'),
|
||||
path('<uuid:pk>/', ExpenseDetailView.as_view(), name='expense_detail'),
|
||||
]),
|
||||
),
|
||||
|
||||
# revenues
|
||||
path('revenues/',
|
||||
include([
|
||||
path('', RevenueListView.as_view(), name='revenue_list'),
|
||||
path('<uuid:pk>/', RevenueDetailView.as_view(), name='revenue_detail'),
|
||||
]),
|
||||
),
|
||||
|
||||
# reimbursements
|
||||
path('reimbursements/',
|
||||
include([
|
||||
path('', ReimbursementListView.as_view(), name='reimbursement_list'),
|
||||
path('<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
|
||||
"""
|
||||
permission_required = ("camps.backoffice_permission")
|
||||
|
||||
permission_required = "camps.backoffice_permission"
|
||||
template_name = "index.html"
|
||||
|
||||
|
||||
|
@ -42,13 +43,13 @@ class ProductHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
|||
handed_out=False,
|
||||
order__paid=True,
|
||||
order__refunded=False,
|
||||
order__cancelled=False
|
||||
).order_by('order')
|
||||
order__cancelled=False,
|
||||
).order_by("order")
|
||||
|
||||
|
||||
class BadgeHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
||||
template_name = "badge_handout.html"
|
||||
context_object_name = 'tickets'
|
||||
context_object_name = "tickets"
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
shoptickets = ShopTicket.objects.filter(badge_handed_out=False)
|
||||
|
@ -59,7 +60,7 @@ class BadgeHandoutView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
|||
|
||||
class TicketCheckinView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
||||
template_name = "ticket_checkin.html"
|
||||
context_object_name = 'tickets'
|
||||
context_object_name = "tickets"
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
shoptickets = ShopTicket.objects.filter(checked_in=False)
|
||||
|
@ -70,30 +71,31 @@ class TicketCheckinView(CampViewMixin, InfoTeamPermissionMixin, ListView):
|
|||
|
||||
class ApproveNamesView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
||||
template_name = "approve_public_credit_names.html"
|
||||
context_object_name = 'profiles'
|
||||
context_object_name = "profiles"
|
||||
|
||||
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):
|
||||
"""
|
||||
This view shows a list of pending SpeakerProposal and EventProposals.
|
||||
"""
|
||||
|
||||
template_name = "manage_proposals.html"
|
||||
context_object_name = 'speakerproposals'
|
||||
context_object_name = "speakerproposals"
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
return SpeakerProposal.objects.filter(
|
||||
camp=self.camp,
|
||||
proposal_status=SpeakerProposal.PROPOSAL_PENDING
|
||||
camp=self.camp, proposal_status=SpeakerProposal.PROPOSAL_PENDING
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['eventproposals'] = EventProposal.objects.filter(
|
||||
track__camp=self.camp,
|
||||
proposal_status=EventProposal.PROPOSAL_PENDING
|
||||
context["eventproposals"] = EventProposal.objects.filter(
|
||||
track__camp=self.camp, proposal_status=EventProposal.PROPOSAL_PENDING
|
||||
)
|
||||
return context
|
||||
|
||||
|
@ -102,6 +104,7 @@ class ProposalManageBaseView(CampViewMixin, ContentTeamPermissionMixin, UpdateVi
|
|||
"""
|
||||
This class contains the shared logic between SpeakerProposalManageView and EventProposalManageView
|
||||
"""
|
||||
|
||||
fields = []
|
||||
|
||||
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
|
||||
"""
|
||||
logger.debug(form.data)
|
||||
if 'approve' in form.data:
|
||||
if "approve" in form.data:
|
||||
# approve button was pressed
|
||||
form.instance.mark_as_approved(self.request)
|
||||
elif 'reject' in form.data:
|
||||
elif "reject" in form.data:
|
||||
# reject button was pressed
|
||||
form.instance.mark_as_rejected(self.request)
|
||||
else:
|
||||
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):
|
||||
"""
|
||||
This view allows an admin to approve/reject SpeakerProposals
|
||||
"""
|
||||
|
||||
model = SpeakerProposal
|
||||
template_name = "manage_speakerproposal.html"
|
||||
|
||||
|
@ -132,6 +138,7 @@ class EventProposalManageView(ProposalManageBaseView):
|
|||
"""
|
||||
This view allows an admin to approve/reject EventProposals
|
||||
"""
|
||||
|
||||
model = EventProposal
|
||||
template_name = "manage_eventproposal.html"
|
||||
|
||||
|
@ -140,34 +147,34 @@ class MerchandiseOrdersView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
|||
template_name = "orders_merchandise.html"
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
camp_prefix = 'BornHack {}'.format(timezone.now().year)
|
||||
camp_prefix = "BornHack {}".format(timezone.now().year)
|
||||
|
||||
return OrderProductRelation.objects.filter(
|
||||
handed_out=False,
|
||||
order__paid=True,
|
||||
order__refunded=False,
|
||||
order__cancelled=False,
|
||||
product__category__name='Merchandise',
|
||||
).filter(
|
||||
product__name__startswith=camp_prefix
|
||||
).order_by('order')
|
||||
return (
|
||||
OrderProductRelation.objects.filter(
|
||||
handed_out=False,
|
||||
order__paid=True,
|
||||
order__refunded=False,
|
||||
order__cancelled=False,
|
||||
product__category__name="Merchandise",
|
||||
)
|
||||
.filter(product__name__startswith=camp_prefix)
|
||||
.order_by("order")
|
||||
)
|
||||
|
||||
|
||||
class MerchandiseToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
||||
template_name = "merchandise_to_order.html"
|
||||
|
||||
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(
|
||||
handed_out=False,
|
||||
order__paid=True,
|
||||
order__refunded=False,
|
||||
order__cancelled=False,
|
||||
product__category__name='Merchandise',
|
||||
).filter(
|
||||
product__name__startswith=camp_prefix
|
||||
)
|
||||
product__category__name="Merchandise",
|
||||
).filter(product__name__startswith=camp_prefix)
|
||||
|
||||
merchandise_orders = {}
|
||||
for relation in order_relations:
|
||||
|
@ -178,7 +185,7 @@ class MerchandiseToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateVie
|
|||
merchandise_orders[relation.product.name] = relation.quantity
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['merchandise'] = merchandise_orders
|
||||
context["merchandise"] = merchandise_orders
|
||||
return context
|
||||
|
||||
|
||||
|
@ -186,34 +193,34 @@ class VillageOrdersView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
|||
template_name = "orders_village.html"
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
camp_prefix = 'BornHack {}'.format(timezone.now().year)
|
||||
camp_prefix = "BornHack {}".format(timezone.now().year)
|
||||
|
||||
return OrderProductRelation.objects.filter(
|
||||
handed_out=False,
|
||||
order__paid=True,
|
||||
order__refunded=False,
|
||||
order__cancelled=False,
|
||||
product__category__name='Villages',
|
||||
).filter(
|
||||
product__name__startswith=camp_prefix
|
||||
).order_by('order')
|
||||
return (
|
||||
OrderProductRelation.objects.filter(
|
||||
handed_out=False,
|
||||
order__paid=True,
|
||||
order__refunded=False,
|
||||
order__cancelled=False,
|
||||
product__category__name="Villages",
|
||||
)
|
||||
.filter(product__name__startswith=camp_prefix)
|
||||
.order_by("order")
|
||||
)
|
||||
|
||||
|
||||
class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
||||
template_name = "village_to_order.html"
|
||||
|
||||
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(
|
||||
handed_out=False,
|
||||
order__paid=True,
|
||||
order__refunded=False,
|
||||
order__cancelled=False,
|
||||
product__category__name='Villages',
|
||||
).filter(
|
||||
product__name__startswith=camp_prefix
|
||||
)
|
||||
product__category__name="Villages",
|
||||
).filter(product__name__startswith=camp_prefix)
|
||||
|
||||
village_orders = {}
|
||||
for relation in order_relations:
|
||||
|
@ -224,7 +231,7 @@ class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
|||
village_orders[relation.product.name] = relation.quantity
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['village'] = village_orders
|
||||
context["village"] = village_orders
|
||||
return context
|
||||
|
||||
|
||||
|
@ -234,27 +241,28 @@ class VillageToOrderView(CampViewMixin, OrgaTeamPermissionMixin, TemplateView):
|
|||
|
||||
class ChainListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||
model = Chain
|
||||
template_name = 'chain_list_backoffice.html'
|
||||
template_name = "chain_list_backoffice.html"
|
||||
|
||||
|
||||
class ChainDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
||||
model = Chain
|
||||
template_name = 'chain_detail_backoffice.html'
|
||||
slug_url_kwarg = 'chain_slug'
|
||||
template_name = "chain_detail_backoffice.html"
|
||||
slug_url_kwarg = "chain_slug"
|
||||
|
||||
|
||||
class CredebtorDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
||||
model = Credebtor
|
||||
template_name = 'credebtor_detail_backoffice.html'
|
||||
slug_url_kwarg = 'credebtor_slug'
|
||||
template_name = "credebtor_detail_backoffice.html"
|
||||
slug_url_kwarg = "credebtor_slug"
|
||||
|
||||
|
||||
################################
|
||||
########### EXPENSES ###########
|
||||
|
||||
|
||||
class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||
model = Expense
|
||||
template_name = 'expense_list_backoffice.html'
|
||||
template_name = "expense_list_backoffice.html"
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
"""
|
||||
|
@ -268,46 +276,53 @@ class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
|||
Include unapproved expenses seperately
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
class ExpenseDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
||||
model = Expense
|
||||
template_name = 'expense_detail_backoffice.html'
|
||||
fields = ['notes']
|
||||
template_name = "expense_detail_backoffice.html"
|
||||
fields = ["notes"]
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
We have two submit buttons in this form, Approve and Reject
|
||||
"""
|
||||
expense = form.save()
|
||||
if 'approve' in form.data:
|
||||
if "approve" in form.data:
|
||||
# approve button was pressed
|
||||
expense.approve(self.request)
|
||||
elif 'reject' in form.data:
|
||||
elif "reject" in form.data:
|
||||
# reject button was pressed
|
||||
expense.reject(self.request)
|
||||
else:
|
||||
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 ###########
|
||||
|
||||
|
||||
class ReimbursementListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||
model = Reimbursement
|
||||
template_name = 'reimbursement_list_backoffice.html'
|
||||
template_name = "reimbursement_list_backoffice.html"
|
||||
|
||||
|
||||
class ReimbursementDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
|
||||
model = Reimbursement
|
||||
template_name = 'reimbursement_detail_backoffice.html'
|
||||
template_name = "reimbursement_detail_backoffice.html"
|
||||
|
||||
|
||||
class ReimbursementCreateUserSelectView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||
template_name = 'reimbursement_create_userselect.html'
|
||||
class ReimbursementCreateUserSelectView(
|
||||
CampViewMixin, EconomyTeamPermissionMixin, ListView
|
||||
):
|
||||
template_name = "reimbursement_create_userselect.html"
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = User.objects.filter(
|
||||
|
@ -316,20 +331,22 @@ class ReimbursementCreateUserSelectView(CampViewMixin, EconomyTeamPermissionMixi
|
|||
reimbursement__isnull=True,
|
||||
paid_by_bornhack=False,
|
||||
approved=True,
|
||||
).values_list('user', flat=True).distinct()
|
||||
)
|
||||
.values_list("user", flat=True)
|
||||
.distinct()
|
||||
)
|
||||
return queryset
|
||||
|
||||
|
||||
class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateView):
|
||||
model = Reimbursement
|
||||
template_name = 'reimbursement_create.html'
|
||||
fields = ['notes', 'paid']
|
||||
template_name = "reimbursement_create.html"
|
||||
fields = ["notes", "paid"]
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
""" Get the user from kwargs """
|
||||
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
|
||||
response = super().dispatch(request, *args, **kwargs)
|
||||
|
@ -339,21 +356,27 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
|
|||
|
||||
def get(self, request, *args, **kwargs):
|
||||
# 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):
|
||||
messages.error(request, "This user has no approved and unreimbursed expenses!")
|
||||
return(redirect(reverse('backoffice:index', kwargs={'camp_slug': self.camp.slug})))
|
||||
if not self.reimbursement_user.expenses.filter(
|
||||
reimbursement__isnull=True, approved=True, paid_by_bornhack=False
|
||||
):
|
||||
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)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['expenses'] = Expense.objects.filter(
|
||||
context["expenses"] = Expense.objects.filter(
|
||||
user=self.reimbursement_user,
|
||||
approved=True,
|
||||
reimbursement__isnull=True,
|
||||
paid_by_bornhack=False,
|
||||
)
|
||||
context['total_amount'] = context['expenses'].aggregate(Sum('amount'))
|
||||
context['reimbursement_user'] = self.reimbursement_user
|
||||
context["total_amount"] = context["expenses"].aggregate(Sum("amount"))
|
||||
context["reimbursement_user"] = self.reimbursement_user
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -361,17 +384,34 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
|
|||
Set user and camp for the Reimbursement before saving
|
||||
"""
|
||||
# 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:
|
||||
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
|
||||
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:
|
||||
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
|
||||
reimbursement = form.save(commit=False)
|
||||
|
@ -387,50 +427,83 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
|
|||
|
||||
# create expense for this reimbursement
|
||||
expense = Expense()
|
||||
expense.camp=self.camp
|
||||
expense.user=self.request.user
|
||||
expense.amount=reimbursement.amount
|
||||
expense.description="Payment of reimbursement %s to %s" % (reimbursement.pk, reimbursement.reimbursement_user)
|
||||
expense.paid_by_bornhack=True
|
||||
expense.responsible_team=economyteam
|
||||
expense.approved=True
|
||||
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.camp = self.camp
|
||||
expense.user = self.request.user
|
||||
expense.amount = reimbursement.amount
|
||||
expense.description = "Payment of reimbursement %s to %s" % (
|
||||
reimbursement.pk,
|
||||
reimbursement.reimbursement_user,
|
||||
)
|
||||
expense.paid_by_bornhack = True
|
||||
expense.responsible_team = economyteam
|
||||
expense.approved = True
|
||||
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()
|
||||
|
||||
messages.success(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}))
|
||||
messages.success(
|
||||
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):
|
||||
model = Reimbursement
|
||||
template_name = 'reimbursement_form.html'
|
||||
fields = ['notes', 'paid']
|
||||
template_name = "reimbursement_form.html"
|
||||
fields = ["notes", "paid"]
|
||||
|
||||
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):
|
||||
model = Reimbursement
|
||||
template_name = 'reimbursement_delete.html'
|
||||
fields = ['notes', 'paid']
|
||||
template_name = "reimbursement_delete.html"
|
||||
fields = ["notes", "paid"]
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
response = super().dispatch(request, *args, **kwargs)
|
||||
if self.get_object().paid:
|
||||
messages.error(request, "This reimbursement has already been paid so it cannot be deleted")
|
||||
return redirect(reverse('backoffice:reimbursement_list', kwargs={'camp_slug': self.camp.slug}))
|
||||
messages.error(
|
||||
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
|
||||
|
||||
|
||||
################################
|
||||
########### REVENUES ###########
|
||||
|
||||
|
||||
class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
||||
model = Revenue
|
||||
template_name = 'revenue_list_backoffice.html'
|
||||
template_name = "revenue_list_backoffice.html"
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
"""
|
||||
|
@ -444,27 +517,30 @@ class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
|
|||
Include unapproved revenues seperately
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
||||
model = Revenue
|
||||
template_name = 'revenue_detail_backoffice.html'
|
||||
fields = ['notes']
|
||||
template_name = "revenue_detail_backoffice.html"
|
||||
fields = ["notes"]
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
We have two submit buttons in this form, Approve and Reject
|
||||
"""
|
||||
revenue = form.save()
|
||||
if 'approve' in form.data:
|
||||
if "approve" in form.data:
|
||||
# approve button was pressed
|
||||
revenue.approve(self.request)
|
||||
elif 'reject' in form.data:
|
||||
elif "reject" in form.data:
|
||||
# reject button was pressed
|
||||
revenue.reject(self.request)
|
||||
else:
|
||||
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)
|
||||
class ProductAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'price', 'category', 'in_stock']
|
||||
list_editable = ['in_stock']
|
||||
list_display = ["name", "price", "category", "in_stock"]
|
||||
list_editable = ["in_stock"]
|
||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class BarConfig(AppConfig):
|
||||
name = 'bar'
|
||||
name = "bar"
|
||||
|
|
|
@ -10,36 +10,55 @@ class Migration(migrations.Migration):
|
|||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('camps', '0022_camp_colour'),
|
||||
]
|
||||
dependencies = [("camps", "0022_camp_colour")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Product',
|
||||
name="Product",
|
||||
fields=[
|
||||
('id', models.AutoField(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)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
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(
|
||||
name='ProductCategory',
|
||||
name="ProductCategory",
|
||||
fields=[
|
||||
('id', 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)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('camp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camps.Camp')),
|
||||
(
|
||||
"id",
|
||||
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)),
|
||||
("name", models.CharField(max_length=255)),
|
||||
(
|
||||
"camp",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="camps.Camp"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
options={"abstract": False},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='product',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bar.ProductCategory'),
|
||||
model_name="product",
|
||||
name="category",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('bar', '0001_initial'),
|
||||
]
|
||||
dependencies = [("bar", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(name="product", options={"ordering": ("name",)}),
|
||||
migrations.AlterModelOptions(
|
||||
name='product',
|
||||
options={'ordering': ('name',)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='productcategory',
|
||||
options={'ordering': ('name',)},
|
||||
name="productcategory", options={"ordering": ("name",)}
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='bar.ProductCategory'),
|
||||
model_name="product",
|
||||
name="category",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('bar', '0002_auto_20170916_2128'),
|
||||
]
|
||||
dependencies = [("bar", "0002_auto_20170916_2128")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='products', to='bar.ProductCategory'),
|
||||
model_name="product",
|
||||
name="category",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="products",
|
||||
to="bar.ProductCategory",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productcategory',
|
||||
name='camp',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='camps.Camp'),
|
||||
model_name="productcategory",
|
||||
name="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):
|
||||
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):
|
||||
return self.name
|
||||
|
@ -17,9 +17,7 @@ class Product(models.Model):
|
|||
name = models.CharField(max_length=255)
|
||||
price = models.IntegerField()
|
||||
category = models.ForeignKey(
|
||||
ProductCategory,
|
||||
related_name="products",
|
||||
on_delete=models.PROTECT
|
||||
ProductCategory, related_name="products", on_delete=models.PROTECT
|
||||
)
|
||||
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")
|
||||
django.setup()
|
||||
application = get_default_application()
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ from channels.auth import AuthMiddlewareStack
|
|||
from program.consumers import ScheduleConsumer
|
||||
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
"websocket": AuthMiddlewareStack(
|
||||
URLRouter([
|
||||
url(r"^schedule/", ScheduleConsumer)
|
||||
])
|
||||
)
|
||||
})
|
||||
application = ProtocolTypeRouter(
|
||||
{
|
||||
"websocket": AuthMiddlewareStack(
|
||||
URLRouter([url(r"^schedule/", ScheduleConsumer)])
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -11,19 +11,17 @@ from program.schema import ProgramQuery
|
|||
class CampNode(DjangoObjectType):
|
||||
class Meta:
|
||||
model = Camp
|
||||
interfaces = (relay.Node, )
|
||||
filter_fields = {
|
||||
'title': ['icontains', 'iexact'],
|
||||
}
|
||||
interfaces = (relay.Node,)
|
||||
filter_fields = {"title": ["icontains", "iexact"]}
|
||||
only_fields = (
|
||||
'title',
|
||||
'slug',
|
||||
'tagline',
|
||||
'shortslug',
|
||||
'buildup',
|
||||
'camp',
|
||||
'teardown',
|
||||
'colour',
|
||||
"title",
|
||||
"slug",
|
||||
"tagline",
|
||||
"shortslug",
|
||||
"buildup",
|
||||
"camp",
|
||||
"teardown",
|
||||
"colour",
|
||||
)
|
||||
|
||||
def resolve_buildup(self, info):
|
||||
|
|
|
@ -2,6 +2,7 @@ import os
|
|||
|
||||
from .environment_settings import *
|
||||
|
||||
|
||||
def local_dir(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)
|
||||
|
||||
urlpatterns = [
|
||||
path('profile/', include('allauth.urls')),
|
||||
path('profile/', include('allauth_2fa.urls')),
|
||||
path('profile/', include('profiles.urls', namespace='profiles')),
|
||||
path("profile/", include("allauth.urls")),
|
||||
path("profile/", include("allauth_2fa.urls")),
|
||||
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(
|
||||
'tickets/',
|
||||
include('tickets.urls', namespace='tickets')
|
||||
"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(
|
||||
'shop/',
|
||||
include('shop.urls', namespace='shop')
|
||||
"general-terms-and-conditions/",
|
||||
TemplateView.as_view(template_name="legal/general_terms_and_conditions.html"),
|
||||
name="general-terms",
|
||||
),
|
||||
path(
|
||||
'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),
|
||||
|
||||
path("admin/", admin.site.urls),
|
||||
# We don't need CSRF checks for the API
|
||||
path('api/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
|
||||
|
||||
path(
|
||||
'camps/',
|
||||
CampListView.as_view(),
|
||||
name='camp_list'
|
||||
),
|
||||
|
||||
path(
|
||||
'token/',
|
||||
include('tokens.urls', namespace='tokens'),
|
||||
),
|
||||
|
||||
path("api/", csrf_exempt(GraphQLView.as_view(graphiql=True))),
|
||||
path("camps/", CampListView.as_view(), name="camp_list"),
|
||||
path("token/", include("tokens.urls", namespace="tokens")),
|
||||
# camp redirect views here
|
||||
|
||||
path(
|
||||
'',
|
||||
"",
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'camp_detail'},
|
||||
name='camp_detail_redirect',
|
||||
kwargs={"page": "camp_detail"},
|
||||
name="camp_detail_redirect",
|
||||
),
|
||||
|
||||
path(
|
||||
'program/',
|
||||
"program/",
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'schedule_index'},
|
||||
name='schedule_index_redirect',
|
||||
kwargs={"page": "schedule_index"},
|
||||
name="schedule_index_redirect",
|
||||
),
|
||||
|
||||
path(
|
||||
'info/',
|
||||
"info/",
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'info'},
|
||||
name='info_redirect',
|
||||
kwargs={"page": "info"},
|
||||
name="info_redirect",
|
||||
),
|
||||
|
||||
path(
|
||||
'sponsors/',
|
||||
"sponsors/",
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'sponsors'},
|
||||
name='sponsors_redirect',
|
||||
kwargs={"page": "sponsors"},
|
||||
name="sponsors_redirect",
|
||||
),
|
||||
|
||||
path(
|
||||
'villages/',
|
||||
"villages/",
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'village_list'},
|
||||
name='village_list_redirect',
|
||||
kwargs={"page": "village_list"},
|
||||
name="village_list_redirect",
|
||||
),
|
||||
|
||||
path(
|
||||
'people/',
|
||||
PeopleView.as_view(),
|
||||
name='people',
|
||||
),
|
||||
|
||||
path("people/", PeopleView.as_view(), name="people"),
|
||||
# camp specific urls below here
|
||||
|
||||
path(
|
||||
'<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(
|
||||
'villages/', include([
|
||||
path(
|
||||
'',
|
||||
VillageListView.as_view(),
|
||||
name='village_list'
|
||||
"<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(
|
||||
"villages/",
|
||||
include(
|
||||
[
|
||||
path("", VillageListView.as_view(), name="village_list"),
|
||||
path(
|
||||
"create/",
|
||||
VillageCreateView.as_view(),
|
||||
name="village_create",
|
||||
),
|
||||
path(
|
||||
"<slug:slug>/delete/",
|
||||
VillageDeleteView.as_view(),
|
||||
name="village_delete",
|
||||
),
|
||||
path(
|
||||
"<slug:slug>/edit/",
|
||||
VillageUpdateView.as_view(),
|
||||
name="village_update",
|
||||
),
|
||||
# this has to be the last url in the list
|
||||
path(
|
||||
"<slug:slug>/",
|
||||
VillageDetailView.as_view(),
|
||||
name="village_detail",
|
||||
),
|
||||
]
|
||||
),
|
||||
path(
|
||||
'create/',
|
||||
VillageCreateView.as_view(),
|
||||
name='village_create'
|
||||
),
|
||||
path(
|
||||
'<slug:slug>/delete/',
|
||||
VillageDeleteView.as_view(),
|
||||
name='village_delete'
|
||||
),
|
||||
path(
|
||||
'<slug:slug>/edit/',
|
||||
VillageUpdateView.as_view(),
|
||||
name='village_update'
|
||||
),
|
||||
# this has to be the last url in the list
|
||||
path(
|
||||
'<slug:slug>/',
|
||||
VillageDetailView.as_view(),
|
||||
name='village_detail'
|
||||
),
|
||||
])
|
||||
),
|
||||
|
||||
path(
|
||||
'teams/',
|
||||
include('teams.urls', namespace='teams')
|
||||
),
|
||||
|
||||
path(
|
||||
'rideshare/',
|
||||
include('rideshare.urls', namespace='rideshare')
|
||||
),
|
||||
|
||||
path(
|
||||
'backoffice/',
|
||||
include('backoffice.urls', namespace='backoffice')
|
||||
),
|
||||
|
||||
path(
|
||||
'feedback/',
|
||||
FeedbackCreate.as_view(),
|
||||
name='feedback'
|
||||
),
|
||||
|
||||
path(
|
||||
'economy/',
|
||||
include('economy.urls', namespace='economy'),
|
||||
),
|
||||
])
|
||||
)
|
||||
),
|
||||
path("teams/", include("teams.urls", namespace="teams")),
|
||||
path("rideshare/", include("rideshare.urls", namespace="rideshare")),
|
||||
path("backoffice/", include("backoffice.urls", namespace="backoffice")),
|
||||
path("feedback/", FeedbackCreate.as_view(), name="feedback"),
|
||||
path("economy/", include("economy.urls", namespace="economy")),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
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)
|
||||
class CampModelAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
|
|
@ -7,19 +7,15 @@ def camp(request):
|
|||
Return it after adding the slug to request.session along with a "camps"
|
||||
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:
|
||||
camp = Camp.objects.get(slug=request.resolver_match.kwargs['camp_slug'])
|
||||
request.session['campslug'] = camp.slug
|
||||
camp = Camp.objects.get(slug=request.resolver_match.kwargs["camp_slug"])
|
||||
request.session["campslug"] = camp.slug
|
||||
except Camp.DoesNotExist:
|
||||
request.session['campslug'] = None
|
||||
request.session["campslug"] = None
|
||||
camp = None
|
||||
else:
|
||||
request.session['campslug'] = None
|
||||
request.session["campslug"] = None
|
||||
camp = None
|
||||
|
||||
return {
|
||||
'camps': Camp.objects.all().order_by('-camp'),
|
||||
'camp': camp
|
||||
}
|
||||
|
||||
return {"camps": Camp.objects.all().order_by("-camp"), "camp": camp}
|
||||
|
|
|
@ -6,64 +6,58 @@ import os
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Creates html files needed for a camp'
|
||||
help = "Creates html files needed for a camp"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('camp_slug', type=str)
|
||||
parser.add_argument("camp_slug", type=str)
|
||||
|
||||
def output(self, message):
|
||||
self.stdout.write('{}: {}'.format(
|
||||
timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
message
|
||||
)
|
||||
self.stdout.write(
|
||||
"{}: {}".format(timezone.now().strftime("%Y-%m-%d %H:%M:%S"), message)
|
||||
)
|
||||
|
||||
def local_dir(self, entry):
|
||||
return os.path.join(
|
||||
settings.DJANGO_BASE_PATH,
|
||||
entry
|
||||
)
|
||||
return os.path.join(settings.DJANGO_BASE_PATH, entry)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
# files to create, relative to DJANGO_BASE_PATH
|
||||
files = [
|
||||
'camps/templates/{camp_slug}_camp_detail.html',
|
||||
]
|
||||
files = ["camps/templates/{camp_slug}_camp_detail.html"]
|
||||
|
||||
# directories to create, relative to DJANGO_BASE_PATH
|
||||
dirs = [
|
||||
'static_src/img/{camp_slug}/logo'
|
||||
]
|
||||
dirs = ["static_src/img/{camp_slug}/logo"]
|
||||
|
||||
camp_slug = options['camp_slug']
|
||||
camp_slug = options["camp_slug"]
|
||||
|
||||
for _file in files:
|
||||
path = self.local_dir(_file.format(camp_slug=camp_slug))
|
||||
if os.path.isfile(_file):
|
||||
self.output('File {} exists...'.format(path))
|
||||
self.output("File {} exists...".format(path))
|
||||
else:
|
||||
self.output('Creating {}'.format(path))
|
||||
with open(path, mode='w', encoding='utf-8') as f:
|
||||
self.output("Creating {}".format(path))
|
||||
with open(path, mode="w", encoding="utf-8") as f:
|
||||
f.write(_file.format(camp_slug=camp_slug))
|
||||
|
||||
for _dir in dirs:
|
||||
path = self.local_dir(_file.format(camp_slug=camp_slug))
|
||||
if os.path.exists(path):
|
||||
self.output('Path {} exists...'.format(path))
|
||||
self.output("Path {} exists...".format(path))
|
||||
else:
|
||||
self.output('Creating {}'.format(path))
|
||||
self.output("Creating {}".format(path))
|
||||
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.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.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):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Camp',
|
||||
name="Camp",
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=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')),
|
||||
('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')),
|
||||
("created", models.DateTimeField(auto_now_add=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",
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
'verbose_name_plural': 'Camps',
|
||||
'verbose_name': 'Camp',
|
||||
},
|
||||
options={"verbose_name_plural": "Camps", "verbose_name": "Camp"},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Day',
|
||||
name="Day",
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=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')),
|
||||
('camp', models.ForeignKey(on_delete=models.PROTECT, to='camps.Camp', help_text='Which camp does this day belong to.', verbose_name='Camp')),
|
||||
("created", models.DateTimeField(auto_now_add=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")),
|
||||
(
|
||||
"camp",
|
||||
models.ForeignKey(
|
||||
on_delete=models.PROTECT,
|
||||
to="camps.Camp",
|
||||
help_text="Which camp does this day belong to.",
|
||||
verbose_name="Camp",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Days',
|
||||
'verbose_name': 'Day',
|
||||
},
|
||||
options={"verbose_name_plural": "Days", "verbose_name": "Day"},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Expense',
|
||||
name="Expense",
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=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')),
|
||||
('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')], help_text='What currency the amount is in.', verbose_name='Currency')),
|
||||
('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)),
|
||||
("created", models.DateTimeField(auto_now_add=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",
|
||||
),
|
||||
),
|
||||
(
|
||||
"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"),
|
||||
],
|
||||
help_text="What currency the amount is in.",
|
||||
verbose_name="Currency",
|
||||
),
|
||||
),
|
||||
(
|
||||
"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',
|
||||
},
|
||||
options={"verbose_name_plural": "Expenses", "verbose_name": "Expense"},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Signup',
|
||||
name="Signup",
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=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)),
|
||||
('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')),
|
||||
("created", models.DateTimeField(auto_now_add=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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
'verbose_name_plural': 'Signups',
|
||||
'verbose_name': 'Signup',
|
||||
},
|
||||
options={"verbose_name_plural": "Signups", "verbose_name": "Signup"},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -8,14 +8,18 @@ import django.db.models.deletion
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0001_initial'),
|
||||
]
|
||||
dependencies = [("camps", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='day',
|
||||
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'),
|
||||
),
|
||||
model_name="day",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0002_auto_20160117_1718'),
|
||||
]
|
||||
dependencies = [("camps", "0002_auto_20160117_1718")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='signup',
|
||||
name='camp',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='signup',
|
||||
name='user',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Signup',
|
||||
),
|
||||
migrations.RemoveField(model_name="signup", name="camp"),
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0003_auto_20160422_2019'),
|
||||
]
|
||||
dependencies = [("camps", "0003_auto_20160422_2019")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='ticket_sale_open',
|
||||
field=models.BooleanField(default=False, help_text='Whether tickets are for sale or not.', verbose_name='Ticket sale open?'),
|
||||
),
|
||||
model_name="camp",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0004_camp_ticket_sale_open'),
|
||||
]
|
||||
dependencies = [("camps", "0004_camp_ticket_sale_open")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='ticket_sale_open',
|
||||
),
|
||||
migrations.RemoveField(model_name="camp", name="ticket_sale_open"),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='shop_open',
|
||||
field=models.BooleanField(default=False, help_text='Whether the shop is open or not.', verbose_name='Shop open?'),
|
||||
model_name="camp",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0005_auto_20160510_2011'),
|
||||
]
|
||||
dependencies = [("camps", "0005_auto_20160510_2011")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='day',
|
||||
options={'ordering': ['date'], 'verbose_name': 'Day', 'verbose_name_plural': 'Days'},
|
||||
),
|
||||
name="day",
|
||||
options={
|
||||
"ordering": ["date"],
|
||||
"verbose_name": "Day",
|
||||
"verbose_name_plural": "Days",
|
||||
},
|
||||
)
|
||||
]
|
||||
|
|
|
@ -13,81 +13,101 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('camps', '0006_auto_20160804_1705'),
|
||||
("camps", "0006_auto_20160804_1705"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='day',
|
||||
name='camp',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
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.RemoveField(model_name="day", name="camp"),
|
||||
migrations.RemoveField(model_name="camp", 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(
|
||||
model_name='camp',
|
||||
name='slug',
|
||||
field=models.SlugField(default='', help_text=b'The url slug to use for this camp', verbose_name=b'Url Slug'),
|
||||
model_name="camp",
|
||||
name="slug",
|
||||
field=models.SlugField(
|
||||
default="",
|
||||
help_text=b"The url slug to use for this camp",
|
||||
verbose_name=b"Url Slug",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
name='refund_paid',
|
||||
field=models.BooleanField(default=False, help_text=b'Has this expense been refunded to the user?', verbose_name=b'Refund paid?'),
|
||||
model_name="expense",
|
||||
name="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(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='end',
|
||||
field=models.DateTimeField(help_text=b'When the camp ends.', verbose_name=b'End date'),
|
||||
model_name="camp",
|
||||
name="end",
|
||||
field=models.DateTimeField(
|
||||
help_text=b"When the camp ends.", verbose_name=b"End date"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='name',
|
||||
field=models.CharField(help_text=b'Name of the camp, ie. Bornhack 2016.', max_length=255, verbose_name=b'Name'),
|
||||
model_name="camp",
|
||||
name="name",
|
||||
field=models.CharField(
|
||||
help_text=b"Name of the camp, ie. Bornhack 2016.",
|
||||
max_length=255,
|
||||
verbose_name=b"Name",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='start',
|
||||
field=models.DateTimeField(help_text=b'When the camp starts.', verbose_name=b'Start date'),
|
||||
model_name="camp",
|
||||
name="start",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('program', '0010_auto_20161212_1809'),
|
||||
('camps', '0007_auto_20161212_1803'),
|
||||
("program", "0010_auto_20161212_1809"),
|
||||
("camps", "0007_auto_20161212_1803"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='Day',
|
||||
),
|
||||
]
|
||||
operations = [migrations.DeleteModel(name="Day")]
|
||||
|
|
|
@ -9,31 +9,31 @@ from django.utils.timezone import utc
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0008_delete_day'),
|
||||
]
|
||||
dependencies = [("camps", "0008_delete_day")]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(model_name="camp", old_name="end", new_name="camp_end"),
|
||||
migrations.RenameField(
|
||||
model_name='camp',
|
||||
old_name='end',
|
||||
new_name='camp_end',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='camp',
|
||||
old_name='start',
|
||||
new_name='camp_start',
|
||||
model_name="camp", old_name="start", new_name="camp_start"
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,25 +7,30 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0009_auto_20161220_1645'),
|
||||
]
|
||||
dependencies = [("camps", "0009_auto_20161220_1645")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='name',
|
||||
),
|
||||
migrations.RemoveField(model_name="camp", name="name"),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='tagline',
|
||||
field=models.CharField(default='', help_text=b'Tagline of the camp, ie. "Initial Commit"', max_length=255, verbose_name=b'Tagline'),
|
||||
model_name="camp",
|
||||
name="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,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='title',
|
||||
field=models.CharField(default='', help_text=b'Title of the camp, ie. Bornhack 2016.', max_length=255, verbose_name=b'Title'),
|
||||
model_name="camp",
|
||||
name="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,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,21 +7,29 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0010_auto_20161220_1714'),
|
||||
]
|
||||
dependencies = [("camps", "0010_auto_20161220_1714")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,17 +7,9 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0011_auto_20161228_1750'),
|
||||
]
|
||||
dependencies = [("camps", "0011_auto_20161228_1750")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='logo_large',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='logo_small',
|
||||
),
|
||||
migrations.RemoveField(model_name="camp", name="logo_large"),
|
||||
migrations.RemoveField(model_name="camp", name="logo_small"),
|
||||
]
|
||||
|
|
|
@ -8,40 +8,36 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0012_auto_20161228_2312'),
|
||||
]
|
||||
dependencies = [("camps", "0012_auto_20161228_2312")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='buildup_start',
|
||||
),
|
||||
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.RemoveField(model_name="camp", name="buildup_start"),
|
||||
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(
|
||||
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.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'),
|
||||
model_name="camp",
|
||||
name="camp",
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||
help_text=b"The camp period.", null=True, verbose_name=b"Camp Period"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='camp',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp period.', null=True, verbose_name=b'Camp 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'),
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0013_auto_20161229_2201'),
|
||||
]
|
||||
dependencies = [("camps", "0013_auto_20161229_2201")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
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)),
|
||||
model_name="camp",
|
||||
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),
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_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)),
|
||||
model_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),
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
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)),
|
||||
model_name="camp",
|
||||
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),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,16 +7,9 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0014_auto_20161229_2202'),
|
||||
]
|
||||
dependencies = [("camps", "0014_auto_20161229_2202")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='expense',
|
||||
name='refund_user',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Expense',
|
||||
),
|
||||
migrations.RemoveField(model_name="expense", name="refund_user"),
|
||||
migrations.DeleteModel(name="Expense"),
|
||||
]
|
||||
|
|
|
@ -7,14 +7,16 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0015_auto_20170116_1634'),
|
||||
]
|
||||
dependencies = [("camps", "0015_auto_20170116_1634")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -7,13 +7,6 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0016_camp_description'),
|
||||
]
|
||||
dependencies = [("camps", "0016_camp_description")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='camp',
|
||||
name='description',
|
||||
),
|
||||
]
|
||||
operations = [migrations.RemoveField(model_name="camp", name="description")]
|
||||
|
|
|
@ -8,24 +8,28 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0017_remove_camp_description'),
|
||||
]
|
||||
dependencies = [("camps", "0017_remove_camp_description")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='buildup',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp buildup period.', verbose_name=b'Buildup Period'),
|
||||
model_name="camp",
|
||||
name="buildup",
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||
help_text=b"The camp buildup period.", verbose_name=b"Buildup Period"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='camp',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp period.', verbose_name=b'Camp Period'),
|
||||
model_name="camp",
|
||||
name="camp",
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||
help_text=b"The camp period.", verbose_name=b"Camp Period"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='teardown',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text=b'The camp teardown period.', verbose_name=b'Teardown period'),
|
||||
model_name="camp",
|
||||
name="teardown",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0018_auto_20170128_1841'),
|
||||
]
|
||||
dependencies = [("camps", "0018_auto_20170128_1841")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='buildup',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='The camp buildup period.', verbose_name='Buildup Period'),
|
||||
model_name="camp",
|
||||
name="buildup",
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||
help_text="The camp buildup period.", verbose_name="Buildup Period"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='camp',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='The camp period.', verbose_name='Camp Period'),
|
||||
model_name="camp",
|
||||
name="camp",
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||
help_text="The camp period.", verbose_name="Camp Period"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='slug',
|
||||
field=models.SlugField(help_text='The url slug to use for this camp', verbose_name='Url Slug'),
|
||||
model_name="camp",
|
||||
name="slug",
|
||||
field=models.SlugField(
|
||||
help_text="The url slug to use for this camp", verbose_name="Url Slug"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='tagline',
|
||||
field=models.CharField(help_text='Tagline of the camp, ie. "Initial Commit"', max_length=255, verbose_name='Tagline'),
|
||||
model_name="camp",
|
||||
name="tagline",
|
||||
field=models.CharField(
|
||||
help_text='Tagline of the camp, ie. "Initial Commit"',
|
||||
max_length=255,
|
||||
verbose_name="Tagline",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='teardown',
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(help_text='The camp teardown period.', verbose_name='Teardown period'),
|
||||
model_name="camp",
|
||||
name="teardown",
|
||||
field=django.contrib.postgres.fields.ranges.DateTimeRangeField(
|
||||
help_text="The camp teardown period.", verbose_name="Teardown period"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
name='title',
|
||||
field=models.CharField(help_text='Title of the camp, ie. Bornhack 2016.', max_length=255, verbose_name='Title'),
|
||||
model_name="camp",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0019_auto_20170131_1849'),
|
||||
]
|
||||
dependencies = [("camps", "0019_auto_20170131_1849")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='read_only',
|
||||
field=models.BooleanField(default=False, help_text='Whether the camp is read only (i.e. in the past)'),
|
||||
),
|
||||
model_name="camp",
|
||||
name="read_only",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0020_camp_read_only'),
|
||||
]
|
||||
dependencies = [("camps", "0020_camp_read_only")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='camp',
|
||||
options={'ordering': ['-title'], 'verbose_name': 'Camp', 'verbose_name_plural': 'Camps'},
|
||||
),
|
||||
name="camp",
|
||||
options={
|
||||
"ordering": ["-title"],
|
||||
"verbose_name": "Camp",
|
||||
"verbose_name_plural": "Camps",
|
||||
},
|
||||
)
|
||||
]
|
||||
|
|
|
@ -7,15 +7,18 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0021_auto_20170711_2247'),
|
||||
]
|
||||
dependencies = [("camps", "0021_auto_20170711_2247")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='colour',
|
||||
field=models.CharField(default='#000000', help_text='The primary colour for the camp in hex', max_length=7, verbose_name='Colour'),
|
||||
model_name="camp",
|
||||
name="colour",
|
||||
field=models.CharField(
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0022_camp_colour'),
|
||||
]
|
||||
dependencies = [("camps", "0022_camp_colour")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -4,19 +4,16 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
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():
|
||||
if not camp.shortslug:
|
||||
camp.shortslug = camp.slug
|
||||
camp.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('camps', '0023_camp_shortslug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(populate_camp_shortslugs),
|
||||
]
|
||||
dependencies = [("camps", "0023_camp_shortslug")]
|
||||
|
||||
operations = [migrations.RunPython(populate_camp_shortslugs)]
|
||||
|
|
|
@ -7,14 +7,15 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0024_populate_camp_shortslugs'),
|
||||
]
|
||||
dependencies = [("camps", "0024_populate_camp_shortslugs")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -5,19 +5,23 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0025_auto_20180318_1250'),
|
||||
]
|
||||
dependencies = [("camps", "0025_auto_20180318_1250")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='call_for_participation_open',
|
||||
field=models.BooleanField(default=False, help_text='Check if the Call for Participation is open for this camp'),
|
||||
model_name="camp",
|
||||
name="call_for_participation_open",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="Check if the Call for Participation is open for this camp",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='call_for_sponsors_open',
|
||||
field=models.BooleanField(default=False, help_text='Check if the Call for Sponsors is open for this camp'),
|
||||
model_name="camp",
|
||||
name="call_for_sponsors_open",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0026_auto_20180506_1633'),
|
||||
]
|
||||
dependencies = [("camps", "0026_auto_20180506_1633")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='call_for_participation',
|
||||
field=models.TextField(blank=True, help_text='The CFP markdown for this Camp'),
|
||||
model_name="camp",
|
||||
name="call_for_participation",
|
||||
field=models.TextField(
|
||||
blank=True, help_text="The CFP markdown for this Camp"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='call_for_sponsors',
|
||||
field=models.TextField(blank=True, help_text='The CFS markdown for this Camp'),
|
||||
model_name="camp",
|
||||
name="call_for_sponsors",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0027_auto_20180525_1019'),
|
||||
]
|
||||
dependencies = [("camps", "0027_auto_20180525_1019")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='camp',
|
||||
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'),
|
||||
model_name="camp",
|
||||
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",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,13 +5,16 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0028_auto_20180525_1025'),
|
||||
]
|
||||
dependencies = [("camps", "0028_auto_20180525_1025")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='camp',
|
||||
options={'ordering': ['-title'], 'permissions': (('infodesk_permission', 'Infodesk permission'),), 'verbose_name': 'Camp', 'verbose_name_plural': 'Camps'},
|
||||
),
|
||||
name="camp",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0029_auto_20180815_2018'),
|
||||
]
|
||||
dependencies = [("camps", "0029_auto_20180815_2018")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='camp',
|
||||
name='light_text',
|
||||
field=models.BooleanField(default=True, help_text='Check if this camps colour requires white text, uncheck if black text is better'),
|
||||
),
|
||||
model_name="camp",
|
||||
name="light_text",
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0030_camp_light_text'),
|
||||
]
|
||||
dependencies = [("camps", "0030_camp_light_text")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Permission',
|
||||
name="Permission",
|
||||
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={
|
||||
'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')),
|
||||
'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"),
|
||||
),
|
||||
"default_permissions": (),
|
||||
"managed": False,
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='camp',
|
||||
options={'ordering': ['-title'], 'verbose_name': 'Camp', 'verbose_name_plural': 'Camps'},
|
||||
name="camp",
|
||||
options={
|
||||
"ordering": ["-title"],
|
||||
"verbose_name": "Camp",
|
||||
"verbose_name_plural": "Camps",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,13 +5,23 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0031_auto_20180830_0014'),
|
||||
]
|
||||
dependencies = [("camps", "0031_auto_20180830_0014")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
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'))},
|
||||
),
|
||||
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"),
|
||||
),
|
||||
},
|
||||
)
|
||||
]
|
||||
|
|
|
@ -21,7 +21,7 @@ class CampViewMixin(object):
|
|||
return queryset
|
||||
|
||||
# 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
|
||||
|
||||
# get the camp_filter from the model
|
||||
|
@ -36,14 +36,14 @@ class CampViewMixin(object):
|
|||
filter_dict = {_filter: self.camp}
|
||||
|
||||
# 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)
|
||||
if pk is not None:
|
||||
# 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
|
||||
if hasattr(self, 'slug_url_kwarg'):
|
||||
if hasattr(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):
|
||||
# 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
|
||||
return result
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from datetime import timedelta
|
|||
from django.utils import timezone
|
||||
from django.urls import reverse
|
||||
import logging
|
||||
|
||||
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)
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
default_permissions=()
|
||||
default_permissions = ()
|
||||
permissions = (
|
||||
("backoffice_permission", "BackOffice access"),
|
||||
("orgateam_permission", "Orga Team permissions set"),
|
||||
|
@ -31,102 +33,94 @@ class Permission(models.Model):
|
|||
|
||||
class Camp(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
verbose_name = 'Camp'
|
||||
verbose_name_plural = 'Camps'
|
||||
ordering = ['-title']
|
||||
verbose_name = "Camp"
|
||||
verbose_name_plural = "Camps"
|
||||
ordering = ["-title"]
|
||||
|
||||
title = models.CharField(
|
||||
verbose_name='Title',
|
||||
help_text='Title of the camp, ie. Bornhack 2016.',
|
||||
verbose_name="Title",
|
||||
help_text="Title of the camp, ie. Bornhack 2016.",
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
tagline = models.CharField(
|
||||
verbose_name='Tagline',
|
||||
verbose_name="Tagline",
|
||||
help_text='Tagline of the camp, ie. "Initial Commit"',
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
slug = models.SlugField(
|
||||
verbose_name='Url Slug',
|
||||
help_text='The url slug to use for this camp'
|
||||
verbose_name="Url Slug", help_text="The url slug to use for this camp"
|
||||
)
|
||||
|
||||
shortslug = models.SlugField(
|
||||
verbose_name='Short Slug',
|
||||
help_text='Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.',
|
||||
verbose_name="Short Slug",
|
||||
help_text="Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.",
|
||||
)
|
||||
|
||||
buildup = DateTimeRangeField(
|
||||
verbose_name='Buildup Period',
|
||||
help_text='The camp buildup period.',
|
||||
verbose_name="Buildup Period", help_text="The camp buildup period."
|
||||
)
|
||||
|
||||
camp = DateTimeRangeField(
|
||||
verbose_name='Camp Period',
|
||||
help_text='The camp period.',
|
||||
)
|
||||
camp = DateTimeRangeField(verbose_name="Camp Period", help_text="The camp period.")
|
||||
|
||||
teardown = DateTimeRangeField(
|
||||
verbose_name='Teardown period',
|
||||
help_text='The camp teardown period.',
|
||||
verbose_name="Teardown period", help_text="The camp teardown period."
|
||||
)
|
||||
|
||||
read_only = models.BooleanField(
|
||||
help_text='Whether the camp is read only (i.e. in the past)',
|
||||
default=False
|
||||
help_text="Whether the camp is read only (i.e. in the past)", default=False
|
||||
)
|
||||
|
||||
colour = models.CharField(
|
||||
verbose_name='Colour',
|
||||
help_text='The primary colour for the camp in hex',
|
||||
max_length=7
|
||||
verbose_name="Colour",
|
||||
help_text="The primary colour for the camp in hex",
|
||||
max_length=7,
|
||||
)
|
||||
|
||||
light_text = models.BooleanField(
|
||||
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(
|
||||
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,
|
||||
)
|
||||
|
||||
call_for_participation = models.TextField(
|
||||
blank=True,
|
||||
help_text='The CFP markdown for this Camp',
|
||||
default='The Call For Participation for this Camp has not been written yet',
|
||||
help_text="The CFP markdown for this Camp",
|
||||
default="The Call For Participation for this Camp has not been written yet",
|
||||
)
|
||||
|
||||
call_for_sponsors_open = models.BooleanField(
|
||||
help_text='Check if the Call for Sponsors is open for this camp',
|
||||
default=False,
|
||||
help_text="Check if the Call for Sponsors is open for this camp", default=False
|
||||
)
|
||||
|
||||
call_for_sponsors = models.TextField(
|
||||
blank=True,
|
||||
help_text='The CFS markdown for this Camp',
|
||||
default='The Call For Sponsors for this Camp has not been written yet',
|
||||
help_text="The CFS markdown for this Camp",
|
||||
default="The Call For Sponsors for this Camp has not been written yet",
|
||||
)
|
||||
|
||||
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):
|
||||
''' 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 = []
|
||||
# check for overlaps buildup vs. camp
|
||||
if self.buildup.upper > self.camp.lower:
|
||||
msg = "End of buildup must not be after camp start"
|
||||
errors.append(ValidationError({'buildup', msg}))
|
||||
errors.append(ValidationError({'camp', msg}))
|
||||
errors.append(ValidationError({"buildup", msg}))
|
||||
errors.append(ValidationError({"camp", msg}))
|
||||
|
||||
# check for overlaps camp vs. teardown
|
||||
if self.camp.upper > self.teardown.lower:
|
||||
msg = "End of camp must not be after teardown start"
|
||||
errors.append(ValidationError({'camp', msg}))
|
||||
errors.append(ValidationError({'teardown', msg}))
|
||||
errors.append(ValidationError({"camp", msg}))
|
||||
errors.append(ValidationError({"teardown", msg}))
|
||||
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
@ -137,40 +131,48 @@ class Camp(CreatedUpdatedModel, UUIDModel):
|
|||
@property
|
||||
def event_types(self):
|
||||
""" 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
|
||||
def event_locations(self):
|
||||
''' Return all event locations with at least one event in this camp'''
|
||||
return EventLocation.objects.filter(eventinstances__isnull=False, camp=self).distinct()
|
||||
""" Return all event locations with at least one event in this camp"""
|
||||
return EventLocation.objects.filter(
|
||||
eventinstances__isnull=False, camp=self
|
||||
).distinct()
|
||||
|
||||
@property
|
||||
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
|
||||
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
|
||||
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
|
||||
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):
|
||||
'''
|
||||
"""
|
||||
Returns a list of DateTimeTZRanges representing the days during the specified part of the camp.
|
||||
'''
|
||||
"""
|
||||
if not hasattr(self, camppart):
|
||||
logger.error("nonexistant field/attribute")
|
||||
return False
|
||||
|
||||
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)
|
||||
return False
|
||||
|
||||
|
@ -182,45 +184,44 @@ class Camp(CreatedUpdatedModel, UUIDModel):
|
|||
days.append(
|
||||
DateTimeTZRange(
|
||||
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
|
||||
days.append(
|
||||
DateTimeTZRange(
|
||||
(field.lower+timedelta(days=i)).replace(hour=0),
|
||||
field.lower+timedelta(days=i+1)
|
||||
(field.lower + timedelta(days=i)).replace(hour=0),
|
||||
field.lower + timedelta(days=i + 1),
|
||||
)
|
||||
)
|
||||
else:
|
||||
# neither first nor last day, goes from midnight to midnight
|
||||
days.append(
|
||||
DateTimeTZRange(
|
||||
(field.lower+timedelta(days=i)).replace(hour=0),
|
||||
(field.lower+timedelta(days=i+1)).replace(hour=0)
|
||||
(field.lower + timedelta(days=i)).replace(hour=0),
|
||||
(field.lower + timedelta(days=i + 1)).replace(hour=0),
|
||||
)
|
||||
)
|
||||
return days
|
||||
|
||||
@property
|
||||
def buildup_days(self):
|
||||
'''
|
||||
"""
|
||||
Returns a list of DateTimeTZRanges representing the days during the buildup.
|
||||
'''
|
||||
return self.get_days('buildup')
|
||||
"""
|
||||
return self.get_days("buildup")
|
||||
|
||||
@property
|
||||
def camp_days(self):
|
||||
'''
|
||||
"""
|
||||
Returns a list of DateTimeTZRanges representing the days during the camp.
|
||||
'''
|
||||
return self.get_days('camp')
|
||||
"""
|
||||
return self.get_days("camp")
|
||||
|
||||
@property
|
||||
def teardown_days(self):
|
||||
'''
|
||||
"""
|
||||
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
|
||||
a property and not a real model field.
|
||||
"""
|
||||
title = 'Camp'
|
||||
parameter_name = 'camp'
|
||||
|
||||
title = "Camp"
|
||||
parameter_name = "camp"
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
# get the current queryset
|
||||
|
|
|
@ -6,36 +6,37 @@ from .mixins import CampViewMixin
|
|||
from django.views import View
|
||||
from django.conf import settings
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||
|
||||
|
||||
class CampRedirectView(CampViewMixin, View):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
now = timezone.now()
|
||||
|
||||
try:
|
||||
camp = Camp.objects.get(
|
||||
camp__contains=now
|
||||
camp = Camp.objects.get(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:
|
||||
pass
|
||||
|
||||
# no ongoing camp, find the closest camp in the past
|
||||
try:
|
||||
prevcamp = Camp.objects.filter(
|
||||
camp__endswith__lt=now
|
||||
).order_by('-camp').first()
|
||||
prevcamp = (
|
||||
Camp.objects.filter(camp__endswith__lt=now).order_by("-camp").first()
|
||||
)
|
||||
except Camp.DoesNotExist:
|
||||
prevcamp = None
|
||||
|
||||
# find the closest upcoming camp
|
||||
try:
|
||||
nextcamp = Camp.objects.filter(
|
||||
camp__startswith__gt=now
|
||||
).order_by('camp').first()
|
||||
nextcamp = (
|
||||
Camp.objects.filter(camp__startswith__gt=now).order_by("camp").first()
|
||||
)
|
||||
except Camp.DoesNotExist:
|
||||
nextcamp = None
|
||||
|
||||
|
@ -59,19 +60,18 @@ class CampRedirectView(CampViewMixin, View):
|
|||
camp = prevcamp
|
||||
|
||||
# do the redirect
|
||||
return redirect(kwargs['page'], camp_slug=camp.slug)
|
||||
return redirect(kwargs["page"], camp_slug=camp.slug)
|
||||
|
||||
|
||||
class CampDetailView(DetailView):
|
||||
model = Camp
|
||||
slug_url_kwarg = 'camp_slug'
|
||||
slug_url_kwarg = "camp_slug"
|
||||
|
||||
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):
|
||||
model = Camp
|
||||
template_name = 'camp_list.html'
|
||||
queryset = Camp.objects.all().order_by('camp')
|
||||
|
||||
template_name = "camp_list.html"
|
||||
queryset = Camp.objects.all().order_by("camp")
|
||||
|
|
|
@ -5,71 +5,108 @@ from .models import Chain, Credebtor, Expense, Reimbursement, Revenue
|
|||
|
||||
### chains and credebtors
|
||||
|
||||
|
||||
@admin.register(Chain)
|
||||
class ChainAdmin(admin.ModelAdmin):
|
||||
list_filter = ['name']
|
||||
list_display = ['name', 'notes']
|
||||
search_fields = ['name', 'notes']
|
||||
list_filter = ["name"]
|
||||
list_display = ["name", "notes"]
|
||||
search_fields = ["name", "notes"]
|
||||
|
||||
|
||||
@admin.register(Credebtor)
|
||||
class ChainAdmin(admin.ModelAdmin):
|
||||
list_filter = ['chain', 'name']
|
||||
list_display = ['chain', 'name', 'notes']
|
||||
search_fields = ['chain', 'name', 'notes']
|
||||
list_filter = ["chain", "name"]
|
||||
list_display = ["chain", "name", "notes"]
|
||||
search_fields = ["chain", "name", "notes"]
|
||||
|
||||
|
||||
### expenses
|
||||
|
||||
|
||||
def approve_expenses(modeladmin, request, queryset):
|
||||
for expense in queryset.all():
|
||||
expense.approve(request)
|
||||
|
||||
|
||||
approve_expenses.short_description = "Approve Expenses"
|
||||
|
||||
|
||||
def reject_expenses(modeladmin, request, queryset):
|
||||
for expense in queryset.all():
|
||||
expense.reject(request)
|
||||
|
||||
|
||||
reject_expenses.short_description = "Reject Expenses"
|
||||
|
||||
|
||||
@admin.register(Expense)
|
||||
class ExpenseAdmin(admin.ModelAdmin):
|
||||
list_filter = ['camp', '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']
|
||||
list_filter = [
|
||||
"camp",
|
||||
"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]
|
||||
|
||||
|
||||
### revenues
|
||||
|
||||
|
||||
def approve_revenues(modeladmin, request, queryset):
|
||||
for revenue in queryset.all():
|
||||
revenue.approve(request)
|
||||
|
||||
|
||||
approve_revenues.short_description = "Approve Revenues"
|
||||
|
||||
|
||||
def reject_revenues(modeladmin, request, queryset):
|
||||
for revenue in queryset.all():
|
||||
revenue.reject(request)
|
||||
|
||||
|
||||
reject_revenues.short_description = "Reject Revenues"
|
||||
|
||||
|
||||
@admin.register(Revenue)
|
||||
class RevenueAdmin(admin.ModelAdmin):
|
||||
list_filter = ['camp', 'responsible_team', 'approved', 'user']
|
||||
list_display = ['user', 'description', 'invoice_date', 'amount', 'camp', 'responsible_team', 'approved']
|
||||
search_fields = ['description', 'amount', 'user']
|
||||
list_filter = ["camp", "responsible_team", "approved", "user"]
|
||||
list_display = [
|
||||
"user",
|
||||
"description",
|
||||
"invoice_date",
|
||||
"amount",
|
||||
"camp",
|
||||
"responsible_team",
|
||||
"approved",
|
||||
]
|
||||
search_fields = ["description", "amount", "user"]
|
||||
actions = [approve_revenues, reject_revenues]
|
||||
|
||||
|
||||
### reimbursements
|
||||
|
||||
|
||||
@admin.register(Reimbursement)
|
||||
class ReimbursementAdmin(admin.ModelAdmin):
|
||||
def get_amount(self, obj):
|
||||
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):
|
||||
name = 'economy'
|
||||
name = "economy"
|
||||
|
|
|
@ -6,6 +6,7 @@ from utils.email import add_outgoing_email
|
|||
|
||||
# expense emails
|
||||
|
||||
|
||||
def send_accountingsystem_expense_email(expense):
|
||||
"""
|
||||
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],
|
||||
)
|
||||
|
||||
|
||||
# revenue emails
|
||||
|
||||
|
||||
def send_accountingsystem_revenue_email(revenue):
|
||||
"""
|
||||
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,
|
||||
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,
|
||||
and we cannot change the clean_* methods with an autogenerated form from inside views.py
|
||||
"""
|
||||
|
||||
invoice = forms.FileField()
|
||||
|
||||
def clean_invoice(self):
|
||||
# get the uploaded file from cleaned_data
|
||||
uploaded_file = self.cleaned_data['invoice']
|
||||
uploaded_file = self.cleaned_data["invoice"]
|
||||
# is this a valid image?
|
||||
try:
|
||||
# create an ImageField instance
|
||||
|
@ -36,23 +37,41 @@ class CleanInvoiceForm(forms.ModelForm):
|
|||
class ExpenseCreateForm(CleanInvoiceForm):
|
||||
class Meta:
|
||||
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 Meta:
|
||||
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 Meta:
|
||||
model = Revenue
|
||||
fields = ['description', 'amount', 'invoice_date', 'invoice', 'responsible_team']
|
||||
fields = [
|
||||
"description",
|
||||
"amount",
|
||||
"invoice_date",
|
||||
"invoice",
|
||||
"responsible_team",
|
||||
]
|
||||
|
||||
|
||||
class RevenueUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
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 = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('teams', '0049_auto_20180815_1119'),
|
||||
('camps', '0031_auto_20180830_0014'),
|
||||
("teams", "0049_auto_20180815_1119"),
|
||||
("camps", "0031_auto_20180830_0014"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Expense',
|
||||
name="Expense",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=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 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')),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=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 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={
|
||||
'abstract': False,
|
||||
},
|
||||
options={"abstract": False},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Reimbursement',
|
||||
name="Reimbursement",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('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)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("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={
|
||||
'abstract': False,
|
||||
},
|
||||
options={"abstract": False},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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),
|
||||
model_name="expense",
|
||||
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,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -9,32 +9,101 @@ import uuid
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shop', '0057_order_notes'),
|
||||
('teams', '0049_auto_20180815_1119'),
|
||||
('camps', '0031_auto_20180830_0014'),
|
||||
("shop", "0057_order_notes"),
|
||||
("teams", "0049_auto_20180815_1119"),
|
||||
("camps", "0031_auto_20180830_0014"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('economy', '0001_initial'),
|
||||
("economy", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Revenue',
|
||||
name="Revenue",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=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)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=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,
|
||||
},
|
||||
),
|
||||
options={"abstract": False},
|
||||
)
|
||||
]
|
||||
|
|
|
@ -6,14 +6,19 @@ import django.db.models.deletion
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0002_revenue'),
|
||||
]
|
||||
dependencies = [("economy", "0002_revenue")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='revenue',
|
||||
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'),
|
||||
),
|
||||
model_name="revenue",
|
||||
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",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -7,14 +7,17 @@ import django.db.models.deletion
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0003_auto_20180917_1933'),
|
||||
]
|
||||
dependencies = [("economy", "0003_auto_20180917_1933")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='reimbursement',
|
||||
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),
|
||||
),
|
||||
model_name="reimbursement",
|
||||
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,
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -5,19 +5,25 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0004_auto_20181120_1835'),
|
||||
]
|
||||
dependencies = [("economy", "0004_auto_20181120_1835")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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),
|
||||
model_name="expense",
|
||||
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,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='revenue',
|
||||
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),
|
||||
model_name="revenue",
|
||||
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,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,19 +5,25 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0005_auto_20190120_1532'),
|
||||
]
|
||||
dependencies = [("economy", "0005_auto_20190120_1532")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='expense',
|
||||
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),
|
||||
model_name="expense",
|
||||
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,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='revenue',
|
||||
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),
|
||||
model_name="revenue",
|
||||
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,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,19 +5,25 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0006_auto_20190120_1642'),
|
||||
]
|
||||
dependencies = [("economy", "0006_auto_20190120_1642")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='expense',
|
||||
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),
|
||||
model_name="expense",
|
||||
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,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='revenue',
|
||||
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),
|
||||
model_name="revenue",
|
||||
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,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,63 +7,136 @@ import uuid
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0007_auto_20190327_0936'),
|
||||
]
|
||||
dependencies = [("economy", "0007_auto_20190327_0936")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Chain',
|
||||
name="Chain",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, 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.')),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
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={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
options={"ordering": ["name"]},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Credebtor',
|
||||
name="Credebtor",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('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')),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("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={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
options={"ordering": ["name"]},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='expense',
|
||||
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.'),
|
||||
model_name="expense",
|
||||
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."
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='revenue',
|
||||
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.'),
|
||||
model_name="revenue",
|
||||
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."
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='revenue',
|
||||
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'),
|
||||
model_name="revenue",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='credebtor',
|
||||
unique_together={('chain', 'slug')},
|
||||
name="credebtor", unique_together={("chain", "slug")}
|
||||
),
|
||||
]
|
||||
|
|
|
@ -5,19 +5,24 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0008_auto_20190327_1721'),
|
||||
]
|
||||
dependencies = [("economy", "0008_auto_20190327_1721")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='chain',
|
||||
name='slug',
|
||||
field=models.SlugField(blank=True, help_text='The url slug for this Chain. Leave blank to auto generate a slug.', unique=True),
|
||||
model_name="chain",
|
||||
name="slug",
|
||||
field=models.SlugField(
|
||||
blank=True,
|
||||
help_text="The url slug for this Chain. Leave blank to auto generate a slug.",
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='credebtor',
|
||||
name='slug',
|
||||
field=models.SlugField(blank=True, help_text='The url slug for this Credebtor. Leave blank to auto generate a slug.'),
|
||||
model_name="credebtor",
|
||||
name="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):
|
||||
|
||||
dependencies = [
|
||||
('economy', '0009_auto_20190328_0715'),
|
||||
]
|
||||
dependencies = [("economy", "0009_auto_20190328_0715")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='expense',
|
||||
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'),
|
||||
model_name="expense",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='revenue',
|
||||
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'),
|
||||
model_name="revenue",
|
||||
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",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -10,14 +10,16 @@ from django.utils.text import slugify
|
|||
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
||||
from .email import *
|
||||
|
||||
|
||||
class ChainManager(models.Manager):
|
||||
"""
|
||||
ChainManager adds 'expenses_total' and 'revenues_total' to the Chain qs
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
qs = qs.annotate(expenses_total=models.Sum('credebtors__expenses__amount'))
|
||||
qs = qs.annotate(revenues_total=models.Sum('credebtors__revenues__amount'))
|
||||
qs = qs.annotate(expenses_total=models.Sum("credebtors__expenses__amount"))
|
||||
qs = qs.annotate(revenues_total=models.Sum("credebtors__revenues__amount"))
|
||||
return qs
|
||||
|
||||
|
||||
|
@ -26,25 +28,26 @@ class Chain(CreatedUpdatedModel, UUIDModel):
|
|||
A chain of Credebtors. Used to group when several Creditors/Debtors
|
||||
belong to the same Chain/company, like XL Byg stores or Netto stores.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
ordering = ["name"]
|
||||
|
||||
objects = ChainManager()
|
||||
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
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(
|
||||
unique=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(
|
||||
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,
|
||||
)
|
||||
|
||||
|
@ -69,10 +72,11 @@ class CredebtorManager(models.Manager):
|
|||
"""
|
||||
CredebtorManager adds 'expenses_total' and 'revenues_total' to the Credebtor qs
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
qs = qs.annotate(expenses_total=models.Sum('expenses__amount'))
|
||||
qs = qs.annotate(revenues_total=models.Sum('revenues__amount'))
|
||||
qs = qs.annotate(expenses_total=models.Sum("expenses__amount"))
|
||||
qs = qs.annotate(revenues_total=models.Sum("revenues__amount"))
|
||||
return qs
|
||||
|
||||
|
||||
|
@ -83,37 +87,36 @@ class Credebtor(CreatedUpdatedModel, UUIDModel):
|
|||
The model is used for both creditors and debtors, since there is a
|
||||
lot of overlap between them.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
unique_together=('chain', 'slug')
|
||||
ordering = ["name"]
|
||||
unique_together = ("chain", "slug")
|
||||
|
||||
objects = CredebtorManager()
|
||||
|
||||
chain = models.ForeignKey(
|
||||
'economy.Chain',
|
||||
"economy.Chain",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='credebtors',
|
||||
help_text='The Chain to which this Credebtor belongs.',
|
||||
related_name="credebtors",
|
||||
help_text="The Chain to which this Credebtor belongs.",
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
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(
|
||||
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(
|
||||
help_text='The address of this Credebtor.',
|
||||
)
|
||||
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.',
|
||||
help_text="Any notes for this Credebtor. Shown when creating an Expense or Revenue for this Credebtor.",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -138,76 +141,77 @@ class Revenue(CampRelatedModel, UUIDModel):
|
|||
Other Revenue objects (such as money returned from bottle deposits) will
|
||||
not have a related BornHack Invoice object.
|
||||
"""
|
||||
|
||||
camp = models.ForeignKey(
|
||||
'camps.Camp',
|
||||
"camps.Camp",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='revenues',
|
||||
help_text='The camp to which this revenue belongs',
|
||||
related_name="revenues",
|
||||
help_text="The camp to which this revenue belongs",
|
||||
)
|
||||
|
||||
debtor = models.ForeignKey(
|
||||
'economy.Credebtor',
|
||||
"economy.Credebtor",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='revenues',
|
||||
help_text='The Debtor to which this revenue belongs',
|
||||
related_name="revenues",
|
||||
help_text="The Debtor to which this revenue belongs",
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
'auth.User',
|
||||
"auth.User",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='revenues',
|
||||
help_text='The user who submitted this revenue',
|
||||
related_name="revenues",
|
||||
help_text="The user who submitted this revenue",
|
||||
)
|
||||
|
||||
amount = models.DecimalField(
|
||||
decimal_places=2,
|
||||
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(
|
||||
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(
|
||||
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/',
|
||||
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/",
|
||||
)
|
||||
|
||||
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(
|
||||
'shop.Invoice',
|
||||
"shop.Invoice",
|
||||
on_delete=models.PROTECT,
|
||||
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.',
|
||||
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.",
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
responsible_team = models.ForeignKey(
|
||||
'teams.Team',
|
||||
"teams.Team",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='revenues',
|
||||
help_text='The team to which this revenue belongs. When in doubt pick the Economy team.'
|
||||
related_name="revenues",
|
||||
help_text="The team to which this revenue belongs. When in doubt pick the Economy team.",
|
||||
)
|
||||
|
||||
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.'
|
||||
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.'
|
||||
help_text="Economy Team notes for this revenue. Only visible to the Economy team and the submitting user.",
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
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
|
||||
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
|
||||
"""
|
||||
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
|
||||
|
||||
# mark as approved and save
|
||||
|
@ -262,35 +269,35 @@ class Revenue(CampRelatedModel, UUIDModel):
|
|||
|
||||
class Expense(CampRelatedModel, UUIDModel):
|
||||
camp = models.ForeignKey(
|
||||
'camps.Camp',
|
||||
"camps.Camp",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='expenses',
|
||||
help_text='The camp to which this expense belongs',
|
||||
related_name="expenses",
|
||||
help_text="The camp to which this expense belongs",
|
||||
)
|
||||
|
||||
creditor = models.ForeignKey(
|
||||
'economy.Credebtor',
|
||||
"economy.Credebtor",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='expenses',
|
||||
help_text='The Creditor to which this expense belongs',
|
||||
related_name="expenses",
|
||||
help_text="The Creditor to which this expense belongs",
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
'auth.User',
|
||||
"auth.User",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='expenses',
|
||||
help_text='The user to which this expense belongs',
|
||||
related_name="expenses",
|
||||
help_text="The user to which this expense belongs",
|
||||
)
|
||||
|
||||
amount = models.DecimalField(
|
||||
decimal_places=2,
|
||||
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(
|
||||
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(
|
||||
|
@ -299,43 +306,43 @@ class Expense(CampRelatedModel, UUIDModel):
|
|||
)
|
||||
|
||||
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/',
|
||||
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/",
|
||||
)
|
||||
|
||||
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(
|
||||
'teams.Team',
|
||||
"teams.Team",
|
||||
on_delete=models.PROTECT,
|
||||
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.'
|
||||
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.",
|
||||
)
|
||||
|
||||
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.'
|
||||
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(
|
||||
'economy.Reimbursement',
|
||||
"economy.Reimbursement",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='expenses',
|
||||
related_name="expenses",
|
||||
null=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(
|
||||
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):
|
||||
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
|
||||
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.
|
||||
"""
|
||||
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
|
||||
|
||||
# mark as approved and save
|
||||
|
@ -392,30 +402,31 @@ class Reimbursement(CampRelatedModel, UUIDModel):
|
|||
"""
|
||||
A reimbursement covers one or more expenses.
|
||||
"""
|
||||
|
||||
camp = models.ForeignKey(
|
||||
'camps.Camp',
|
||||
"camps.Camp",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='reimbursements',
|
||||
help_text='The camp to which this reimbursement belongs',
|
||||
related_name="reimbursements",
|
||||
help_text="The camp to which this reimbursement belongs",
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
'auth.User',
|
||||
"auth.User",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='created_reimbursements',
|
||||
help_text='The economy team member who created this reimbursement.'
|
||||
related_name="created_reimbursements",
|
||||
help_text="The economy team member who created this reimbursement.",
|
||||
)
|
||||
|
||||
reimbursement_user = models.ForeignKey(
|
||||
'auth.User',
|
||||
"auth.User",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='reimbursements',
|
||||
help_text='The user this reimbursement belongs to.'
|
||||
related_name="reimbursements",
|
||||
help_text="The user this reimbursement belongs to.",
|
||||
)
|
||||
|
||||
notes = models.TextField(
|
||||
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(
|
||||
|
@ -439,5 +450,3 @@ class Reimbursement(CampRelatedModel, UUIDModel):
|
|||
for expense in self.expenses.filter(paid_by_bornhack=False):
|
||||
amount += expense.amount
|
||||
return amount
|
||||
|
||||
|
||||
|
|
|
@ -1,150 +1,132 @@
|
|||
from django.urls import path, include
|
||||
from .views import *
|
||||
|
||||
app_name = 'economy'
|
||||
app_name = "economy"
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
'',
|
||||
EconomyDashboardView.as_view(),
|
||||
name='dashboard'
|
||||
),
|
||||
|
||||
path("", EconomyDashboardView.as_view(), name="dashboard"),
|
||||
# chains
|
||||
path('chains/',
|
||||
include([
|
||||
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(
|
||||
'add/',
|
||||
CredebtorCreateView.as_view(),
|
||||
name='credebtor_create',
|
||||
),
|
||||
path(
|
||||
'<slug:credebtor_slug>/',
|
||||
include([
|
||||
path(
|
||||
"chains/",
|
||||
include(
|
||||
[
|
||||
path("", ChainListView.as_view(), name="chain_list"),
|
||||
path("add/", ChainCreateView.as_view(), name="chain_create"),
|
||||
path(
|
||||
"<slug:chain_slug>/",
|
||||
include(
|
||||
[
|
||||
path(
|
||||
'add_expense/',
|
||||
ExpenseCreateView.as_view(),
|
||||
name='expense_create',
|
||||
"", CredebtorListView.as_view(), name="credebtor_list"
|
||||
),
|
||||
path(
|
||||
'add_revenue/',
|
||||
RevenueCreateView.as_view(),
|
||||
name='revenue_create',
|
||||
"add/",
|
||||
CredebtorCreateView.as_view(),
|
||||
name="credebtor_create",
|
||||
),
|
||||
]),
|
||||
path(
|
||||
"<slug:credebtor_slug>/",
|
||||
include(
|
||||
[
|
||||
path(
|
||||
"add_expense/",
|
||||
ExpenseCreateView.as_view(),
|
||||
name="expense_create",
|
||||
),
|
||||
path(
|
||||
"add_revenue/",
|
||||
RevenueCreateView.as_view(),
|
||||
name="revenue_create",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
||||
# expenses
|
||||
path(
|
||||
'expenses/',
|
||||
include([
|
||||
path(
|
||||
'',
|
||||
ExpenseListView.as_view(),
|
||||
name='expense_list',
|
||||
),
|
||||
path(
|
||||
'<uuid:pk>/',
|
||||
include([
|
||||
path(
|
||||
'',
|
||||
ExpenseDetailView.as_view(),
|
||||
name='expense_detail'
|
||||
"expenses/",
|
||||
include(
|
||||
[
|
||||
path("", ExpenseListView.as_view(), name="expense_list"),
|
||||
path(
|
||||
"<uuid:pk>/",
|
||||
include(
|
||||
[
|
||||
path(
|
||||
"", ExpenseDetailView.as_view(), name="expense_detail"
|
||||
),
|
||||
path(
|
||||
"update/",
|
||||
ExpenseUpdateView.as_view(),
|
||||
name="expense_update",
|
||||
),
|
||||
path(
|
||||
"delete/",
|
||||
ExpenseDeleteView.as_view(),
|
||||
name="expense_delete",
|
||||
),
|
||||
path(
|
||||
"invoice/",
|
||||
ExpenseInvoiceView.as_view(),
|
||||
name="expense_invoice",
|
||||
),
|
||||
]
|
||||
),
|
||||
path(
|
||||
'update/',
|
||||
ExpenseUpdateView.as_view(),
|
||||
name='expense_update'
|
||||
),
|
||||
path(
|
||||
'delete/',
|
||||
ExpenseDeleteView.as_view(),
|
||||
name='expense_delete'
|
||||
),
|
||||
path(
|
||||
'invoice/',
|
||||
ExpenseInvoiceView.as_view(),
|
||||
name='expense_invoice'
|
||||
),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
||||
# reimbursements
|
||||
path(
|
||||
'reimbursements/',
|
||||
include([
|
||||
path(
|
||||
'',
|
||||
ReimbursementListView.as_view(),
|
||||
name='reimbursement_list'
|
||||
),
|
||||
path(
|
||||
'<uuid:pk>/',
|
||||
ReimbursementDetailView.as_view(),
|
||||
name='reimbursement_detail'
|
||||
),
|
||||
]),
|
||||
"reimbursements/",
|
||||
include(
|
||||
[
|
||||
path("", ReimbursementListView.as_view(), name="reimbursement_list"),
|
||||
path(
|
||||
"<uuid:pk>/",
|
||||
ReimbursementDetailView.as_view(),
|
||||
name="reimbursement_detail",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
||||
# revenue
|
||||
path(
|
||||
'revenues/',
|
||||
include([
|
||||
path(
|
||||
'',
|
||||
RevenueListView.as_view(),
|
||||
name='revenue_list'
|
||||
),
|
||||
path(
|
||||
'<uuid:pk>/',
|
||||
include([
|
||||
path(
|
||||
'',
|
||||
RevenueDetailView.as_view(),
|
||||
name='revenue_detail'
|
||||
"revenues/",
|
||||
include(
|
||||
[
|
||||
path("", RevenueListView.as_view(), name="revenue_list"),
|
||||
path(
|
||||
"<uuid:pk>/",
|
||||
include(
|
||||
[
|
||||
path(
|
||||
"", RevenueDetailView.as_view(), name="revenue_detail"
|
||||
),
|
||||
path(
|
||||
"update/",
|
||||
RevenueUpdateView.as_view(),
|
||||
name="revenue_update",
|
||||
),
|
||||
path(
|
||||
"delete/",
|
||||
RevenueDeleteView.as_view(),
|
||||
name="revenue_delete",
|
||||
),
|
||||
path(
|
||||
"invoice/",
|
||||
RevenueInvoiceView.as_view(),
|
||||
name="revenue_invoice",
|
||||
),
|
||||
]
|
||||
),
|
||||
path(
|
||||
'update/',
|
||||
RevenueUpdateView.as_view(),
|
||||
name='revenue_update'
|
||||
),
|
||||
path(
|
||||
'delete/',
|
||||
RevenueDeleteView.as_view(),
|
||||
name='revenue_delete'
|
||||
),
|
||||
path(
|
||||
'invoice/',
|
||||
RevenueInvoiceView.as_view(),
|
||||
name='revenue_invoice'
|
||||
),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from django.contrib import admin
|
||||
from .models import Type, Routing
|
||||
|
||||
|
||||
@admin.register(Type)
|
||||
class TypeAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(Routing)
|
||||
class RoutingAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class EventsConfig(AppConfig):
|
||||
name = 'events'
|
||||
name = "events"
|
||||
|
|
|
@ -2,19 +2,27 @@ from django.utils import timezone
|
|||
from datetime import timedelta
|
||||
from ircbot.utils import add_irc_message
|
||||
import logging
|
||||
|
||||
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.
|
||||
The type of event determines which teams receive notifications.
|
||||
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
|
||||
from .models import Type
|
||||
|
||||
try:
|
||||
eventtype = Type.objects.get(name=eventtype)
|
||||
except Type.DoesNotExist:
|
||||
|
@ -24,14 +32,21 @@ def handle_team_event(eventtype, irc_message=None, irc_timeout=60, email_templat
|
|||
|
||||
if not eventtype.teams:
|
||||
# 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
|
||||
|
||||
# loop over routes (teams) for this eventtype
|
||||
for team in eventtype.teams:
|
||||
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_email_notification(team=team, eventtype=eventtype, email_template=None, email_formatdict=None)
|
||||
team_irc_notification(
|
||||
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..
|
||||
|
||||
|
||||
|
@ -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
|
||||
add_irc_message(
|
||||
target=team.private_irc_channel_name,
|
||||
message=irc_message,
|
||||
timeout=60
|
||||
target=team.private_irc_channel_name, message=irc_message, timeout=60
|
||||
)
|
||||
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,
|
||||
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()]
|
||||
|
||||
# TODO: actually send the email here
|
||||
|
||||
|
|
|
@ -10,42 +10,62 @@ class Migration(migrations.Migration):
|
|||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('teams', '0025_auto_20180318_1318'),
|
||||
]
|
||||
dependencies = [("teams", "0025_auto_20180318_1318")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Routing',
|
||||
name="Routing",
|
||||
fields=[
|
||||
('id', 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)),
|
||||
(
|
||||
"id",
|
||||
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={
|
||||
'abstract': False,
|
||||
},
|
||||
options={"abstract": False},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Type',
|
||||
name="Type",
|
||||
fields=[
|
||||
('id', 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)),
|
||||
('name', models.TextField(help_text='The type of event', unique=True)),
|
||||
(
|
||||
"id",
|
||||
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)),
|
||||
("name", models.TextField(help_text="The type of event", unique=True)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
options={"abstract": False},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='routing',
|
||||
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'),
|
||||
model_name="routing",
|
||||
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",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='routing',
|
||||
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'),
|
||||
model_name="routing",
|
||||
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",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -4,17 +4,14 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_eventtypes(apps, schema_editor):
|
||||
Type = apps.get_model('events', 'Type')
|
||||
Type.objects.create(name='public_credit_name_changed')
|
||||
Type = apps.get_model("events", "Type")
|
||||
Type.objects.create(name="public_credit_name_changed")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_eventtypes),
|
||||
]
|
||||
dependencies = [("events", "0001_initial")]
|
||||
|
||||
operations = [migrations.RunPython(create_eventtypes)]
|
||||
|
|
|
@ -4,17 +4,14 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_eventtype(apps, schema_editor):
|
||||
Type = apps.get_model('events', 'Type')
|
||||
Type.objects.create(name='ticket_created')
|
||||
Type = apps.get_model("events", "Type")
|
||||
Type.objects.create(name="ticket_created")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0002_create_eventtype'),
|
||||
]
|
||||
dependencies = [("events", "0002_create_eventtype")]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_eventtype),
|
||||
]
|
||||
operations = [migrations.RunPython(create_eventtype)]
|
||||
|
|
|
@ -7,19 +7,23 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0003_create_another_eventtype'),
|
||||
]
|
||||
dependencies = [("events", "0003_create_another_eventtype")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='type',
|
||||
name='email_notification',
|
||||
field=models.BooleanField(default=False, help_text='Check to send email notifications for this type of event.'),
|
||||
model_name="type",
|
||||
name="email_notification",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="Check to send email notifications for this type of event.",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='type',
|
||||
name='irc_notification',
|
||||
field=models.BooleanField(default=False, help_text='Check to send IRC notifications for this type of event.'),
|
||||
model_name="type",
|
||||
name="irc_notification",
|
||||
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 teams.models import Team
|
||||
|
||||
|
||||
class Type(CreatedUpdatedModel):
|
||||
"""
|
||||
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
|
||||
- public_credit_name_changed: Whenever a user changes public_credit_name in the profile
|
||||
"""
|
||||
name = models.TextField(
|
||||
unique=True,
|
||||
help_text='The type of event'
|
||||
)
|
||||
|
||||
name = models.TextField(unique=True, help_text="The type of event")
|
||||
|
||||
irc_notification = models.BooleanField(
|
||||
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(
|
||||
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):
|
||||
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
|
||||
"""
|
||||
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)
|
||||
|
||||
|
||||
|
@ -42,20 +42,20 @@ class Routing(CreatedUpdatedModel):
|
|||
Add a new entry to route events of a certain type to a team.
|
||||
Several teams can receive the same type of event.
|
||||
"""
|
||||
|
||||
eventtype = models.ForeignKey(
|
||||
'events.Type',
|
||||
related_name='eventroutes',
|
||||
"events.Type",
|
||||
related_name="eventroutes",
|
||||
on_delete=models.PROTECT,
|
||||
help_text='The type of event to route',
|
||||
help_text="The type of event to route",
|
||||
)
|
||||
|
||||
team = models.ForeignKey(
|
||||
'teams.Team',
|
||||
related_name='eventroutes',
|
||||
"teams.Team",
|
||||
related_name="eventroutes",
|
||||
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):
|
||||
return "%s -> %s" % (self.eventtype, self.team)
|
||||
|
||||
|
|
|
@ -5,4 +5,4 @@ from .models import Feedback
|
|||
|
||||
@admin.register(Feedback)
|
||||
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):
|
||||
name = 'feedback'
|
||||
name = "feedback"
|
||||
|
|
|
@ -10,22 +10,32 @@ class Migration(migrations.Migration):
|
|||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Feedback',
|
||||
name="Feedback",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=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)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=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,
|
||||
},
|
||||
),
|
||||
options={"abstract": False},
|
||||
)
|
||||
]
|
||||
|
|
|
@ -6,16 +6,17 @@ import django.db.models.deletion
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('camps', '0030_camp_light_text'),
|
||||
('feedback', '0001_initial'),
|
||||
]
|
||||
dependencies = [("camps", "0030_camp_light_text"), ("feedback", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='feedback',
|
||||
name='camp',
|
||||
field=models.ForeignKey(default='30fd754f-dae4-460f-8128-6638fb29ab2d', on_delete=django.db.models.deletion.PROTECT, to='camps.Camp'),
|
||||
model_name="feedback",
|
||||
name="camp",
|
||||
field=models.ForeignKey(
|
||||
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):
|
||||
camp = models.ForeignKey('camps.Camp', on_delete=models.PROTECT)
|
||||
user = models.ForeignKey('auth.User', on_delete=models.PROTECT)
|
||||
camp = models.ForeignKey("camps.Camp", on_delete=models.PROTECT)
|
||||
user = models.ForeignKey("auth.User", on_delete=models.PROTECT)
|
||||
feedback = models.TextField()
|
||||
|
|
|
@ -11,7 +11,7 @@ from .models import Feedback
|
|||
|
||||
class FeedbackCreate(LoginRequiredMixin, CampViewMixin, CreateView):
|
||||
model = Feedback
|
||||
fields = ['feedback']
|
||||
fields = ["feedback"]
|
||||
|
||||
def form_valid(self, form):
|
||||
feedback = form.save(commit=False)
|
||||
|
@ -20,14 +20,15 @@ class FeedbackCreate(LoginRequiredMixin, CampViewMixin, CreateView):
|
|||
feedback.save()
|
||||
thanks_message = "Thank you! Your feedback is highly appreciated!"
|
||||
try:
|
||||
token = Token.objects.get(
|
||||
camp=self.camp,
|
||||
description="Feedback thanks"
|
||||
token = Token.objects.get(camp=self.camp, description="Feedback thanks")
|
||||
thanks_message += " And for your efforts, here is a token: {}".format(
|
||||
token.token
|
||||
)
|
||||
thanks_message += " And for your efforts, here is a token: {}".format(token.token)
|
||||
except Token.DoesNotExist:
|
||||
pass
|
||||
|
||||
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 reversion.admin import VersionAdmin
|
||||
from .models import (
|
||||
InfoItem,
|
||||
InfoCategory
|
||||
)
|
||||
from .models import InfoItem, InfoCategory
|
||||
|
||||
|
||||
@admin.register(InfoItem)
|
||||
class InfoItemAdmin(VersionAdmin):
|
||||
list_filter = ['category', 'category__team__camp',]
|
||||
list_display = ['headline',]
|
||||
list_filter = ["category", "category__team__camp"]
|
||||
list_display = ["headline"]
|
||||
|
||||
|
||||
class InfoItemInlineAdmin(admin.StackedInline):
|
||||
model = InfoItem
|
||||
list_filter = ['category', 'category__team__camp',]
|
||||
list_display = ['headline',]
|
||||
list_filter = ["category", "category__team__camp"]
|
||||
list_display = ["headline"]
|
||||
|
||||
|
||||
@admin.register(InfoCategory)
|
||||
class InfoCategorydmin(admin.ModelAdmin):
|
||||
list_filter = ['team__camp',]
|
||||
list_display = ['headline',]
|
||||
search_fields = ['headline', 'body']
|
||||
list_filter = ["team__camp"]
|
||||
list_display = ["headline"]
|
||||
search_fields = ["headline", "body"]
|
||||
inlines = [InfoItemInlineAdmin]
|
||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class InfoConfig(AppConfig):
|
||||
name = 'info'
|
||||
name = "info"
|
||||
|
|
|
@ -10,48 +10,107 @@ class Migration(migrations.Migration):
|
|||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('camps', '0010_auto_20161220_1714'),
|
||||
]
|
||||
dependencies = [("camps", "0010_auto_20161220_1714")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='InfoCategory',
|
||||
name="InfoCategory",
|
||||
fields=[
|
||||
('id', 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)),
|
||||
('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')),
|
||||
(
|
||||
"id",
|
||||
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)),
|
||||
(
|
||||
"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={
|
||||
'ordering': ['-weight', 'headline'],
|
||||
},
|
||||
options={"ordering": ["-weight", "headline"]},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InfoItem',
|
||||
name="InfoItem",
|
||||
fields=[
|
||||
('id', 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)),
|
||||
('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')),
|
||||
(
|
||||
"id",
|
||||
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)),
|
||||
(
|
||||
"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={
|
||||
'ordering': ['-weight', 'headline'],
|
||||
},
|
||||
options={"ordering": ["-weight", "headline"]},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='infoitem',
|
||||
unique_together=set([('headline', 'category'), ('anchor', 'category')]),
|
||||
name="infoitem",
|
||||
unique_together=set([("headline", "category"), ("anchor", "category")]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='infocategory',
|
||||
unique_together=set([('headline', 'camp'), ('anchor', 'camp')]),
|
||||
name="infocategory",
|
||||
unique_together=set([("headline", "camp"), ("anchor", "camp")]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,23 +7,30 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('info', '0001_initial'),
|
||||
]
|
||||
dependencies = [("info", "0001_initial")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='infocategory',
|
||||
options={'ordering': ['-weight', 'headline'], 'verbose_name_plural': 'Info Categories'},
|
||||
name="infocategory",
|
||||
options={
|
||||
"ordering": ["-weight", "headline"],
|
||||
"verbose_name_plural": "Info Categories",
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='infocategory',
|
||||
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.'),
|
||||
model_name="infocategory",
|
||||
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.",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='infoitem',
|
||||
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.'),
|
||||
model_name="infoitem",
|
||||
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.",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,17 +7,17 @@ from django.db import migrations
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('info', '0002_auto_20161228_2312'),
|
||||
]
|
||||
dependencies = [("info", "0002_auto_20161228_2312")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='infocategory',
|
||||
options={'ordering': ['weight', 'headline'], 'verbose_name_plural': 'Info Categories'},
|
||||
name="infocategory",
|
||||
options={
|
||||
"ordering": ["weight", "headline"],
|
||||
"verbose_name_plural": "Info Categories",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='infoitem',
|
||||
options={'ordering': ['weight', 'headline']},
|
||||
name="infoitem", options={"ordering": ["weight", "headline"]}
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,14 +7,20 @@ import django.db.models.deletion
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teams', '0042_auto_20180413_1933'),
|
||||
('info', '0003_auto_20170218_1148'),
|
||||
("teams", "0042_auto_20180413_1933"),
|
||||
("info", "0003_auto_20170218_1148"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='infocategory',
|
||||
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'),
|
||||
),
|
||||
model_name="infocategory",
|
||||
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",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -69,10 +69,6 @@ def add_teams_to_categories(apps, schema_editor):
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('info', '0004_infocategory_team'),
|
||||
]
|
||||
dependencies = [("info", "0004_infocategory_team")]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_teams_to_categories)
|
||||
]
|
||||
operations = [migrations.RunPython(add_teams_to_categories)]
|
||||
|
|
|
@ -6,22 +6,21 @@ import django.db.models.deletion
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('info', '0005_add_teams_to_categories'),
|
||||
]
|
||||
dependencies = [("info", "0005_add_teams_to_categories")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='infocategory',
|
||||
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'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='infocategory',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='infocategory',
|
||||
name='camp',
|
||||
model_name="infocategory",
|
||||
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",
|
||||
),
|
||||
),
|
||||
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):
|
||||
|
||||
dependencies = [
|
||||
('info', '0006_auto_20180520_1113'),
|
||||
]
|
||||
dependencies = [("info", "0006_auto_20180520_1113")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='infocategory',
|
||||
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'),
|
||||
),
|
||||
model_name="infocategory",
|
||||
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",
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
|
@ -7,12 +7,11 @@ import reversion
|
|||
|
||||
class InfoCategory(CampRelatedModel):
|
||||
class Meta:
|
||||
ordering = ['weight', 'headline']
|
||||
ordering = ["weight", "headline"]
|
||||
verbose_name_plural = "Info Categories"
|
||||
|
||||
headline = models.CharField(
|
||||
max_length=100,
|
||||
help_text="The headline of this info category"
|
||||
max_length=100, help_text="The headline of this info category"
|
||||
)
|
||||
|
||||
anchor = models.SlugField(
|
||||
|
@ -20,62 +19,55 @@ class InfoCategory(CampRelatedModel):
|
|||
)
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
team = models.ForeignKey(
|
||||
'teams.Team',
|
||||
help_text='The team responsible for this info category.',
|
||||
"teams.Team",
|
||||
help_text="The team responsible for this info category.",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='info_categories'
|
||||
related_name="info_categories",
|
||||
)
|
||||
|
||||
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)
|
||||
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
|
||||
def camp(self):
|
||||
return self.team.camp
|
||||
|
||||
camp_filter = 'team__camp'
|
||||
camp_filter = "team__camp"
|
||||
|
||||
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
|
||||
@reversion.register()
|
||||
class InfoItem(CampRelatedModel):
|
||||
class Meta:
|
||||
ordering = ['weight', 'headline']
|
||||
unique_together = (('anchor', 'category'), ('headline', 'category'))
|
||||
ordering = ["weight", "headline"]
|
||||
unique_together = (("anchor", "category"), ("headline", "category"))
|
||||
|
||||
category = models.ForeignKey(
|
||||
'info.InfoCategory',
|
||||
related_name='infoitems',
|
||||
on_delete=models.PROTECT
|
||||
"info.InfoCategory", related_name="infoitems", on_delete=models.PROTECT
|
||||
)
|
||||
|
||||
headline = models.CharField(
|
||||
max_length=100,
|
||||
help_text="Headline of this info item."
|
||||
)
|
||||
headline = models.CharField(max_length=100, help_text="Headline of this info item.")
|
||||
|
||||
anchor = models.SlugField(
|
||||
help_text="The HTML anchor to use for this info item."
|
||||
)
|
||||
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.'
|
||||
)
|
||||
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. 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,
|
||||
)
|
||||
|
||||
|
@ -83,12 +75,19 @@ class InfoItem(CampRelatedModel):
|
|||
def camp(self):
|
||||
return self.category.camp
|
||||
|
||||
camp_filter = 'category__team__camp'
|
||||
camp_filter = "category__team__camp"
|
||||
|
||||
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)
|
||||
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):
|
||||
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 camps.mixins import CampViewMixin
|
||||
|
||||
|
||||
class CampInfoView(CampViewMixin, ListView):
|
||||
model = InfoCategory
|
||||
template_name = 'info.html'
|
||||
context_object_name = 'categories'
|
||||
template_name = "info.html"
|
||||
context_object_name = "categories"
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super(CampInfoView, self).get_queryset()
|
||||
# do not show categories with 0 items
|
||||
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