mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-25 10:34:21 +00:00
commit
fbcf525378
@ -35,5 +35,3 @@ admin.site.register(SimplifiedAccountingType)
|
||||
admin.site.register(Operation)
|
||||
admin.site.register(Label)
|
||||
admin.site.register(Company)
|
||||
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core import validators
|
||||
from django.db.models import Count
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@ -37,6 +36,7 @@ from decimal import Decimal
|
||||
from core.models import User, SithFile
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class CurrencyField(models.DecimalField):
|
||||
"""
|
||||
This is a custom database field used for currency
|
||||
@ -56,6 +56,7 @@ class CurrencyField(models.DecimalField):
|
||||
|
||||
# Accounting classes
|
||||
|
||||
|
||||
class Company(models.Model):
|
||||
name = models.CharField(_('name'), max_length=60)
|
||||
street = models.CharField(_('street'), max_length=60, blank=True)
|
||||
@ -104,6 +105,7 @@ class Company(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class BankAccount(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
iban = models.CharField(_('iban'), max_length=255, blank=True)
|
||||
@ -131,6 +133,7 @@ class BankAccount(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ClubAccount(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
club = models.ForeignKey(Club, related_name="club_account", verbose_name=_("club"))
|
||||
@ -244,6 +247,7 @@ class GeneralJournal(models.Model):
|
||||
self.amount -= o.amount
|
||||
self.save()
|
||||
|
||||
|
||||
class Operation(models.Model):
|
||||
"""
|
||||
An operation is a line in the journal, a debit or a credit
|
||||
@ -352,6 +356,7 @@ class Operation(models.Model):
|
||||
self.amount, self.date, self.accounting_type, self.done,
|
||||
)
|
||||
|
||||
|
||||
class AccountingType(models.Model):
|
||||
"""
|
||||
Class describing the accounting types.
|
||||
@ -383,7 +388,8 @@ class AccountingType(models.Model):
|
||||
return reverse('accounting:type_list')
|
||||
|
||||
def __str__(self):
|
||||
return self.code+" - "+self.get_movement_type_display()+" - "+self.label
|
||||
return self.code + " - " + self.get_movement_type_display() + " - " + self.label
|
||||
|
||||
|
||||
class SimplifiedAccountingType(models.Model):
|
||||
"""
|
||||
@ -408,7 +414,8 @@ class SimplifiedAccountingType(models.Model):
|
||||
return reverse('accounting:simple_type_list')
|
||||
|
||||
def __str__(self):
|
||||
return self.get_movement_type_display()+" - "+self.accounting_type.code+" - "+self.label
|
||||
return self.get_movement_type_display() + " - " + self.accounting_type.code + " - " + self.label
|
||||
|
||||
|
||||
class Label(models.Model):
|
||||
"""Label allow a club to sort its operations"""
|
||||
@ -432,4 +439,3 @@ class Label(models.Model):
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
return self.club_account.can_be_viewed_by(user)
|
||||
|
||||
|
@ -22,15 +22,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.test import Client, TestCase
|
||||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from core.models import User
|
||||
from counter.models import Counter
|
||||
from accounting.models import GeneralJournal, Operation, Label, AccountingType, SimplifiedAccountingType
|
||||
|
||||
|
||||
@ -72,10 +69,11 @@ class RefoundAccountTest(TestCase):
|
||||
self.assertFalse(response_post.status_code == 403)
|
||||
self.assertTrue(self.skia.customer.amount == 0)
|
||||
|
||||
|
||||
class JournalTest(TestCase):
|
||||
def setUp(self):
|
||||
call_command("populate")
|
||||
self.journal = GeneralJournal.objects.filter(id = 1).first()
|
||||
self.journal = GeneralJournal.objects.filter(id=1).first()
|
||||
|
||||
def test_permission_granted(self):
|
||||
self.client.login(username='comptable', password='plop')
|
||||
@ -91,14 +89,15 @@ class JournalTest(TestCase):
|
||||
self.assertTrue(response_get.status_code == 403)
|
||||
self.assertFalse('<td>M\xc3\xa9thode de paiement</td>' in str(response_get.content))
|
||||
|
||||
|
||||
class OperationTest(TestCase):
|
||||
def setUp(self):
|
||||
call_command("populate")
|
||||
self.journal = GeneralJournal.objects.filter(id = 1).first()
|
||||
self.journal = GeneralJournal.objects.filter(id=1).first()
|
||||
self.skia = User.objects.filter(username='skia').first()
|
||||
at = AccountingType(code='443', label="Ce code n'existe pas", movement_type='CREDIT')
|
||||
at.save()
|
||||
l = Label(club_account= self.journal.club_account, name='bob')
|
||||
l = Label(club_account=self.journal.club_account, name='bob')
|
||||
l.save()
|
||||
self.client.login(username='comptable', password='plop')
|
||||
self.op1 = Operation(journal=self.journal, date=date.today(), amount=1,
|
||||
@ -112,73 +111,73 @@ class OperationTest(TestCase):
|
||||
|
||||
def test_new_operation(self):
|
||||
self.client.login(username='comptable', password='plop')
|
||||
at = AccountingType.objects.filter(code = '604').first()
|
||||
at = AccountingType.objects.filter(code='604').first()
|
||||
response = self.client.post(reverse('accounting:op_new',
|
||||
args=[self.journal.id]),
|
||||
{'amount': 30,
|
||||
'remark' : "Un gros test",
|
||||
'journal' : self.journal.id,
|
||||
'target_type' : 'OTHER',
|
||||
'target_id' : '',
|
||||
'target_label' : "Le fantome de la nuit",
|
||||
'date' : '04/12/2020',
|
||||
'mode' : 'CASH',
|
||||
'cheque_number' : '',
|
||||
'invoice' : '',
|
||||
'simpleaccounting_type' : '',
|
||||
'remark': "Un gros test",
|
||||
'journal': self.journal.id,
|
||||
'target_type': 'OTHER',
|
||||
'target_id': '',
|
||||
'target_label': "Le fantome de la nuit",
|
||||
'date': '04/12/2020',
|
||||
'mode': 'CASH',
|
||||
'cheque_number': '',
|
||||
'invoice': '',
|
||||
'simpleaccounting_type': '',
|
||||
'accounting_type': at.id,
|
||||
'label' : '',
|
||||
'done' : False,
|
||||
'label': '',
|
||||
'done': False,
|
||||
})
|
||||
self.assertFalse(response.status_code == 403)
|
||||
self.assertTrue(self.journal.operations.filter(target_label = "Le fantome de la nuit").exists())
|
||||
self.assertTrue(self.journal.operations.filter(target_label="Le fantome de la nuit").exists())
|
||||
response_get = self.client.get(reverse("accounting:journal_details", args=[self.journal.id]))
|
||||
self.assertTrue('<td>Le fantome de la nuit</td>' in str(response_get.content))
|
||||
|
||||
def test_bad_new_operation(self):
|
||||
self.client.login(username='comptable', password='plop')
|
||||
at = AccountingType.objects.filter(code = '604').first()
|
||||
AccountingType.objects.filter(code='604').first()
|
||||
response = self.client.post(reverse('accounting:op_new',
|
||||
args=[self.journal.id]),
|
||||
{'amount': 30,
|
||||
'remark' : "Un gros test",
|
||||
'journal' : self.journal.id,
|
||||
'target_type' : 'OTHER',
|
||||
'target_id' : '',
|
||||
'target_label' : "Le fantome de la nuit",
|
||||
'date' : '04/12/2020',
|
||||
'mode' : 'CASH',
|
||||
'cheque_number' : '',
|
||||
'invoice' : '',
|
||||
'simpleaccounting_type' : '',
|
||||
'remark': "Un gros test",
|
||||
'journal': self.journal.id,
|
||||
'target_type': 'OTHER',
|
||||
'target_id': '',
|
||||
'target_label': "Le fantome de la nuit",
|
||||
'date': '04/12/2020',
|
||||
'mode': 'CASH',
|
||||
'cheque_number': '',
|
||||
'invoice': '',
|
||||
'simpleaccounting_type': '',
|
||||
'accounting_type': '',
|
||||
'label' : '',
|
||||
'done' : False,
|
||||
'label': '',
|
||||
'done': False,
|
||||
})
|
||||
self.assertTrue('Vous devez fournir soit un type comptable simplifi\\xc3\\xa9 ou un type comptable standard' in str(response.content))
|
||||
|
||||
def test_new_operation_not_authorized(self):
|
||||
self.client.login(username='skia', password='plop')
|
||||
at = AccountingType.objects.filter(code = '604').first()
|
||||
at = AccountingType.objects.filter(code='604').first()
|
||||
response = self.client.post(reverse('accounting:op_new',
|
||||
args=[self.journal.id]),
|
||||
{'amount': 30,
|
||||
'remark' : "Un gros test",
|
||||
'journal' : self.journal.id,
|
||||
'target_type' : 'OTHER',
|
||||
'target_id' : '',
|
||||
'target_label' : "Le fantome du jour",
|
||||
'date' : '04/12/2020',
|
||||
'mode' : 'CASH',
|
||||
'cheque_number' : '',
|
||||
'invoice' : '',
|
||||
'simpleaccounting_type' : '',
|
||||
'remark': "Un gros test",
|
||||
'journal': self.journal.id,
|
||||
'target_type': 'OTHER',
|
||||
'target_id': '',
|
||||
'target_label': "Le fantome du jour",
|
||||
'date': '04/12/2020',
|
||||
'mode': 'CASH',
|
||||
'cheque_number': '',
|
||||
'invoice': '',
|
||||
'simpleaccounting_type': '',
|
||||
'accounting_type': at.id,
|
||||
'label' : '',
|
||||
'done' : False,
|
||||
'label': '',
|
||||
'done': False,
|
||||
})
|
||||
self.assertTrue(response.status_code == 403)
|
||||
self.assertFalse(self.journal.operations.filter(target_label = "Le fantome du jour").exists())
|
||||
self.assertFalse(self.journal.operations.filter(target_label="Le fantome du jour").exists())
|
||||
|
||||
def test__operation_simple_accounting(self):
|
||||
self.client.login(username='comptable', password='plop')
|
||||
@ -186,19 +185,19 @@ class OperationTest(TestCase):
|
||||
response = self.client.post(reverse('accounting:op_new',
|
||||
args=[self.journal.id]),
|
||||
{'amount': 23,
|
||||
'remark' : "Un gros test",
|
||||
'journal' : self.journal.id,
|
||||
'target_type' : 'OTHER',
|
||||
'target_id' : '',
|
||||
'target_label' : "Le fantome de l'aurore",
|
||||
'date' : '04/12/2020',
|
||||
'mode' : 'CASH',
|
||||
'cheque_number' : '',
|
||||
'invoice' : '',
|
||||
'simpleaccounting_type' : sat.id,
|
||||
'remark': "Un gros test",
|
||||
'journal': self.journal.id,
|
||||
'target_type': 'OTHER',
|
||||
'target_id': '',
|
||||
'target_label': "Le fantome de l'aurore",
|
||||
'date': '04/12/2020',
|
||||
'mode': 'CASH',
|
||||
'cheque_number': '',
|
||||
'invoice': '',
|
||||
'simpleaccounting_type': sat.id,
|
||||
'accounting_type': '',
|
||||
'label' : '',
|
||||
'done' : False,
|
||||
'label': '',
|
||||
'done': False,
|
||||
})
|
||||
self.assertFalse(response.status_code == 403)
|
||||
self.assertTrue(self.journal.operations.filter(amount=23).exists())
|
||||
@ -216,7 +215,6 @@ class OperationTest(TestCase):
|
||||
response_get = self.client.get(reverse("accounting:journal_person_statement", args=[self.journal.id]))
|
||||
self.assertTrue("S' Kia</a></td>\\n \\n <td>3.00</td>" in str(response_get.content))
|
||||
|
||||
|
||||
def test_accounting_statement(self):
|
||||
self.client.login(username='comptable', password='plop')
|
||||
response_get = self.client.get(reverse("accounting:journal_accounting_statement", args=[self.journal.id]))
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from accounting.views import *
|
||||
|
||||
@ -71,5 +71,3 @@ urlpatterns = [
|
||||
# User account
|
||||
url(r'^refound/account$', RefoundAccountView.as_view(), name='refound_account'),
|
||||
]
|
||||
|
||||
|
||||
|
@ -22,25 +22,21 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.views.generic import ListView, DetailView, RedirectView
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
|
||||
from django.shortcuts import render
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.forms.models import modelform_factory
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.forms import HiddenInput, TextInput
|
||||
from django.forms import HiddenInput
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
from django.conf import settings
|
||||
from django import forms
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from django.http import HttpResponse
|
||||
import collections
|
||||
|
||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views.forms import SelectFile, SelectDate
|
||||
@ -49,6 +45,7 @@ from counter.models import Counter, Selling, Product
|
||||
|
||||
# Main accounting view
|
||||
|
||||
|
||||
class BankAccountListView(CanViewMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -57,6 +54,7 @@ class BankAccountListView(CanViewMixin, ListView):
|
||||
template_name = 'accounting/bank_account_list.jinja'
|
||||
ordering = ['name']
|
||||
|
||||
|
||||
# Simplified accounting types
|
||||
|
||||
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
|
||||
@ -66,6 +64,7 @@ class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
|
||||
model = SimplifiedAccountingType
|
||||
template_name = 'accounting/simplifiedaccountingtype_list.jinja'
|
||||
|
||||
|
||||
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -75,6 +74,7 @@ class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
fields = ['label', 'accounting_type']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create an accounting type (for the admins)
|
||||
@ -83,6 +83,7 @@ class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
fields = ['label', 'accounting_type']
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
|
||||
# Accounting types
|
||||
|
||||
class AccountingTypeListView(CanViewMixin, ListView):
|
||||
@ -92,6 +93,7 @@ class AccountingTypeListView(CanViewMixin, ListView):
|
||||
model = AccountingType
|
||||
template_name = 'accounting/accountingtype_list.jinja'
|
||||
|
||||
|
||||
class AccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -101,6 +103,7 @@ class AccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
fields = ['code', 'label', 'movement_type']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class AccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create an accounting type (for the admins)
|
||||
@ -109,6 +112,7 @@ class AccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
fields = ['code', 'label', 'movement_type']
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
|
||||
# BankAccount views
|
||||
|
||||
class BankAccountEditView(CanViewMixin, UpdateView):
|
||||
@ -120,6 +124,7 @@ class BankAccountEditView(CanViewMixin, UpdateView):
|
||||
fields = ['name', 'iban', 'number', 'club']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class BankAccountDetailView(CanViewMixin, DetailView):
|
||||
"""
|
||||
A detail view, listing every club account
|
||||
@ -128,6 +133,7 @@ class BankAccountDetailView(CanViewMixin, DetailView):
|
||||
pk_url_kwarg = "b_account_id"
|
||||
template_name = 'accounting/bank_account_details.jinja'
|
||||
|
||||
|
||||
class BankAccountCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a bank account (for the admins)
|
||||
@ -136,6 +142,7 @@ class BankAccountCreateView(CanCreateMixin, CreateView):
|
||||
fields = ['name', 'club', 'iban', 'number']
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
|
||||
class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
||||
"""
|
||||
Delete a bank account (for the admins)
|
||||
@ -145,6 +152,7 @@ class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('accounting:bank_list')
|
||||
|
||||
|
||||
# ClubAccount views
|
||||
|
||||
class ClubAccountEditView(CanViewMixin, UpdateView):
|
||||
@ -156,6 +164,7 @@ class ClubAccountEditView(CanViewMixin, UpdateView):
|
||||
fields = ['name', 'club', 'bank_account']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class ClubAccountDetailView(CanViewMixin, DetailView):
|
||||
"""
|
||||
A detail view, listing every journal
|
||||
@ -164,6 +173,7 @@ class ClubAccountDetailView(CanViewMixin, DetailView):
|
||||
pk_url_kwarg = "c_account_id"
|
||||
template_name = 'accounting/club_account_details.jinja'
|
||||
|
||||
|
||||
class ClubAccountCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a club account (for the admins)
|
||||
@ -180,6 +190,7 @@ class ClubAccountCreateView(CanCreateMixin, CreateView):
|
||||
ret['bank_account'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
||||
"""
|
||||
Delete a club account (for the admins)
|
||||
@ -189,6 +200,7 @@ class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('accounting:bank_list')
|
||||
|
||||
|
||||
# Journal views
|
||||
|
||||
class JournalTabsMixin(TabedViewMixin):
|
||||
@ -219,13 +231,14 @@ class JournalTabsMixin(TabedViewMixin):
|
||||
})
|
||||
return tab_list
|
||||
|
||||
|
||||
class JournalCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a general journal
|
||||
"""
|
||||
model = GeneralJournal
|
||||
form_class = modelform_factory(GeneralJournal, fields=['name', 'start_date', 'club_account'],
|
||||
widgets={ 'start_date': SelectDate, })
|
||||
widgets={'start_date': SelectDate, })
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
def get_initial(self):
|
||||
@ -236,6 +249,7 @@ class JournalCreateView(CanCreateMixin, CreateView):
|
||||
ret['club_account'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
A detail view, listing every operation
|
||||
@ -245,6 +259,7 @@ class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = 'accounting/journal_details.jinja'
|
||||
current_tab = 'journal'
|
||||
|
||||
|
||||
class JournalEditView(CanEditMixin, UpdateView):
|
||||
"""
|
||||
Update a general journal
|
||||
@ -254,6 +269,7 @@ class JournalEditView(CanEditMixin, UpdateView):
|
||||
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class JournalDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""
|
||||
Delete a club account (for the admins)
|
||||
@ -270,13 +286,14 @@ class JournalDeleteView(CanEditPropMixin, DeleteView):
|
||||
else:
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
# Operation views
|
||||
|
||||
class OperationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Operation
|
||||
fields = ['amount', 'remark', 'journal', 'target_type', 'target_id', 'target_label', 'date', 'mode',
|
||||
'cheque_number', 'invoice', 'simpleaccounting_type', 'accounting_type', 'label', 'done' ]
|
||||
'cheque_number', 'invoice', 'simpleaccounting_type', 'accounting_type', 'label', 'done']
|
||||
widgets = {
|
||||
'journal': HiddenInput,
|
||||
'target_id': HiddenInput,
|
||||
@ -350,6 +367,7 @@ class OperationForm(forms.ModelForm):
|
||||
self.save()
|
||||
return ret
|
||||
|
||||
|
||||
class OperationCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create an operation
|
||||
@ -376,6 +394,7 @@ class OperationCreateView(CanCreateMixin, CreateView):
|
||||
kwargs['object'] = self.journal
|
||||
return kwargs
|
||||
|
||||
|
||||
class OperationEditView(CanEditMixin, UpdateView):
|
||||
"""
|
||||
An edit view, working as detail for the moment
|
||||
@ -391,6 +410,7 @@ class OperationEditView(CanEditMixin, UpdateView):
|
||||
kwargs['object'] = self.object.journal
|
||||
return kwargs
|
||||
|
||||
|
||||
class OperationPDFView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display the PDF of a given operation
|
||||
@ -402,11 +422,10 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
|
||||
from reportlab.platypus import Table, TableStyle
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter
|
||||
from reportlab.lib.utils import ImageReader
|
||||
from reportlab.graphics.shapes import Drawing
|
||||
from reportlab.pdfbase.ttfonts import TTFont
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
|
||||
@ -419,7 +438,6 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
num = self.object.number
|
||||
date = self.object.date
|
||||
mode = self.object.mode
|
||||
cheque_number = self.object.cheque_number
|
||||
club_name = self.object.journal.club_account.name
|
||||
ti = self.object.journal.name
|
||||
op_label = self.object.label
|
||||
@ -432,7 +450,7 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
target = self.object.target.get_display_name()
|
||||
|
||||
response = HttpResponse(content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'filename="op-%d(%s_on_%s).pdf"' %(num, ti, club_name)
|
||||
response['Content-Disposition'] = 'filename="op-%d(%s_on_%s).pdf"' % (num, ti, club_name)
|
||||
p = canvas.Canvas(response)
|
||||
|
||||
p.setFont('DejaVu', 12)
|
||||
@ -441,21 +459,21 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
width, height = letter
|
||||
im = ImageReader("core/static/core/img/logo.jpg")
|
||||
iw, ih = im.getSize()
|
||||
p.drawImage(im, 40, height - 50, width=iw/2, height=ih/2)
|
||||
p.drawImage(im, 40, height - 50, width=iw / 2, height=ih / 2)
|
||||
|
||||
labelStr = [["%s %s - %s %s" % (_("Journal"), ti, _("Operation"), num)]]
|
||||
|
||||
label = Table(labelStr, colWidths=[150], rowHeights=[20])
|
||||
|
||||
label.setStyle(TableStyle([
|
||||
('ALIGN',(0,0),(-1,-1),'RIGHT'),
|
||||
('ALIGN', (0, 0), (-1, -1), 'RIGHT'),
|
||||
]))
|
||||
w, h = label.wrapOn(label, 0, 0)
|
||||
label.drawOn(p, width-180, height)
|
||||
label.drawOn(p, width - 180, height)
|
||||
|
||||
p.drawString(90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)) #Justificatif du libellé
|
||||
p.drawString(90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)) # Justificatif du libellé
|
||||
p.drawString(90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name}))
|
||||
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label != None else ""})
|
||||
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label is not None else ""})
|
||||
p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date})
|
||||
|
||||
data = []
|
||||
@ -470,7 +488,7 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
payment_mode += "[\u00D7]"
|
||||
else:
|
||||
payment_mode += "[ ]"
|
||||
payment_mode += " %s\n" %(m[1])
|
||||
payment_mode += " %s\n" % (m[1])
|
||||
|
||||
data += [[payment_mode]]
|
||||
|
||||
@ -478,29 +496,29 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
|
||||
data += [["%s \n%s" % (_("Comment:"), remark)]]
|
||||
|
||||
t = Table(data, colWidths=[(width-90*2)/2]*2, rowHeights=[20, 20, 70, 20, 80])
|
||||
t = Table(data, colWidths=[(width - 90 * 2) / 2] * 2, rowHeights=[20, 20, 70, 20, 80])
|
||||
t.setStyle(TableStyle([
|
||||
('ALIGN',(0,0),(-1,-1),'CENTER'),
|
||||
('VALIGN',(-2,-1),(-1,-1),'TOP'),
|
||||
('VALIGN',(0,0),(-1,-2),'MIDDLE'),
|
||||
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
|
||||
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
|
||||
('VALIGN', (-2, -1), (-1, -1), 'TOP'),
|
||||
('VALIGN', (0, 0), (-1, -2), 'MIDDLE'),
|
||||
('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
|
||||
('SPAN', (0, 0), (1, 0)), # line DEBIT/CREDIT
|
||||
('SPAN', (0, 1), (1, 1)), # line amount
|
||||
('SPAN',(-2, -1), (-1,-1)), # line comment
|
||||
('SPAN', (-2, -1), (-1, -1)), # line comment
|
||||
('SPAN', (0, -2), (-1, -2)), # line creditor/debtor
|
||||
('SPAN', (0, 2), (1, 2)), # line payment_mode
|
||||
('ALIGN',(0, 2), (1, 2),'LEFT'), # line payment_mode
|
||||
('ALIGN', (0, 2), (1, 2), 'LEFT'), # line payment_mode
|
||||
('ALIGN', (-2, -1), (-1, -1), 'LEFT'),
|
||||
('BOX', (0,0), (-1,-1), 0.25, colors.black),
|
||||
('BOX', (0, 0), (-1, -1), 0.25, colors.black),
|
||||
]))
|
||||
|
||||
signature = []
|
||||
signature += [[_("Signature:")]]
|
||||
|
||||
tSig = Table(signature, colWidths=[(width-90*2)], rowHeights=[80])
|
||||
tSig = Table(signature, colWidths=[(width - 90 * 2)], rowHeights=[80])
|
||||
tSig.setStyle(TableStyle([
|
||||
('VALIGN',(0,0),(-1,-1),'TOP'),
|
||||
('BOX', (0,0), (-1,-1), 0.25, colors.black)]))
|
||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||
('BOX', (0, 0), (-1, -1), 0.25, colors.black)]))
|
||||
|
||||
w, h = tSig.wrapOn(p, 0, 0)
|
||||
tSig.drawOn(p, 90, 200)
|
||||
@ -516,14 +534,15 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
p.save()
|
||||
return response
|
||||
|
||||
|
||||
class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a statement sorted by labels
|
||||
"""
|
||||
model = GeneralJournal
|
||||
pk_url_kwarg = "j_id"
|
||||
template_name='accounting/journal_statement_nature.jinja'
|
||||
current_tab='nature_statement'
|
||||
template_name = 'accounting/journal_statement_nature.jinja'
|
||||
current_tab = 'nature_statement'
|
||||
|
||||
def statement(self, queryset, movement_type):
|
||||
ret = collections.OrderedDict()
|
||||
@ -532,13 +551,15 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
for sat in [None] + list(SimplifiedAccountingType.objects.order_by('label').all()):
|
||||
sum = queryset.filter(accounting_type__movement_type=movement_type,
|
||||
simpleaccounting_type=sat).aggregate(amount_sum=Sum('amount'))['amount_sum']
|
||||
if sat: sat = sat.label
|
||||
else: sat = ""
|
||||
if sat:
|
||||
sat = sat.label
|
||||
else:
|
||||
sat = ""
|
||||
if sum:
|
||||
total_sum += sum
|
||||
statement[sat] = sum
|
||||
ret[movement_type] = statement
|
||||
ret[movement_type+"_sum"] = total_sum
|
||||
ret[movement_type + "_sum"] = total_sum
|
||||
return ret
|
||||
|
||||
def big_statement(self):
|
||||
@ -566,14 +587,15 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['statement'] = self.big_statement()
|
||||
return kwargs
|
||||
|
||||
|
||||
class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Calculate a dictionary with operation target and sum of operations
|
||||
"""
|
||||
model = GeneralJournal
|
||||
pk_url_kwarg = "j_id"
|
||||
template_name='accounting/journal_statement_person.jinja'
|
||||
current_tab='person_statement'
|
||||
template_name = 'accounting/journal_statement_person.jinja'
|
||||
current_tab = 'person_statement'
|
||||
|
||||
def sum_by_target(self, target_id, target_type, movement_type):
|
||||
return self.object.operations.filter(accounting_type__movement_type=movement_type,
|
||||
@ -598,13 +620,14 @@ class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['total_debit'] = self.total("DEBIT")
|
||||
return kwargs
|
||||
|
||||
|
||||
class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Calculate a dictionary with operation type and sum of operations
|
||||
"""
|
||||
model = GeneralJournal
|
||||
pk_url_kwarg = "j_id"
|
||||
template_name='accounting/journal_statement_accounting.jinja'
|
||||
template_name = 'accounting/journal_statement_accounting.jinja'
|
||||
current_tab = "accounting_statement"
|
||||
|
||||
def statement(self):
|
||||
@ -624,10 +647,12 @@ class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView)
|
||||
|
||||
# Company views
|
||||
|
||||
|
||||
class CompanyListView(CanViewMixin, ListView):
|
||||
model = Company
|
||||
template_name = 'accounting/co_list.jinja'
|
||||
|
||||
|
||||
class CompanyCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a company
|
||||
@ -648,6 +673,7 @@ class CompanyEditView(CanCreateMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
success_url = reverse_lazy('accounting:co_list')
|
||||
|
||||
|
||||
# Label views
|
||||
|
||||
class LabelListView(CanViewMixin, DetailView):
|
||||
@ -655,6 +681,7 @@ class LabelListView(CanViewMixin, DetailView):
|
||||
pk_url_kwarg = "clubaccount_id"
|
||||
template_name = 'accounting/label_list.jinja'
|
||||
|
||||
|
||||
class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the rights before creating the object
|
||||
model = Label
|
||||
form_class = modelform_factory(Label, fields=['name', 'club_account'], widgets={
|
||||
@ -670,12 +697,14 @@ class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the
|
||||
ret['club_account'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
class LabelEditView(CanEditMixin, UpdateView):
|
||||
model = Label
|
||||
pk_url_kwarg = "label_id"
|
||||
fields = ['name']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class LabelDeleteView(CanEditMixin, DeleteView):
|
||||
model = Label
|
||||
pk_url_kwarg = "label_id"
|
||||
@ -684,9 +713,11 @@ class LabelDeleteView(CanEditMixin, DeleteView):
|
||||
def get_success_url(self):
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
|
||||
class CloseCustomerAccountForm(forms.Form):
|
||||
user = AutoCompleteSelectField('users', label=_('Refound this account'), help_text=None, required=True)
|
||||
|
||||
|
||||
class RefoundAccountView(FormView):
|
||||
"""
|
||||
Create a selling with the same amount than the current user money
|
||||
|
@ -29,4 +29,3 @@ from club.models import Club, Membership
|
||||
|
||||
admin.site.register(Club)
|
||||
admin.site.register(Membership)
|
||||
|
||||
|
@ -27,12 +27,13 @@ from django.core import validators
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db import transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from core.models import User, MetaGroup, Group, SithFile
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class Club(models.Model):
|
||||
@ -109,9 +110,9 @@ class Club(models.Model):
|
||||
self._change_unixname(self.unix_name)
|
||||
super(Club, self).save(*args, **kwargs)
|
||||
if creation:
|
||||
board = MetaGroup(name=self.unix_name+settings.SITH_BOARD_SUFFIX)
|
||||
board = MetaGroup(name=self.unix_name + settings.SITH_BOARD_SUFFIX)
|
||||
board.save()
|
||||
member = MetaGroup(name=self.unix_name+settings.SITH_MEMBER_SUFFIX)
|
||||
member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX)
|
||||
member.save()
|
||||
subscribers = Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first()
|
||||
self.make_home()
|
||||
@ -153,6 +154,7 @@ class Club(models.Model):
|
||||
return sub.is_subscribed
|
||||
|
||||
_memberships = {}
|
||||
|
||||
def get_membership_for(self, user):
|
||||
"""
|
||||
Returns the current membership the given user
|
||||
@ -168,6 +170,7 @@ class Club(models.Model):
|
||||
Club._memberships[self.id][user.id] = m
|
||||
return m
|
||||
|
||||
|
||||
class Membership(models.Model):
|
||||
"""
|
||||
The Membership class makes the connection between User and Clubs
|
||||
@ -195,8 +198,8 @@ class Membership(models.Model):
|
||||
raise ValidationError(_('User is already member of that club'))
|
||||
|
||||
def __str__(self):
|
||||
return self.club.name+' - '+self.user.username+' - '+str(settings.SITH_CLUB_ROLES[self.role])+str(
|
||||
" - "+str(_('past member')) if self.end_date is not None else ""
|
||||
return self.club.name + ' - ' + self.user.username + ' - ' + str(settings.SITH_CLUB_ROLES[self.role]) + str(
|
||||
" - " + str(_('past member')) if self.end_date is not None else ""
|
||||
)
|
||||
|
||||
def is_owned_by(self, user):
|
||||
@ -216,4 +219,3 @@ class Membership(models.Model):
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('club:club_members', kwargs={'club_id': self.club.id})
|
||||
|
||||
|
@ -31,6 +31,7 @@ from club.models import Club
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
|
||||
class ClubTest(TestCase):
|
||||
def setUp(self):
|
||||
call_command("populate")
|
||||
@ -41,34 +42,34 @@ class ClubTest(TestCase):
|
||||
|
||||
def test_create_add_user_to_club_from_root_ok(self):
|
||||
self.client.login(username='root', password='plop')
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.skia.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 3})
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("S' Kia</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
||||
|
||||
def test_create_add_user_to_club_from_root_fail_not_subscriber(self):
|
||||
self.client.login(username='root', password='plop')
|
||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.guy.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 3})
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue('<ul class="errorlist nonfield"><li>' in str(response.content))
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||
self.assertFalse("Guy Carlier</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
||||
|
||||
def test_create_add_user_to_club_from_root_fail_already_in_club(self):
|
||||
self.client.login(username='root', password='plop')
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.skia.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 3})
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||
self.assertTrue("S' Kia</a></td>\\n <td>Responsable info</td>" in str(response.content))
|
||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.skia.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 4})
|
||||
@ -77,30 +78,29 @@ class ClubTest(TestCase):
|
||||
|
||||
def test_create_add_user_to_club_from_skia_ok(self):
|
||||
self.client.login(username='root', password='plop')
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.skia.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 10})
|
||||
self.client.login(username='skia', password='plop')
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.rbatsbak.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 9})
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id":self.bdf.id}))
|
||||
response = self.client.get(reverse("club:club_members", kwargs={"club_id": self.bdf.id}))
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("""Richard Batsbak</a></td>\\n <td>Vice-Pr\\xc3\\xa9sident</td>""" in str(response.content))
|
||||
|
||||
def test_create_add_user_to_club_from_richard_fail(self):
|
||||
self.client.login(username='root', password='plop')
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.rbatsbak.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 3})
|
||||
self.client.login(username='rbatsbak', password='plop')
|
||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id":self.bdf.id}), {
|
||||
response = self.client.post(reverse("club:club_members", kwargs={"club_id": self.bdf.id}), {
|
||||
"user": self.skia.id,
|
||||
"start_date": "12/06/2016",
|
||||
"role": 10})
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("<li>Vous n'avez pas la permission de faire cela</li>" in str(response.content))
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from club.views import *
|
||||
|
||||
@ -40,4 +40,3 @@ urlpatterns = [
|
||||
url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
|
||||
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
|
||||
]
|
||||
|
||||
|
@ -23,27 +23,21 @@
|
||||
#
|
||||
|
||||
from django import forms
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext as _t
|
||||
from django.conf import settings
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
|
||||
from core.views.forms import SelectDate, SelectSingle, SelectDateTime
|
||||
from core.views.forms import SelectDate, SelectDateTime
|
||||
from club.models import Club, Membership
|
||||
from core.models import User
|
||||
from sith.settings import SITH_MAXIMUM_FREE_ROLE, SITH_MAIN_BOARD_GROUP
|
||||
from counter.models import Product, Selling, Counter
|
||||
from sith.settings import SITH_MAXIMUM_FREE_ROLE
|
||||
from counter.models import Selling, Counter
|
||||
|
||||
|
||||
class ClubTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
@ -91,6 +85,7 @@ class ClubTabsMixin(TabedViewMixin):
|
||||
})
|
||||
return tab_list
|
||||
|
||||
|
||||
class ClubListView(ListView):
|
||||
"""
|
||||
List the Clubs
|
||||
@ -98,6 +93,7 @@ class ClubListView(ListView):
|
||||
model = Club
|
||||
template_name = 'club/club_list.jinja'
|
||||
|
||||
|
||||
class ClubView(ClubTabsMixin, DetailView):
|
||||
"""
|
||||
Front page of a Club
|
||||
@ -107,6 +103,7 @@ class ClubView(ClubTabsMixin, DetailView):
|
||||
template_name = 'club/club_detail.jinja'
|
||||
current_tab = "infos"
|
||||
|
||||
|
||||
class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
"""
|
||||
Tools page of a Club
|
||||
@ -116,12 +113,14 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
template_name = 'club/club_tools.jinja'
|
||||
current_tab = "tools"
|
||||
|
||||
|
||||
class ClubMemberForm(forms.ModelForm):
|
||||
"""
|
||||
Form handling the members of a club
|
||||
"""
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = Membership
|
||||
fields = ['user', 'role', 'start_date', 'description']
|
||||
@ -134,9 +133,10 @@ class ClubMemberForm(forms.ModelForm):
|
||||
"""
|
||||
Overloaded to return the club, and not to a Membership object that has no view
|
||||
"""
|
||||
ret = super(ClubMemberForm, self).save(*args, **kwargs)
|
||||
super(ClubMemberForm, self).save(*args, **kwargs)
|
||||
return self.instance.club
|
||||
|
||||
|
||||
class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
||||
"""
|
||||
View of a club's members
|
||||
@ -182,6 +182,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Old members of a club
|
||||
@ -191,11 +192,13 @@ class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = 'club/club_old_members.jinja'
|
||||
current_tab = "elderlies"
|
||||
|
||||
|
||||
class SellingsFormBase(forms.Form):
|
||||
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
||||
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
||||
counter = forms.ModelChoiceField(Counter.objects.order_by('name').all(), label=_("Counter"), required=False)
|
||||
|
||||
|
||||
class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
"""
|
||||
Sellings of a club
|
||||
@ -235,6 +238,7 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
kwargs['form'] = form
|
||||
return kwargs
|
||||
|
||||
|
||||
class ClubSellingCSVView(ClubSellingView):
|
||||
"""
|
||||
Generate sellings in csv for a given period
|
||||
@ -252,27 +256,31 @@ class ClubSellingCSVView(ClubSellingView):
|
||||
writer.writerow([_t('Quantity'), kwargs['total_quantity']])
|
||||
writer.writerow([_t('Total'), kwargs['total']])
|
||||
writer.writerow([_t('Benefit'), kwargs['benefit']])
|
||||
writer.writerow([_t('Date'),_t('Counter'),_t('Barman'),_t('Customer'),_t('Label'),
|
||||
_t('Quantity'), _t('Total'),_t('Payment method'), _t('Selling price'), _t('Purchase price'), _t('Benefit')])
|
||||
writer.writerow([_t('Date'), _t('Counter'), _t('Barman'), _t('Customer'), _t('Label'),
|
||||
_t('Quantity'), _t('Total'), _t('Payment method'), _t('Selling price'), _t('Purchase price'), _t('Benefit')])
|
||||
for o in kwargs['result']:
|
||||
row = [o.date, o.counter]
|
||||
if o.seller:
|
||||
row.append(o.seller.get_display_name())
|
||||
else: row.append('')
|
||||
else:
|
||||
row.append('')
|
||||
if o.customer:
|
||||
row.append(o.customer.user.get_display_name())
|
||||
else: row.append('')
|
||||
row = row +[o.label, o.quantity, o.quantity * o.unit_price,
|
||||
else:
|
||||
row.append('')
|
||||
row = row + [o.label, o.quantity, o.quantity * o.unit_price,
|
||||
o.get_payment_method_display()]
|
||||
if o.product:
|
||||
row.append(o.product.selling_price)
|
||||
row.append(o.product.purchase_price)
|
||||
row.append(o.product.selling_price - o.product.purchase_price)
|
||||
else: row = row + ['', '', '']
|
||||
else:
|
||||
row = row + ['', '', '']
|
||||
writer.writerow(row)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
||||
"""
|
||||
Edit a Club's main informations (for the club's members)
|
||||
@ -283,6 +291,7 @@ class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "edit"
|
||||
|
||||
|
||||
class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
|
||||
"""
|
||||
Edit the properties of a Club object (for the Sith admins)
|
||||
@ -293,6 +302,7 @@ class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "props"
|
||||
|
||||
|
||||
class ClubCreateView(CanEditPropMixin, CreateView):
|
||||
"""
|
||||
Create a club (for the Sith admin)
|
||||
@ -302,6 +312,7 @@ class ClubCreateView(CanEditPropMixin, CreateView):
|
||||
fields = ['name', 'unix_name', 'parent']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class MembershipSetOldView(CanEditMixin, DetailView):
|
||||
"""
|
||||
Set a membership as beeing old
|
||||
@ -319,8 +330,9 @@ class MembershipSetOldView(CanEditMixin, DetailView):
|
||||
self.object = self.get_object()
|
||||
return HttpResponseRedirect(reverse('club:club_members', args=self.args, kwargs={'club_id': self.object.club.id}))
|
||||
|
||||
|
||||
class ClubStatView(TemplateView):
|
||||
template_name="club/stats.jinja"
|
||||
template_name = "club/stats.jinja"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(ClubStatView, self).get_context_data(**kwargs)
|
||||
|
@ -30,5 +30,3 @@ admin.site.register(Sith)
|
||||
admin.site.register(News)
|
||||
admin.site.register(Weekmail)
|
||||
|
||||
|
||||
|
||||
|
@ -25,15 +25,14 @@
|
||||
from django.shortcuts import render
|
||||
from django.db import models, transaction
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from core.models import User, Preferences
|
||||
from club.models import Club
|
||||
import os
|
||||
|
||||
|
||||
class Sith(models.Model):
|
||||
"""A one instance class storing all the modifiable infos"""
|
||||
@ -48,12 +47,14 @@ class Sith(models.Model):
|
||||
def __str__(self):
|
||||
return "⛩ Sith ⛩"
|
||||
|
||||
|
||||
NEWS_TYPES = [
|
||||
('NOTICE', _('Notice')),
|
||||
('EVENT', _('Event')),
|
||||
('WEEKLY', _('Weekly')),
|
||||
('CALL', _('Call')),
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class News(models.Model):
|
||||
"""The news class"""
|
||||
@ -81,6 +82,7 @@ class News(models.Model):
|
||||
def __str__(self):
|
||||
return "%s: %s" % (self.type, self.title)
|
||||
|
||||
|
||||
class NewsDate(models.Model):
|
||||
"""
|
||||
A date class, useful for weekly events, or for events that just have no date
|
||||
@ -95,6 +97,7 @@ class NewsDate(models.Model):
|
||||
def __str__(self):
|
||||
return "%s: %s - %s" % (self.news.title, self.start_date, self.end_date)
|
||||
|
||||
|
||||
class Weekmail(models.Model):
|
||||
"""
|
||||
The weekmail class
|
||||
@ -144,6 +147,7 @@ class Weekmail(models.Model):
|
||||
def is_owned_by(self, user):
|
||||
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
|
||||
|
||||
|
||||
class WeekmailArticle(models.Model):
|
||||
weekmail = models.ForeignKey(Weekmail, related_name="articles", verbose_name=_("weekmail"), null=True)
|
||||
title = models.CharField(_("title"), max_length=64)
|
||||
|
@ -28,7 +28,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.core.management import call_command
|
||||
|
||||
from core.models import User, RealGroup
|
||||
from com.models import Sith
|
||||
|
||||
|
||||
class ComTest(TestCase):
|
||||
def setUp(self):
|
||||
@ -56,4 +56,3 @@ class ComTest(TestCase):
|
||||
r = self.client.get(reverse("core:index"))
|
||||
self.assertTrue(r.status_code == 200)
|
||||
self.assertTrue("""<div id="info_box">\\n <div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""" in str(r.content))
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from com.views import *
|
||||
|
||||
|
48
com/views.py
48
com/views.py
@ -22,9 +22,9 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.generic import ListView, DetailView, RedirectView
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@ -49,6 +49,7 @@ from club.models import Club
|
||||
|
||||
sith = Sith.objects.first
|
||||
|
||||
|
||||
class ComTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
return _("Communication administration")
|
||||
@ -82,6 +83,7 @@ class ComTabsMixin(TabedViewMixin):
|
||||
})
|
||||
return tab_list
|
||||
|
||||
|
||||
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
||||
model = Sith
|
||||
template_name = 'core/edit.jinja'
|
||||
@ -89,21 +91,25 @@ class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
||||
def get_object(self, queryset=None):
|
||||
return Sith.objects.first()
|
||||
|
||||
|
||||
class AlertMsgEditView(ComEditView):
|
||||
fields = ['alert_msg']
|
||||
current_tab = "alert"
|
||||
success_url = reverse_lazy('com:alert_edit')
|
||||
|
||||
|
||||
class InfoMsgEditView(ComEditView):
|
||||
fields = ['info_msg']
|
||||
current_tab = "info"
|
||||
success_url = reverse_lazy('com:info_edit')
|
||||
|
||||
|
||||
class IndexEditView(ComEditView):
|
||||
fields = ['index_page']
|
||||
current_tab = "index"
|
||||
success_url = reverse_lazy('com:index_edit')
|
||||
|
||||
|
||||
class WeekmailDestinationEditView(ComEditView):
|
||||
fields = ['weekmail_destinations']
|
||||
current_tab = "weekmail_destinations"
|
||||
@ -111,6 +117,7 @@ class WeekmailDestinationEditView(ComEditView):
|
||||
|
||||
# News
|
||||
|
||||
|
||||
class NewsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = News
|
||||
@ -155,6 +162,7 @@ class NewsForm(forms.ModelForm):
|
||||
end_date += timedelta(days=7)
|
||||
return ret
|
||||
|
||||
|
||||
class NewsEditView(CanEditMixin, UpdateView):
|
||||
model = News
|
||||
form_class = NewsForm
|
||||
@ -165,15 +173,17 @@ class NewsEditView(CanEditMixin, UpdateView):
|
||||
init = {}
|
||||
try:
|
||||
init['start_date'] = self.object.dates.order_by('id').first().start_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
init['end_date'] = self.object.dates.order_by('id').first().end_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid() and not 'preview' in request.POST.keys():
|
||||
if form.is_valid() and 'preview' not in request.POST.keys():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
@ -192,6 +202,7 @@ class NewsEditView(CanEditMixin, UpdateView):
|
||||
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
||||
return super(NewsEditView, self).form_valid(form)
|
||||
|
||||
|
||||
class NewsCreateView(CanCreateMixin, CreateView):
|
||||
model = News
|
||||
form_class = NewsForm
|
||||
@ -201,12 +212,13 @@ class NewsCreateView(CanCreateMixin, CreateView):
|
||||
init = {'author': self.request.user}
|
||||
try:
|
||||
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid() and not 'preview' in request.POST.keys():
|
||||
if form.is_valid() and 'preview' not in request.POST.keys():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
self.object = form.instance
|
||||
@ -224,6 +236,7 @@ class NewsCreateView(CanCreateMixin, CreateView):
|
||||
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
||||
return super(NewsCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
||||
model = News
|
||||
pk_url_kwarg = 'news_id'
|
||||
@ -240,11 +253,13 @@ class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
||||
return redirect(self.request.GET['next'])
|
||||
return redirect('com:news_admin_list')
|
||||
|
||||
|
||||
class NewsAdminListView(CanEditMixin, ListView):
|
||||
model = News
|
||||
template_name = 'com/news_admin_list.jinja'
|
||||
queryset = News.objects.filter(dates__end_date__gte=timezone.now()).distinct().order_by('id')
|
||||
|
||||
|
||||
class NewsListView(CanViewMixin, ListView):
|
||||
model = News
|
||||
template_name = 'com/news_list.jinja'
|
||||
@ -255,6 +270,7 @@ class NewsListView(CanViewMixin, ListView):
|
||||
kwargs['timedelta'] = timedelta
|
||||
return kwargs
|
||||
|
||||
|
||||
class NewsDetailView(CanViewMixin, DetailView):
|
||||
model = News
|
||||
template_name = 'com/news_detail.jinja'
|
||||
@ -262,6 +278,7 @@ class NewsDetailView(CanViewMixin, DetailView):
|
||||
|
||||
# Weekmail
|
||||
|
||||
|
||||
class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
||||
model = Weekmail
|
||||
template_name = 'com/weekmail_preview.jinja'
|
||||
@ -274,7 +291,8 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
||||
if request.POST['send'] == "validate":
|
||||
self.object.send()
|
||||
return HttpResponseRedirect(reverse('com:weekmail') + "?qn_weekmail_send_success")
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(WeekmailEditView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@ -286,6 +304,7 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
||||
kwargs['weekmail_rendered'] = self.object.render_html()
|
||||
return kwargs
|
||||
|
||||
|
||||
class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
||||
model = Weekmail
|
||||
template_name = 'com/weekmail.jinja'
|
||||
@ -341,6 +360,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
|
||||
kwargs['orphans'] = WeekmailArticle.objects.filter(weekmail=None)
|
||||
return kwargs
|
||||
|
||||
|
||||
class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
||||
"""Edit an article"""
|
||||
model = WeekmailArticle
|
||||
@ -351,6 +371,7 @@ class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, U
|
||||
quick_notif_url_arg = "qn_weekmail_article_edit"
|
||||
current_tab = "weekmail"
|
||||
|
||||
|
||||
class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||
"""Post an article"""
|
||||
model = WeekmailArticle
|
||||
@ -363,13 +384,14 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||
init = {}
|
||||
try:
|
||||
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
self.object = form.instance
|
||||
form.is_valid() # Valid a first time to populate club field
|
||||
form.is_valid() # Valid a first time to populate club field
|
||||
try:
|
||||
m = form.instance.club.get_membership_for(request.user)
|
||||
if m.role <= settings.SITH_MAXIMUM_FREE_ROLE:
|
||||
@ -385,14 +407,10 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||
form.instance.author = self.request.user
|
||||
return super(WeekmailArticleCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""Delete an article"""
|
||||
model = WeekmailArticle
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('com:weekmail')
|
||||
pk_url_kwarg = "article_id"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@ admin.site.unregister(AuthGroup)
|
||||
admin.site.register(RealGroup)
|
||||
admin.site.register(Page)
|
||||
|
||||
|
||||
@admin.register(SithFile)
|
||||
class SithFileAdmin(admin.ModelAdmin):
|
||||
form = make_ajax_form(SithFile, {
|
||||
|
@ -23,9 +23,9 @@
|
||||
#
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.dispatch import receiver
|
||||
from django.core.signals import request_started
|
||||
|
||||
|
||||
class SithConfig(AppConfig):
|
||||
name = 'core'
|
||||
verbose_name = "Core app of the Sith"
|
||||
@ -48,4 +48,3 @@ class SithConfig(AppConfig):
|
||||
request_started.connect(clear_cached_groups, weak=False, dispatch_uid="clear_cached_groups")
|
||||
request_started.connect(clear_cached_memberships, weak=False, dispatch_uid="clear_cached_memberships")
|
||||
# TODO: there may be a need to add more cache clearing
|
||||
|
||||
|
@ -31,16 +31,19 @@ from club.models import Club
|
||||
from counter.models import Product, Counter
|
||||
from accounting.models import ClubAccount, Company
|
||||
|
||||
|
||||
def check_token(request):
|
||||
return ('counter_token' in request.session.keys() and
|
||||
request.session['counter_token'] and
|
||||
Counter.objects.filter(token=request.session['counter_token']).exists())
|
||||
|
||||
|
||||
class RightManagedLookupChannel(LookupChannel):
|
||||
def check_auth(self, request):
|
||||
if not request.user.was_subscribed and not check_token(request):
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
@register('users')
|
||||
class UsersLookup(RightManagedLookupChannel):
|
||||
model = User
|
||||
@ -54,6 +57,7 @@ class UsersLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.get_display_name()
|
||||
|
||||
|
||||
@register('groups')
|
||||
class GroupsLookup(RightManagedLookupChannel):
|
||||
model = Group
|
||||
@ -67,6 +71,7 @@ class GroupsLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('clubs')
|
||||
class ClubLookup(RightManagedLookupChannel):
|
||||
model = Club
|
||||
@ -80,6 +85,7 @@ class ClubLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('counters')
|
||||
class CountersLookup(RightManagedLookupChannel):
|
||||
model = Counter
|
||||
@ -90,6 +96,7 @@ class CountersLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('products')
|
||||
class ProductsLookup(RightManagedLookupChannel):
|
||||
model = Product
|
||||
@ -101,6 +108,7 @@ class ProductsLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return "%s (%s)" % (item.name, item.code)
|
||||
|
||||
|
||||
@register('files')
|
||||
class SithFileLookup(RightManagedLookupChannel):
|
||||
model = SithFile
|
||||
@ -108,6 +116,7 @@ class SithFileLookup(RightManagedLookupChannel):
|
||||
def get_query(self, q, request):
|
||||
return self.model.objects.filter(name__icontains=q)[:50]
|
||||
|
||||
|
||||
@register('club_accounts')
|
||||
class ClubAccountLookup(RightManagedLookupChannel):
|
||||
model = ClubAccount
|
||||
@ -118,6 +127,7 @@ class ClubAccountLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('companies')
|
||||
class CompaniesLookup(RightManagedLookupChannel):
|
||||
model = Company
|
||||
|
@ -21,4 +21,3 @@
|
||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
#
|
||||
|
||||
|
@ -44,7 +44,6 @@ class Command(BaseCommand):
|
||||
args['precision'] = settings.SASS_PRECISION
|
||||
return sass.compile(**args)
|
||||
|
||||
|
||||
def is_compilable(self, file, ext_list):
|
||||
path, ext = os.path.splitext(file)
|
||||
return ext in ext_list
|
||||
|
@ -26,7 +26,7 @@ import os
|
||||
from datetime import date, datetime
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
@ -42,7 +42,7 @@ from subscription.models import Subscription
|
||||
from counter.models import Customer, ProductType, Product, Counter
|
||||
from com.models import Sith, Weekmail
|
||||
from election.models import Election, Role, Candidature, ElectionList
|
||||
from forum.models import Forum, ForumMessage, ForumTopic
|
||||
from forum.models import Forum, ForumTopic
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@ -99,7 +99,7 @@ class Command(BaseCommand):
|
||||
launderette_club.save()
|
||||
self.reset_index("club")
|
||||
for b in settings.SITH_COUNTER_BARS:
|
||||
g = Group(name=b[1]+" admin")
|
||||
g = Group(name=b[1] + " admin")
|
||||
g.save()
|
||||
c = Counter(id=b[0], name=b[1], club=bar_club, type='BAR')
|
||||
c.save()
|
||||
@ -120,7 +120,7 @@ class Command(BaseCommand):
|
||||
p = Page(name='Index')
|
||||
p.set_lock(root)
|
||||
p.save()
|
||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.set_lock(root)
|
||||
p.save()
|
||||
PageRev(page=p, title="Wiki index", author=root, content="""
|
||||
@ -130,7 +130,7 @@ Welcome to the wiki page!
|
||||
p = Page(name="services")
|
||||
p.set_lock(root)
|
||||
p.save()
|
||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.set_lock(root)
|
||||
PageRev(page=p, title="Services", author=root, content="""
|
||||
| | | |
|
||||
@ -154,7 +154,7 @@ Welcome to the wiki page!
|
||||
date_of_birth="1942-06-12")
|
||||
skia.set_password("plop")
|
||||
skia.save()
|
||||
skia.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
skia.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
skia.save()
|
||||
skia_profile_path = os.path.join(root_path, 'core/fixtures/images/3.jpg')
|
||||
with open(skia_profile_path, 'rb') as f:
|
||||
@ -174,7 +174,7 @@ Welcome to the wiki page!
|
||||
is_superuser=False, is_staff=False)
|
||||
public.set_password("plop")
|
||||
public.save()
|
||||
public.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
public.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
public.save()
|
||||
# Adding user Subscriber
|
||||
subscriber = User(username='subscriber', last_name="User", first_name="Subscribed",
|
||||
@ -183,7 +183,7 @@ Welcome to the wiki page!
|
||||
is_superuser=False, is_staff=False)
|
||||
subscriber.set_password("plop")
|
||||
subscriber.save()
|
||||
subscriber.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
subscriber.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
subscriber.save()
|
||||
# Adding user old Subscriber
|
||||
old_subscriber = User(username='old_subscriber', last_name="Subscriber", first_name="Old",
|
||||
@ -192,7 +192,7 @@ Welcome to the wiki page!
|
||||
is_superuser=False, is_staff=False)
|
||||
old_subscriber.set_password("plop")
|
||||
old_subscriber.save()
|
||||
old_subscriber.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
old_subscriber.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
old_subscriber.save()
|
||||
# Adding user Counter admin
|
||||
counter = User(username='counter', last_name="Ter", first_name="Coun",
|
||||
@ -201,8 +201,8 @@ Welcome to the wiki page!
|
||||
is_superuser=False, is_staff=False)
|
||||
counter.set_password("plop")
|
||||
counter.save()
|
||||
counter.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
counter.groups=[Group.objects.filter(id=settings.SITH_GROUP_COUNTER_ADMIN_ID).first().id]
|
||||
counter.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
counter.groups = [Group.objects.filter(id=settings.SITH_GROUP_COUNTER_ADMIN_ID).first().id]
|
||||
counter.save()
|
||||
# Adding user Comptable
|
||||
comptable = User(username='comptable', last_name="Able", first_name="Compte",
|
||||
@ -211,8 +211,8 @@ Welcome to the wiki page!
|
||||
is_superuser=False, is_staff=False)
|
||||
comptable.set_password("plop")
|
||||
comptable.save()
|
||||
comptable.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
comptable.groups=[Group.objects.filter(id=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID).first().id]
|
||||
comptable.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
comptable.groups = [Group.objects.filter(id=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID).first().id]
|
||||
comptable.save()
|
||||
# Adding user Guy
|
||||
u = User(username='guy', last_name="Carlier", first_name="Guy",
|
||||
@ -221,7 +221,7 @@ Welcome to the wiki page!
|
||||
is_superuser=False, is_staff=False)
|
||||
u.set_password("plop")
|
||||
u.save()
|
||||
u.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
u.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
u.save()
|
||||
# Adding user Richard Batsbak
|
||||
r = User(username='rbatsbak', last_name="Batsbak", first_name="Richard",
|
||||
@ -229,18 +229,18 @@ Welcome to the wiki page!
|
||||
date_of_birth="1982-06-12")
|
||||
r.set_password("plop")
|
||||
r.save()
|
||||
r.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
r.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
r.save()
|
||||
# Adding syntax help page
|
||||
p = Page(name='Aide_sur_la_syntaxe')
|
||||
p.save(force_lock=True)
|
||||
with open(os.path.join(root_path)+'/doc/SYNTAX.md', 'r') as rm:
|
||||
with open(os.path.join(root_path) + '/doc/SYNTAX.md', 'r') as rm:
|
||||
PageRev(page=p, title="Aide sur la syntaxe", author=skia, content=rm.read()).save()
|
||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.save(force_lock=True)
|
||||
p = Page(name='Services')
|
||||
p.save(force_lock=True)
|
||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.save(force_lock=True)
|
||||
PageRev(page=p, title="Services", author=skia, content="""
|
||||
| | | |
|
||||
@ -252,13 +252,13 @@ Welcome to the wiki page!
|
||||
# Adding README
|
||||
p = Page(name='README')
|
||||
p.save(force_lock=True)
|
||||
p.view_groups=[settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.view_groups = [settings.SITH_GROUP_PUBLIC_ID]
|
||||
p.save(force_lock=True)
|
||||
with open(os.path.join(root_path)+'/README.md', 'r') as rm:
|
||||
with open(os.path.join(root_path) + '/README.md', 'r') as rm:
|
||||
PageRev(page=p, title="README", author=skia, content=rm.read()).save()
|
||||
|
||||
# Subscription
|
||||
## Root
|
||||
# Root
|
||||
s = Subscription(member=User.objects.filter(pk=root.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -266,7 +266,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Skia
|
||||
# Skia
|
||||
s = Subscription(member=User.objects.filter(pk=skia.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -274,7 +274,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Counter admin
|
||||
# Counter admin
|
||||
s = Subscription(member=User.objects.filter(pk=counter.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -282,7 +282,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Comptable
|
||||
# Comptable
|
||||
s = Subscription(member=User.objects.filter(pk=comptable.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -290,7 +290,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Richard
|
||||
# Richard
|
||||
s = Subscription(member=User.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -298,7 +298,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## User
|
||||
# User
|
||||
s = Subscription(member=User.objects.filter(pk=subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -306,7 +306,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Old subscriber
|
||||
# Old subscriber
|
||||
s = Subscription(member=User.objects.filter(pk=old_subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start(datetime(year=2012, month=9, day=4))
|
||||
@ -397,14 +397,14 @@ Welcome to the wiki page!
|
||||
buying.save()
|
||||
comptes = AccountingType(code='6', label="Comptes de charge", movement_type='DEBIT')
|
||||
comptes.save()
|
||||
simple = SimplifiedAccountingType(label = 'Je fais du simple 6', accounting_type = comptes, movement_type='DEBIT')
|
||||
simple = SimplifiedAccountingType(label='Je fais du simple 6', accounting_type=comptes, movement_type='DEBIT')
|
||||
simple.save()
|
||||
woenzco = Company(name="Woenzel & co")
|
||||
woenzco.save()
|
||||
|
||||
operation_list = [
|
||||
(27, "J'avais trop de bière", 'CASH', None, buying, 'USER', skia.id, "", None),
|
||||
(4000, "Ceci n'est pas une opération... en fait si mais non", 'CHECK', None, debit,'COMPANY', woenzco.id, "", 23),
|
||||
(4000, "Ceci n'est pas une opération... en fait si mais non", 'CHECK', None, debit, 'COMPANY', woenzco.id, "", 23),
|
||||
(22, "C'est de l'argent ?", 'CARD', None, credit, 'CLUB', troll.id, "", None),
|
||||
(37, "Je paye CASH", 'CASH', None, debit2, 'OTHER', None, "tous les étudiants <3", None),
|
||||
(300, "Paiement Guy", 'CASH', None, buying, 'USER', skia.id, "", None),
|
||||
@ -428,7 +428,7 @@ Welcome to the wiki page!
|
||||
date_of_birth="1942-06-12")
|
||||
sli.set_password("plop")
|
||||
sli.save()
|
||||
sli.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
sli.view_groups = [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
|
||||
sli.save()
|
||||
sli_profile_path = os.path.join(root_path, 'core/fixtures/images/5.jpg')
|
||||
with open(sli_profile_path, 'rb') as f:
|
||||
@ -456,7 +456,7 @@ Welcome to the wiki page!
|
||||
krophil_profile.save()
|
||||
krophil.profile_pict = krophil_profile
|
||||
krophil.save()
|
||||
## Adding subscription for sli
|
||||
# Adding subscription for sli
|
||||
s = Subscription(member=User.objects.filter(pk=sli.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -464,7 +464,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Adding subscription for Krophil
|
||||
# Adding subscription for Krophil
|
||||
s = Subscription(member=User.objects.filter(pk=krophil.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -483,7 +483,7 @@ Welcome to the wiki page!
|
||||
subscriber_group = Group.objects.get(name=settings.SITH_MAIN_MEMBERS_GROUP)
|
||||
ae_board_group = Group.objects.get(name=settings.SITH_MAIN_BOARD_GROUP)
|
||||
el = Election(title="Élection 2017", description="La roue tourne", start_candidature='1942-06-12 10:28:45+01',
|
||||
end_candidature='2042-06-12 10:28:45+01',start_date='1942-06-12 10:28:45+01',
|
||||
end_candidature='2042-06-12 10:28:45+01', start_date='1942-06-12 10:28:45+01',
|
||||
end_date='7942-06-12 10:28:45+01')
|
||||
el.save()
|
||||
el.view_groups.add(public_group)
|
||||
@ -519,4 +519,3 @@ Welcome to the wiki page!
|
||||
various.save()
|
||||
Forum(name="Promos", description="Réservé aux Promos", parent=various).save()
|
||||
ForumTopic(forum=hall)
|
||||
|
||||
|
@ -23,9 +23,8 @@
|
||||
#
|
||||
|
||||
import os
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@ -37,7 +36,7 @@ class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||
try:
|
||||
os.mkdir(os.path.join(root_path)+'/data')
|
||||
os.mkdir(os.path.join(root_path) + '/data')
|
||||
print("Data dir created")
|
||||
except Exception as e:
|
||||
repr(e)
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
import re
|
||||
from mistune import Renderer, InlineGrammar, InlineLexer, Markdown, escape, escape_link
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
||||
class SithRenderer(Renderer):
|
||||
@ -54,13 +54,16 @@ class SithRenderer(Renderer):
|
||||
src = original_src
|
||||
else:
|
||||
width = m.group(1)
|
||||
if not width.endswith('%'): width += "px"
|
||||
if not width.endswith('%'):
|
||||
width += "px"
|
||||
style = "width: %s; " % width
|
||||
try:
|
||||
height = m.group(3)
|
||||
if not height.endswith('%'): height += "px"
|
||||
if not height.endswith('%'):
|
||||
height += "px"
|
||||
style += "height: %s; " % height
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
params = None
|
||||
src = original_src
|
||||
@ -77,6 +80,7 @@ class SithRenderer(Renderer):
|
||||
return '%s />' % html
|
||||
return '%s>' % html
|
||||
|
||||
|
||||
class SithInlineGrammar(InlineGrammar):
|
||||
double_emphasis = re.compile(
|
||||
r'^\*{2}([\s\S]+?)\*{2}(?!\*)' # **word**
|
||||
@ -94,6 +98,7 @@ class SithInlineGrammar(InlineGrammar):
|
||||
r'^<sub>([\s\S]+?)</sub>' # <sub>text</sub>
|
||||
)
|
||||
|
||||
|
||||
class SithInlineLexer(InlineLexer):
|
||||
grammar_class = SithInlineGrammar
|
||||
|
||||
@ -166,7 +171,8 @@ class SithInlineLexer(InlineLexer):
|
||||
match = page.search(link)
|
||||
page = match.group(1) or ""
|
||||
link = reverse('core:page', kwargs={'page_name': page})
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
try: # Add file:// support for links
|
||||
file_link = re.compile(
|
||||
r'^file://(\d*)/?(\S*)?' # file://4000/download
|
||||
@ -175,9 +181,11 @@ class SithInlineLexer(InlineLexer):
|
||||
id = match.group(1)
|
||||
suffix = match.group(2) or ""
|
||||
link = reverse('core:file_detail', kwargs={'file_id': id}) + suffix
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(SithInlineLexer, self)._process_link(m, link, title)
|
||||
|
||||
|
||||
renderer = SithRenderer(escape=True)
|
||||
inline = SithInlineLexer(renderer)
|
||||
|
||||
@ -222,4 +230,3 @@ Petit *test* _sur_ ^une^ **seule** ^ligne pour voir^
|
||||
|
||||
"""
|
||||
print(markdown(text))
|
||||
|
||||
|
@ -52,4 +52,3 @@ class AuthenticationMiddleware(DjangoAuthenticationMiddleware):
|
||||
"'account.middleware.AuthenticationMiddleware'."
|
||||
)
|
||||
request.user = SimpleLazyObject(lambda: get_cached_user(request))
|
||||
|
||||
|
@ -44,14 +44,17 @@ from datetime import datetime, timedelta, date
|
||||
|
||||
import unicodedata
|
||||
|
||||
|
||||
class RealGroupManager(AuthGroupManager):
|
||||
def get_queryset(self):
|
||||
return super(RealGroupManager, self).get_queryset().filter(is_meta=False)
|
||||
|
||||
|
||||
class MetaGroupManager(AuthGroupManager):
|
||||
def get_queryset(self):
|
||||
return super(MetaGroupManager, self).get_queryset().filter(is_meta=True)
|
||||
|
||||
|
||||
class Group(AuthGroup):
|
||||
is_meta = models.BooleanField(
|
||||
_('meta group status'),
|
||||
@ -69,8 +72,10 @@ class Group(AuthGroup):
|
||||
"""
|
||||
return reverse('core:group_list')
|
||||
|
||||
|
||||
class MetaGroup(Group):
|
||||
objects = MetaGroupManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@ -78,20 +83,24 @@ class MetaGroup(Group):
|
||||
super(MetaGroup, self).__init__(*args, **kwargs)
|
||||
self.is_meta = True
|
||||
|
||||
|
||||
class RealGroup(Group):
|
||||
objects = RealGroupManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
|
||||
def validate_promo(value):
|
||||
start_year = settings.SITH_SCHOOL_START_YEAR
|
||||
delta = (date.today()+timedelta(days=180)).year - start_year
|
||||
delta = (date.today() + timedelta(days=180)).year - start_year
|
||||
if value < 0 or delta < value:
|
||||
raise ValidationError(
|
||||
_('%(value)s is not a valid promo (between 0 and %(end)s)'),
|
||||
params={'value': value, 'end': delta},
|
||||
)
|
||||
|
||||
|
||||
class User(AbstractBaseUser):
|
||||
"""
|
||||
Defines the base user class, useable in every app
|
||||
@ -226,6 +235,7 @@ class User(AbstractBaseUser):
|
||||
_club_memberships = {}
|
||||
_group_names = {}
|
||||
_group_ids = {}
|
||||
|
||||
def is_in_group(self, group_name):
|
||||
"""If the user is in the group passed in argument (as string or by id)"""
|
||||
group_id = 0
|
||||
@ -324,7 +334,7 @@ class User(AbstractBaseUser):
|
||||
(%s, %s, %s, %s, %s, %s)""", (self.id, self.last_name, self.first_name, self.email, "valid", "0"))
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
with open(settings.BASE_DIR+"/user_fail.log", "a") as f:
|
||||
with open(settings.BASE_DIR + "/user_fail.log", "a") as f:
|
||||
print("FAIL to add user %s (%s %s - %s) to old site" % (self.id, self.first_name, self.last_name,
|
||||
self.email), file=f)
|
||||
print("Reason: %s" % (repr(e)), file=f)
|
||||
@ -401,13 +411,13 @@ class User(AbstractBaseUser):
|
||||
Returns the generated username
|
||||
"""
|
||||
def remove_accents(data):
|
||||
return ''.join(x for x in unicodedata.normalize('NFKD', data) if \
|
||||
return ''.join(x for x in unicodedata.normalize('NFKD', data) if
|
||||
unicodedata.category(x)[0] == 'L').lower()
|
||||
user_name = remove_accents(self.first_name[0]+self.last_name).encode('ascii', 'ignore').decode('utf-8')
|
||||
user_name = remove_accents(self.first_name[0] + self.last_name).encode('ascii', 'ignore').decode('utf-8')
|
||||
un_set = [u.username for u in User.objects.all()]
|
||||
if user_name in un_set:
|
||||
i = 1
|
||||
while user_name+str(i) in un_set:
|
||||
while user_name + str(i) in un_set:
|
||||
i += 1
|
||||
user_name += str(i)
|
||||
self.username = user_name
|
||||
@ -489,6 +499,7 @@ class User(AbstractBaseUser):
|
||||
infos.save()
|
||||
return infos
|
||||
|
||||
|
||||
class AnonymousUser(AuthAnonymousUser):
|
||||
def __init__(self, request):
|
||||
super(AnonymousUser, self).__init__()
|
||||
@ -557,6 +568,7 @@ class AnonymousUser(AuthAnonymousUser):
|
||||
def get_display_name(self):
|
||||
return _("Visitor")
|
||||
|
||||
|
||||
class Preferences(models.Model):
|
||||
user = models.OneToOneField(User, related_name="preferences")
|
||||
receive_weekmail = models.BooleanField(
|
||||
@ -576,15 +588,19 @@ class Preferences(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return self.user.get_absolute_url()
|
||||
|
||||
|
||||
def get_directory(instance, filename):
|
||||
return '.{0}/{1}'.format(instance.get_parent_path(), filename)
|
||||
|
||||
|
||||
def get_compressed_directory(instance, filename):
|
||||
return '.{0}/compressed/{1}'.format(instance.get_parent_path(), filename)
|
||||
|
||||
|
||||
def get_thumbnail_directory(instance, filename):
|
||||
return '.{0}/thumbnail/{1}'.format(instance.get_parent_path(), filename)
|
||||
|
||||
|
||||
class SithFile(models.Model):
|
||||
name = models.CharField(_('file name'), max_length=256, blank=False)
|
||||
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True)
|
||||
@ -763,18 +779,22 @@ class SithFile(models.Model):
|
||||
def __str__(self):
|
||||
return self.get_parent_path() + "/" + self.name
|
||||
|
||||
|
||||
class LockError(Exception):
|
||||
"""There was a lock error on the object"""
|
||||
pass
|
||||
|
||||
|
||||
class AlreadyLocked(LockError):
|
||||
"""The object is already locked"""
|
||||
pass
|
||||
|
||||
|
||||
class NotLocked(LockError):
|
||||
"""The object is not locked"""
|
||||
pass
|
||||
|
||||
|
||||
class Page(models.Model):
|
||||
"""
|
||||
The page class to build a Wiki
|
||||
@ -792,8 +812,7 @@ class Page(models.Model):
|
||||
r'^[A-z.+-]+$',
|
||||
_('Enter a valid page name. This value may contain only '
|
||||
'unaccented letters, numbers ' 'and ./+/-/_ characters.')
|
||||
),
|
||||
],
|
||||
), ],
|
||||
blank=False)
|
||||
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True, on_delete=models.SET_NULL)
|
||||
# Attention: this field may not be valid until you call save(). It's made for fast query, but don't rely on it when
|
||||
@ -854,7 +873,8 @@ class Page(models.Model):
|
||||
Performs some needed actions before and after saving a page in database
|
||||
"""
|
||||
locked = kwargs.pop('force_lock', False)
|
||||
if not locked: locked = self.is_locked()
|
||||
if not locked:
|
||||
locked = self.is_locked()
|
||||
if not locked:
|
||||
raise NotLocked("The page is not locked and thus can not be saved")
|
||||
self.full_clean()
|
||||
@ -973,7 +993,7 @@ class PageRev(models.Model):
|
||||
page = models.ForeignKey(Page, related_name='revisions')
|
||||
|
||||
class Meta:
|
||||
ordering = ['date',]
|
||||
ordering = ['date', ]
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""
|
||||
@ -1003,6 +1023,7 @@ class PageRev(models.Model):
|
||||
# Don't forget to unlock, otherwise, people will have to wait for the page's timeout
|
||||
self.page.unset_lock()
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
user = models.ForeignKey(User, related_name='notifications')
|
||||
url = models.CharField(_("url"), max_length=255)
|
||||
@ -1015,4 +1036,3 @@ class Notification(models.Model):
|
||||
if self.param:
|
||||
return self.get_type_display() % self.param
|
||||
return self.get_type_display()
|
||||
|
||||
|
@ -27,30 +27,32 @@ from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
from django.utils.safestring import mark_safe
|
||||
from core.scss.processor import ScssProcessor
|
||||
from django.utils.html import escape
|
||||
|
||||
from core.markdown import markdown as md
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
@stringfilter
|
||||
def markdown(text):
|
||||
return mark_safe("<div class=\"markdown\">%s</div>" % md(text))
|
||||
|
||||
|
||||
@register.filter()
|
||||
@stringfilter
|
||||
def datetime_format_python_to_PHP(python_format_string):
|
||||
"""
|
||||
Given a python datetime format string, attempts to convert it to the nearest PHP datetime format string possible.
|
||||
"""
|
||||
python2PHP = {"%a": "D", "%a": "D", "%A": "l", "%b": "M", "%B": "F", "%c": "", "%d": "d", "%H": "H", "%I": "h", "%j": "z", "%m": "m", "%M": "i", "%p": "A", "%S": "s", "%U": "", "%w": "w", "%W": "W", "%x": "", "%X": "", "%y": "y", "%Y": "Y", "%Z": "e" }
|
||||
python2PHP = {"%a": "D", "%a": "D", "%A": "l", "%b": "M", "%B": "F", "%c": "", "%d": "d", "%H": "H", "%I": "h", "%j": "z", "%m": "m", "%M": "i", "%p": "A", "%S": "s", "%U": "", "%w": "w", "%W": "W", "%x": "", "%X": "", "%y": "y", "%Y": "Y", "%Z": "e"}
|
||||
|
||||
php_format_string = python_format_string
|
||||
for py, php in python2PHP.items():
|
||||
php_format_string = php_format_string.replace(py, php)
|
||||
return php_format_string
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def scss(path):
|
||||
"""
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
from django.test import Client, TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.management import call_command
|
||||
|
||||
from core.models import User, Group, Page
|
||||
@ -34,6 +33,7 @@ to run these tests :
|
||||
python3 manage.py test
|
||||
"""
|
||||
|
||||
|
||||
class UserRegistrationTest(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
@ -185,6 +185,7 @@ class UserRegistrationTest(TestCase):
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("""<p>Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.</p>""" in str(response.content))
|
||||
|
||||
|
||||
class PageHandlingTest(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
@ -260,7 +261,7 @@ class PageHandlingTest(TestCase):
|
||||
'name': 'guy',
|
||||
'owner_group': '1',
|
||||
})
|
||||
r = self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
|
||||
self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
|
||||
'title': 'Bibou',
|
||||
'content':
|
||||
'''Guy *bibou*
|
||||
@ -280,11 +281,12 @@ http://git.an
|
||||
'<h1>Swag</h1>\\n<guy>Bibou</guy>' +
|
||||
"<script>alert(\\'Guy\\');</script>" in str(response.content))
|
||||
|
||||
#TODO: many tests on the pages:
|
||||
# TODO: many tests on the pages:
|
||||
# - renaming a page
|
||||
# - changing a page's parent --> check that page's children's full_name
|
||||
# - changing the different groups of the page
|
||||
|
||||
|
||||
class FileHandlingTest(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
@ -295,19 +297,18 @@ class FileHandlingTest(TestCase):
|
||||
print(e)
|
||||
|
||||
def test_create_folder_home(self):
|
||||
response = self.client.post(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}),
|
||||
response = self.client.post(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
|
||||
{"folder_name": "GUY_folder_test"})
|
||||
self.assertTrue(response.status_code == 302)
|
||||
response = self.client.get(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}))
|
||||
response = self.client.get(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}))
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("GUY_folder_test</a>" in str(response.content))
|
||||
|
||||
def test_upload_file_home(self):
|
||||
with open("/bin/ls", "rb") as f:
|
||||
response = self.client.post(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}),
|
||||
response = self.client.post(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
|
||||
{"file_field": f})
|
||||
self.assertTrue(response.status_code == 302)
|
||||
response = self.client.get(reverse("core:file_detail", kwargs={"file_id":self.subscriber.home.id}))
|
||||
response = self.client.get(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}))
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("ls</a>" in str(response.content))
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from core.views import *
|
||||
|
||||
|
@ -27,9 +27,10 @@ import re
|
||||
# Image utils
|
||||
|
||||
from io import BytesIO
|
||||
from datetime import datetime, timezone, date
|
||||
from datetime import date
|
||||
|
||||
from PIL import Image, ExifTags
|
||||
from PIL import ExifTags
|
||||
# from exceptions import IOError
|
||||
import PIL
|
||||
|
||||
from django.conf import settings
|
||||
@ -49,16 +50,17 @@ def get_start_of_semester(d=date.today()):
|
||||
today = d
|
||||
year = today.year
|
||||
start = date(year, settings.SITH_START_DATE[0], settings.SITH_START_DATE[1])
|
||||
start2 = start.replace(month=(start.month+6)%12)
|
||||
start2 = start.replace(month=(start.month + 6) % 12)
|
||||
if start > start2:
|
||||
start, start2 = start2, start
|
||||
if today < start:
|
||||
return start2.replace(year=year-1)
|
||||
return start2.replace(year=year - 1)
|
||||
elif today < start2:
|
||||
return start
|
||||
else:
|
||||
return start2
|
||||
|
||||
|
||||
def get_semester(d=date.today()):
|
||||
start = get_start_of_semester(d)
|
||||
if start.month <= 6:
|
||||
@ -66,6 +68,7 @@ def get_semester(d=date.today()):
|
||||
else:
|
||||
return "A" + str(start.year)[-2:]
|
||||
|
||||
|
||||
def scale_dimension(width, height, long_edge):
|
||||
if width > height:
|
||||
ratio = long_edge * 1. / width
|
||||
@ -73,6 +76,7 @@ def scale_dimension(width, height, long_edge):
|
||||
ratio = long_edge * 1. / height
|
||||
return int(width * ratio), int(height * ratio)
|
||||
|
||||
|
||||
def resize_image(im, edge, format):
|
||||
(w, h) = im.size
|
||||
(width, height) = scale_dimension(w, h, long_edge=edge)
|
||||
@ -85,20 +89,23 @@ def resize_image(im, edge, format):
|
||||
im.save(fp=content, format=format.upper(), quality=90, optimize=True, progressive=True)
|
||||
return ContentFile(content.getvalue())
|
||||
|
||||
def exif_auto_rotate(image):
|
||||
for orientation in ExifTags.TAGS.keys() :
|
||||
if ExifTags.TAGS[orientation]=='Orientation' : break
|
||||
exif=dict(image._getexif().items())
|
||||
|
||||
if exif[orientation] == 3 :
|
||||
image=image.rotate(180, expand=True)
|
||||
elif exif[orientation] == 6 :
|
||||
image=image.rotate(270, expand=True)
|
||||
elif exif[orientation] == 8 :
|
||||
image=image.rotate(90, expand=True)
|
||||
def exif_auto_rotate(image):
|
||||
for orientation in ExifTags.TAGS.keys():
|
||||
if ExifTags.TAGS[orientation] == 'Orientation':
|
||||
break
|
||||
exif = dict(image._getexif().items())
|
||||
|
||||
if exif[orientation] == 3:
|
||||
image = image.rotate(180, expand=True)
|
||||
elif exif[orientation] == 6:
|
||||
image = image.rotate(270, expand=True)
|
||||
elif exif[orientation] == 8:
|
||||
image = image.rotate(90, expand=True)
|
||||
|
||||
return image
|
||||
|
||||
|
||||
def doku_to_markdown(text):
|
||||
"""This is a quite correct doku translator"""
|
||||
text = re.sub(r'([^:]|^)\/\/(.*?)\/\/', r'*\2*', text) # Italic (prevents protocol:// conflict)
|
||||
@ -164,12 +171,14 @@ def doku_to_markdown(text):
|
||||
quote_level -= 1
|
||||
final_newline = True
|
||||
new_text.append("> " * final_quote_level + line) # Finally append the line
|
||||
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
if final_newline:
|
||||
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
else:
|
||||
new_text.append(line)
|
||||
|
||||
return "\n".join(new_text)
|
||||
|
||||
|
||||
def bbcode_to_markdown(text):
|
||||
"""This is a very basic BBcode translator"""
|
||||
text = re.sub(r'\[b\](.*?)\[\/b\]', r'**\1**', text, flags=re.DOTALL) # Bold
|
||||
@ -205,9 +214,9 @@ def bbcode_to_markdown(text):
|
||||
quote_level -= 1
|
||||
final_newline = True
|
||||
new_text.append("> " * final_quote_level + line) # Finally append the line
|
||||
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
if final_newline:
|
||||
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
else:
|
||||
new_text.append(line)
|
||||
|
||||
return "\n".join(new_text)
|
||||
|
||||
|
@ -23,29 +23,28 @@
|
||||
#
|
||||
|
||||
# This file contains all the views that concern the page model
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.shortcuts import redirect
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, FormMixin, DeleteView
|
||||
from django.views.generic.edit import UpdateView, FormMixin, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.http import HttpResponse
|
||||
from django.core.servers.basehttp import FileWrapper
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django import forms
|
||||
|
||||
import os
|
||||
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select import make_ajax_field
|
||||
|
||||
from core.models import SithFile, RealGroup, Notification
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, can_view, not_found
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, can_view, not_found
|
||||
from counter.models import Counter
|
||||
|
||||
|
||||
def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
||||
"""
|
||||
Send a file through Django without loading the whole file into
|
||||
@ -70,6 +69,7 @@ def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
||||
response['Content-Disposition'] = ('inline; filename="%s"' % f.name).encode('utf-8')
|
||||
return response
|
||||
|
||||
|
||||
class AddFilesForm(forms.Form):
|
||||
folder_name = forms.CharField(label=_("Add a new folder"), max_length=30, required=False)
|
||||
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Files"),
|
||||
@ -100,6 +100,7 @@ class AddFilesForm(forms.Form):
|
||||
if not u.notifications.filter(type="FILE_MODERATION", viewed=False).exists():
|
||||
Notification(user=u, url=reverse("core:file_moderation"), type="FILE_MODERATION").save()
|
||||
|
||||
|
||||
class FileListView(ListView):
|
||||
template_name = 'core/file_list.jinja'
|
||||
context_object_name = "file_list"
|
||||
@ -114,6 +115,7 @@ class FileListView(ListView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileEditView(CanEditMixin, UpdateView):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -138,6 +140,7 @@ class FileEditView(CanEditMixin, UpdateView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileEditPropForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = SithFile
|
||||
@ -147,6 +150,7 @@ class FileEditPropForm(forms.ModelForm):
|
||||
view_groups = make_ajax_field(SithFile, 'view_groups', 'groups', help_text="", label=_("view group"))
|
||||
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
||||
|
||||
|
||||
class FileEditPropView(CanEditPropMixin, UpdateView):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -175,6 +179,7 @@ class FileEditPropView(CanEditPropMixin, UpdateView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileView(CanViewMixin, DetailView, FormMixin):
|
||||
"""This class handle the upload of new files into a folder"""
|
||||
model = SithFile
|
||||
@ -237,6 +242,7 @@ class FileView(CanViewMixin, DetailView, FormMixin):
|
||||
kwargs['clipboard'] = SithFile.objects.filter(id__in=self.request.session['clipboard'])
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileDeleteView(CanEditPropMixin, DeleteView):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -258,6 +264,7 @@ class FileDeleteView(CanEditPropMixin, DeleteView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileModerationView(TemplateView):
|
||||
template_name = "core/file_moderation.jinja"
|
||||
|
||||
@ -266,6 +273,7 @@ class FileModerationView(TemplateView):
|
||||
kwargs['files'] = SithFile.objects.filter(is_moderated=False)[:100]
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -278,4 +286,3 @@ class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
||||
if 'next' in self.request.GET.keys():
|
||||
return redirect(self.request.GET['next'])
|
||||
return redirect('core:file_moderation')
|
||||
|
||||
|
@ -22,22 +22,24 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, UserChangeForm
|
||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.contrib.auth import logout, login, authenticate
|
||||
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput, DateTimeInput, Textarea
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from core.models import User, Page, RealGroup, SithFile
|
||||
from core.models import User, Page, SithFile
|
||||
|
||||
from core.utils import resize_image
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
|
||||
# Widgets
|
||||
@ -50,6 +52,7 @@ class SelectSingle(Select):
|
||||
attrs = {'class': "select_single"}
|
||||
return super(SelectSingle, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class SelectMultiple(Select):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -58,6 +61,7 @@ class SelectMultiple(Select):
|
||||
attrs = {'class': "select_multiple"}
|
||||
return super(SelectMultiple, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class SelectDateTime(DateTimeInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -66,6 +70,7 @@ class SelectDateTime(DateTimeInput):
|
||||
attrs = {'class': "select_datetime"}
|
||||
return super(SelectDateTime, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class SelectDate(DateInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -74,6 +79,7 @@ class SelectDate(DateInput):
|
||||
attrs = {'class': "select_date"}
|
||||
return super(SelectDate, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class MarkdownInput(Textarea):
|
||||
def render(self, name, value, attrs=None):
|
||||
output = '<p><a href="%(syntax_url)s">%(help_text)s</a></p>'\
|
||||
@ -84,6 +90,7 @@ class MarkdownInput(Textarea):
|
||||
}
|
||||
return output
|
||||
|
||||
|
||||
class SelectFile(TextInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -98,6 +105,7 @@ class SelectFile(TextInput):
|
||||
output += '<span name="' + name + '" class="choose_file_button">' + ugettext("Choose file") + '</span>'
|
||||
return output
|
||||
|
||||
|
||||
class SelectUser(TextInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -114,6 +122,7 @@ class SelectUser(TextInput):
|
||||
|
||||
# Forms
|
||||
|
||||
|
||||
class LoginForm(AuthenticationForm):
|
||||
def __init__(self, *arg, **kwargs):
|
||||
if 'data' in kwargs.keys():
|
||||
@ -128,14 +137,17 @@ class LoginForm(AuthenticationForm):
|
||||
else:
|
||||
user = User.objects.filter(username=data['username']).first()
|
||||
data['username'] = user.username
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
kwargs['data'] = data
|
||||
super(LoginForm, self).__init__(*arg, **kwargs)
|
||||
self.fields['username'].label = _("Username, email, or account number")
|
||||
|
||||
|
||||
class RegisteringForm(UserCreationForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('first_name', 'last_name', 'email')
|
||||
@ -148,9 +160,6 @@ class RegisteringForm(UserCreationForm):
|
||||
user.save()
|
||||
return user
|
||||
|
||||
from core.utils import resize_image
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
class UserProfileForm(forms.ModelForm):
|
||||
"""
|
||||
@ -197,7 +206,7 @@ class UserProfileForm(forms.ModelForm):
|
||||
self.cleaned_data['profile_pict'] = profile
|
||||
self.cleaned_data['scrub_pict'] = scrub
|
||||
parent = SithFile.objects.filter(parent=None, name="profiles").first()
|
||||
for field,f in files:
|
||||
for field, f in files:
|
||||
with transaction.atomic():
|
||||
try:
|
||||
im = Image.open(BytesIO(f.read()))
|
||||
@ -223,9 +232,11 @@ class UserProfileForm(forms.ModelForm):
|
||||
{'file_name': f, 'msg': _("Bad image format, only jpeg, png, and gif are accepted")})
|
||||
self._post_clean()
|
||||
|
||||
|
||||
class UserPropForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['groups']
|
||||
@ -236,13 +247,16 @@ class UserPropForm(forms.ModelForm):
|
||||
'groups': CheckboxSelectMultiple,
|
||||
}
|
||||
|
||||
|
||||
class UserGodfathersForm(forms.Form):
|
||||
type = forms.ChoiceField(choices=[('godfather', _("Godfather")), ('godchild', _("Godchild"))], label=_("Add"))
|
||||
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
|
||||
|
||||
|
||||
class PagePropForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = Page
|
||||
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ]
|
||||
@ -255,4 +269,3 @@ class PagePropForm(forms.ModelForm):
|
||||
super(PagePropForm, self).__init__(*arg, **kwargs)
|
||||
self.fields['edit_groups'].required = False
|
||||
self.fields['view_groups'].required = False
|
||||
|
||||
|
@ -29,6 +29,7 @@ from django.core.urlresolvers import reverse_lazy
|
||||
from core.models import RealGroup
|
||||
from core.views import CanEditMixin
|
||||
|
||||
|
||||
class GroupListView(CanEditMixin, ListView):
|
||||
"""
|
||||
Displays the group list
|
||||
@ -36,17 +37,20 @@ class GroupListView(CanEditMixin, ListView):
|
||||
model = RealGroup
|
||||
template_name = "core/group_list.jinja"
|
||||
|
||||
|
||||
class GroupEditView(CanEditMixin, UpdateView):
|
||||
model = RealGroup
|
||||
pk_url_kwarg = "group_id"
|
||||
template_name = "core/group_edit.jinja"
|
||||
fields = ['name', 'description']
|
||||
|
||||
|
||||
class GroupCreateView(CanEditMixin, CreateView):
|
||||
model = RealGroup
|
||||
template_name = "core/group_edit.jinja"
|
||||
fields = ['name', 'description']
|
||||
|
||||
|
||||
class GroupDeleteView(CanEditMixin, DeleteView):
|
||||
model = RealGroup
|
||||
pk_url_kwarg = "group_id"
|
||||
|
@ -23,23 +23,22 @@
|
||||
#
|
||||
|
||||
# This file contains all the views that concern the page model
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple, modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
|
||||
from core.models import Page, PageRev, LockError
|
||||
from core.views.forms import PagePropForm, MarkdownInput
|
||||
from core.views.forms import MarkdownInput
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
||||
|
||||
|
||||
class PageListView(CanViewMixin, ListView):
|
||||
model = Page
|
||||
template_name = 'core/page_list.jinja'
|
||||
|
||||
|
||||
class PageView(CanViewMixin, DetailView):
|
||||
model = Page
|
||||
template_name = 'core/page_detail.jinja'
|
||||
@ -54,6 +53,7 @@ class PageView(CanViewMixin, DetailView):
|
||||
context['new_page'] = self.kwargs['page_name']
|
||||
return context
|
||||
|
||||
|
||||
class PageHistView(CanViewMixin, DetailView):
|
||||
model = Page
|
||||
template_name = 'core/page_hist.jinja'
|
||||
@ -62,6 +62,7 @@ class PageHistView(CanViewMixin, DetailView):
|
||||
self.page = Page.get_page_by_full_name(self.kwargs['page_name'])
|
||||
return self.page
|
||||
|
||||
|
||||
class PageRevView(CanViewMixin, DetailView):
|
||||
model = Page
|
||||
template_name = 'core/page_detail.jinja'
|
||||
@ -84,13 +85,14 @@ class PageRevView(CanViewMixin, DetailView):
|
||||
context['new_page'] = self.kwargs['page_name']
|
||||
return context
|
||||
|
||||
|
||||
class PageCreateView(CanCreateMixin, CreateView):
|
||||
model = Page
|
||||
form_class = modelform_factory(Page,
|
||||
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
||||
fields=['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
||||
widgets={
|
||||
'edit_groups':CheckboxSelectMultiple,
|
||||
'view_groups':CheckboxSelectMultiple,
|
||||
'edit_groups': CheckboxSelectMultiple,
|
||||
'view_groups': CheckboxSelectMultiple,
|
||||
})
|
||||
template_name = 'core/page_prop.jinja'
|
||||
|
||||
@ -115,13 +117,14 @@ class PageCreateView(CanCreateMixin, CreateView):
|
||||
ret = super(PageCreateView, self).form_valid(form)
|
||||
return ret
|
||||
|
||||
|
||||
class PagePropView(CanEditPropMixin, UpdateView):
|
||||
model = Page
|
||||
form_class = modelform_factory(Page,
|
||||
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
||||
fields=['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ],
|
||||
widgets={
|
||||
'edit_groups':CheckboxSelectMultiple,
|
||||
'view_groups':CheckboxSelectMultiple,
|
||||
'edit_groups': CheckboxSelectMultiple,
|
||||
'view_groups': CheckboxSelectMultiple,
|
||||
})
|
||||
template_name = 'core/page_prop.jinja'
|
||||
slug_field = '_full_name'
|
||||
@ -130,7 +133,7 @@ class PagePropView(CanEditPropMixin, UpdateView):
|
||||
def get_object(self):
|
||||
o = super(PagePropView, self).get_object()
|
||||
# Create the page if it does not exists
|
||||
#if p == None:
|
||||
# if p == None:
|
||||
# parent_name = '/'.join(page_name.split('/')[:-1])
|
||||
# name = page_name.split('/')[-1]
|
||||
# if parent_name == "":
|
||||
@ -145,9 +148,10 @@ class PagePropView(CanEditPropMixin, UpdateView):
|
||||
raise e
|
||||
return self.page
|
||||
|
||||
|
||||
class PageEditView(CanEditMixin, UpdateView):
|
||||
model = PageRev
|
||||
form_class = modelform_factory(model=PageRev, fields=['title', 'content',], widgets={'content': MarkdownInput})
|
||||
form_class = modelform_factory(model=PageRev, fields=['title', 'content', ], widgets={'content': MarkdownInput})
|
||||
template_name = 'core/pagerev_edit.jinja'
|
||||
|
||||
def get_object(self):
|
||||
|
@ -22,17 +22,13 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.db import models
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import JsonResponse
|
||||
from django.core import serializers
|
||||
from django.db.models import Q
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.generic import ListView, TemplateView
|
||||
|
||||
import os
|
||||
import json
|
||||
from itertools import chain
|
||||
|
||||
from haystack.query import SearchQuerySet
|
||||
|
||||
@ -40,9 +36,11 @@ from core.models import User, Notification
|
||||
from core.utils import doku_to_markdown, bbcode_to_markdown
|
||||
from club.models import Club
|
||||
|
||||
|
||||
def index(request, context=None):
|
||||
return render(request, "core/index.jinja")
|
||||
|
||||
|
||||
class NotificationList(ListView):
|
||||
model = Notification
|
||||
template_name = "core/notification_list.jinja"
|
||||
@ -52,6 +50,7 @@ class NotificationList(ListView):
|
||||
self.request.user.notifications.update(viewed=True)
|
||||
return self.request.user.notifications.order_by('-id')[:20]
|
||||
|
||||
|
||||
def notification(request, notif_id):
|
||||
notif = Notification.objects.filter(id=notif_id).first()
|
||||
if notif:
|
||||
@ -60,10 +59,12 @@ def notification(request, notif_id):
|
||||
return redirect(notif.url)
|
||||
return redirect("/")
|
||||
|
||||
|
||||
def search_user(query, as_json=False):
|
||||
res = SearchQuerySet().models(User).filter(text=query).filter_or(text__contains=query)[:20]
|
||||
return [r.object for r in res]
|
||||
|
||||
|
||||
def search_club(query, as_json=False):
|
||||
clubs = []
|
||||
if query:
|
||||
@ -75,6 +76,7 @@ def search_club(query, as_json=False):
|
||||
clubs = list(clubs)
|
||||
return clubs
|
||||
|
||||
|
||||
@login_required
|
||||
def search_view(request):
|
||||
result = {
|
||||
@ -83,6 +85,7 @@ def search_view(request):
|
||||
}
|
||||
return render(request, "core/search.jinja", context={'result': result})
|
||||
|
||||
|
||||
@login_required
|
||||
def search_user_json(request):
|
||||
result = {
|
||||
@ -90,6 +93,7 @@ def search_user_json(request):
|
||||
}
|
||||
return JsonResponse(result)
|
||||
|
||||
|
||||
@login_required
|
||||
def search_json(request):
|
||||
result = {
|
||||
@ -98,6 +102,7 @@ def search_json(request):
|
||||
}
|
||||
return JsonResponse(result)
|
||||
|
||||
|
||||
class ToMarkdownView(TemplateView):
|
||||
template_name = "core/to_markdown.jinja"
|
||||
|
||||
@ -119,4 +124,3 @@ class ToMarkdownView(TemplateView):
|
||||
kwargs['text'] = ""
|
||||
kwargs['text_md'] = ""
|
||||
return kwargs
|
||||
|
||||
|
@ -24,30 +24,29 @@
|
||||
|
||||
# This file contains all the views that concern the user model
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth import logout as auth_logout, views
|
||||
from django.contrib.auth import views
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ValidationError
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.http import Http404
|
||||
from django.views.generic.edit import UpdateView
|
||||
from django.views.generic import ListView, DetailView, TemplateView, DeleteView
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.template.response import TemplateResponse
|
||||
from django.conf import settings
|
||||
from django.views.generic.dates import YearMixin, MonthMixin
|
||||
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta, datetime, date
|
||||
from datetime import timedelta, date
|
||||
import logging
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
|
||||
from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm, LoginForm, UserGodfathersForm
|
||||
from core.views.forms import RegisteringForm, UserProfileForm, LoginForm, UserGodfathersForm
|
||||
from core.models import User, SithFile, Preferences
|
||||
from club.models import Club
|
||||
from subscription.models import Subscription
|
||||
from trombi.views import UserTrombiForm
|
||||
|
||||
|
||||
def login(request):
|
||||
"""
|
||||
The login view
|
||||
@ -56,24 +55,28 @@ def login(request):
|
||||
"""
|
||||
return views.login(request, template_name="core/login.jinja", authentication_form=LoginForm)
|
||||
|
||||
|
||||
def logout(request):
|
||||
"""
|
||||
The logout view
|
||||
"""
|
||||
return views.logout_then_login(request)
|
||||
|
||||
|
||||
def password_change(request):
|
||||
"""
|
||||
Allows a user to change its password
|
||||
"""
|
||||
return views.password_change(request, template_name="core/password_change.jinja", post_change_redirect=reverse("core:password_change_done"))
|
||||
|
||||
|
||||
def password_change_done(request):
|
||||
"""
|
||||
Allows a user to change its password
|
||||
"""
|
||||
return views.password_change_done(request, template_name="core/password_change_done.jinja")
|
||||
|
||||
|
||||
def password_root_change(request, user_id):
|
||||
"""
|
||||
Allows a root user to change someone's password
|
||||
@ -92,6 +95,7 @@ def password_root_change(request, user_id):
|
||||
form = views.SetPasswordForm(user=user)
|
||||
return TemplateResponse(request, "core/password_change.jinja", {'form': form, 'target': user})
|
||||
|
||||
|
||||
def password_reset(request):
|
||||
"""
|
||||
Allows someone to enter an email adresse for resetting password
|
||||
@ -102,12 +106,14 @@ def password_reset(request):
|
||||
post_reset_redirect="core:password_reset_done",
|
||||
)
|
||||
|
||||
|
||||
def password_reset_done(request):
|
||||
"""
|
||||
Confirm that the reset email has been sent
|
||||
"""
|
||||
return views.password_reset_done(request, template_name="core/password_reset_done.jinja")
|
||||
|
||||
|
||||
def password_reset_confirm(request, uidb64=None, token=None):
|
||||
"""
|
||||
Provide a reset password formular
|
||||
@ -117,6 +123,7 @@ def password_reset_confirm(request, uidb64=None, token=None):
|
||||
template_name="core/password_reset_confirm.jinja",
|
||||
)
|
||||
|
||||
|
||||
def password_reset_complete(request):
|
||||
"""
|
||||
Confirm the password has sucessfully been reset
|
||||
@ -125,12 +132,13 @@ def password_reset_complete(request):
|
||||
template_name="core/password_reset_complete.jinja",
|
||||
)
|
||||
|
||||
|
||||
def register(request):
|
||||
context = {}
|
||||
if request.method == 'POST':
|
||||
form = RegisteringForm(request.POST)
|
||||
if form.is_valid():
|
||||
logging.debug("Registering "+form.cleaned_data['first_name']+form.cleaned_data['last_name'])
|
||||
logging.debug("Registering " + form.cleaned_data['first_name'] + form.cleaned_data['last_name'])
|
||||
u = form.save()
|
||||
context['user_registered'] = u
|
||||
context['tests'] = 'TEST_REGISTER_USER_FORM_OK'
|
||||
@ -143,6 +151,7 @@ def register(request):
|
||||
context['form'] = form.as_p()
|
||||
return render(request, "core/register.jinja", context)
|
||||
|
||||
|
||||
class UserTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
return self.object.get_display_name()
|
||||
@ -196,7 +205,7 @@ class UserTabsMixin(TabedViewMixin):
|
||||
try:
|
||||
if (self.object.customer and (self.object == self.request.user
|
||||
or self.request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||
or self.request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
|
||||
or self.request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name'] + settings.SITH_BOARD_SUFFIX)
|
||||
or self.request.user.is_root)):
|
||||
tab_list.append({
|
||||
'url': reverse('core:user_stats', kwargs={'user_id': self.object.id}),
|
||||
@ -206,11 +215,13 @@ class UserTabsMixin(TabedViewMixin):
|
||||
tab_list.append({
|
||||
'url': reverse('core:user_account', kwargs={'user_id': self.object.id}),
|
||||
'slug': 'account',
|
||||
'name': _("Account")+" (%s €)" % self.object.customer.amount,
|
||||
'name': _("Account") + " (%s €)" % self.object.customer.amount,
|
||||
})
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return tab_list
|
||||
|
||||
|
||||
class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's profile
|
||||
@ -236,6 +247,7 @@ def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
||||
raise PermissionDenied
|
||||
return redirect('core:user_godfathers', user_id=user_id)
|
||||
|
||||
|
||||
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's pictures
|
||||
@ -246,6 +258,7 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = "core/user_pictures.jinja"
|
||||
current_tab = 'pictures'
|
||||
|
||||
|
||||
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's godfathers
|
||||
@ -277,6 +290,7 @@ class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['form'] = UserGodfathersForm()
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's stats
|
||||
@ -295,7 +309,7 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
if not (profile == request.user
|
||||
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
|
||||
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name'] + settings.SITH_BOARD_SUFFIX)
|
||||
or request.user.is_root):
|
||||
raise PermissionDenied
|
||||
|
||||
@ -303,26 +317,27 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(UserStatsView, self).get_context_data(**kwargs)
|
||||
from counter.models import Counter, Product, Selling
|
||||
from counter.models import Counter
|
||||
from django.db.models import Sum
|
||||
foyer = Counter.objects.filter(name="Foyer").first()
|
||||
mde = Counter.objects.filter(name="MDE").first()
|
||||
gommette = Counter.objects.filter(name="La Gommette").first()
|
||||
semester_start=Subscription.compute_start(d=date.today(), duration=3)
|
||||
kwargs['total_perm_time'] = sum([p.end-p.start for p in self.object.permanencies.exclude(end=None)], timedelta())
|
||||
kwargs['total_foyer_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=foyer).exclude(end=None)], timedelta())
|
||||
kwargs['total_mde_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=mde).exclude(end=None)], timedelta())
|
||||
kwargs['total_gommette_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=gommette).exclude(end=None)], timedelta())
|
||||
kwargs['total_foyer_buyings'] = sum([b.unit_price*b.quantity for b in
|
||||
semester_start = Subscription.compute_start(d=date.today(), duration=3)
|
||||
kwargs['total_perm_time'] = sum([p.end - p.start for p in self.object.permanencies.exclude(end=None)], timedelta())
|
||||
kwargs['total_foyer_time'] = sum([p.end - p.start for p in self.object.permanencies.filter(counter=foyer).exclude(end=None)], timedelta())
|
||||
kwargs['total_mde_time'] = sum([p.end - p.start for p in self.object.permanencies.filter(counter=mde).exclude(end=None)], timedelta())
|
||||
kwargs['total_gommette_time'] = sum([p.end - p.start for p in self.object.permanencies.filter(counter=gommette).exclude(end=None)], timedelta())
|
||||
kwargs['total_foyer_buyings'] = sum([b.unit_price * b.quantity for b in
|
||||
self.object.customer.buyings.filter(counter=foyer, date__gte=semester_start)])
|
||||
kwargs['total_mde_buyings'] = sum([b.unit_price*b.quantity for b in self.object.customer.buyings.filter(counter=mde,
|
||||
kwargs['total_mde_buyings'] = sum([b.unit_price * b.quantity for b in self.object.customer.buyings.filter(counter=mde,
|
||||
date__gte=semester_start)])
|
||||
kwargs['total_gommette_buyings'] = sum([b.unit_price*b.quantity for b in
|
||||
kwargs['total_gommette_buyings'] = sum([b.unit_price * b.quantity for b in
|
||||
self.object.customer.buyings.filter(counter=gommette, date__gte=semester_start)])
|
||||
kwargs['top_product'] = self.object.customer.buyings.values('product__name').annotate(
|
||||
product_sum=Sum('quantity')).exclude(product_sum=None).order_by('-product_sum').all()[:10]
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserMiniView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's profile
|
||||
@ -332,6 +347,7 @@ class UserMiniView(CanViewMixin, DetailView):
|
||||
context_object_name = "profile"
|
||||
template_name = "core/user_mini.jinja"
|
||||
|
||||
|
||||
class UserListView(ListView, CanEditPropMixin):
|
||||
"""
|
||||
Displays the user list
|
||||
@ -339,6 +355,7 @@ class UserListView(ListView, CanEditPropMixin):
|
||||
model = User
|
||||
template_name = "core/user_list.jinja"
|
||||
|
||||
|
||||
class UserUploadProfilePictView(CanEditMixin, DetailView):
|
||||
"""
|
||||
Handle the upload of the profile picture taken with webcam in navigator
|
||||
@ -367,6 +384,7 @@ class UserUploadProfilePictView(CanEditMixin, DetailView):
|
||||
self.object.save()
|
||||
return redirect("core:user_edit", user_id=self.object.id)
|
||||
|
||||
|
||||
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
"""
|
||||
Edit a user's profile
|
||||
@ -412,6 +430,7 @@ class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
kwargs['form'] = self.form
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display the user's club(s)
|
||||
@ -422,6 +441,7 @@ class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = "core/user_clubs.jinja"
|
||||
current_tab = "clubs"
|
||||
|
||||
|
||||
class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
"""
|
||||
Edit a user's preferences
|
||||
@ -453,6 +473,7 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
kwargs['trombi_form'] = UserTrombiForm()
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
||||
"""
|
||||
Edit a user's groups
|
||||
@ -461,10 +482,11 @@ class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
||||
pk_url_kwarg = "user_id"
|
||||
template_name = "core/user_group.jinja"
|
||||
form_class = modelform_factory(User, fields=['groups'],
|
||||
widgets={'groups':CheckboxSelectMultiple})
|
||||
widgets={'groups': CheckboxSelectMultiple})
|
||||
context_object_name = "profile"
|
||||
current_tab = "groups"
|
||||
|
||||
|
||||
class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
|
||||
"""
|
||||
Displays the logged user's tools
|
||||
@ -481,6 +503,7 @@ class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
|
||||
kwargs['object'] = self.request.user
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserAccountBase(UserTabsMixin, DetailView):
|
||||
"""
|
||||
Base class for UserAccount
|
||||
@ -493,11 +516,12 @@ class UserAccountBase(UserTabsMixin, DetailView):
|
||||
res = super(UserAccountBase, self).dispatch(request, *arg, **kwargs)
|
||||
if (self.object == request.user
|
||||
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
|
||||
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name'] + settings.SITH_BOARD_SUFFIX)
|
||||
or request.user.is_root):
|
||||
return res
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
class UserAccountView(UserAccountBase):
|
||||
"""
|
||||
Display a user's account
|
||||
@ -517,8 +541,8 @@ class UserAccountView(UserAccountBase):
|
||||
date__month=month.month
|
||||
)
|
||||
stats[i].append({
|
||||
'sum':sum([calc(p) for p in q]),
|
||||
'date':month
|
||||
'sum': sum([calc(p) for p in q]),
|
||||
'date': month
|
||||
})
|
||||
i += 1
|
||||
return stats
|
||||
@ -551,6 +575,7 @@ class UserAccountView(UserAccountBase):
|
||||
print(repr(e))
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
|
||||
"""
|
||||
Display a user's account for month
|
||||
@ -568,4 +593,3 @@ class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
|
||||
pass
|
||||
kwargs['tab'] = "account"
|
||||
return kwargs
|
||||
|
||||
|
@ -36,4 +36,3 @@ admin.site.register(Selling)
|
||||
admin.site.register(Permanency)
|
||||
admin.site.register(CashRegisterSummary)
|
||||
admin.site.register(Eticket)
|
||||
|
||||
|
@ -22,13 +22,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.db import models, DataError
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.forms import ValidationError
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from datetime import timedelta, date
|
||||
@ -43,6 +42,7 @@ from accounting.models import CurrencyField
|
||||
from core.models import Group, User, Notification
|
||||
from subscription.models import Subscription
|
||||
|
||||
|
||||
class Customer(models.Model):
|
||||
"""
|
||||
This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
|
||||
@ -55,7 +55,7 @@ class Customer(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _('customer')
|
||||
verbose_name_plural = _('customers')
|
||||
ordering = ['account_id',]
|
||||
ordering = ['account_id', ]
|
||||
|
||||
def __str__(self):
|
||||
return "%s - %s" % (self.user.username, self.account_id)
|
||||
@ -68,9 +68,9 @@ class Customer(models.Model):
|
||||
def generate_account_id(number):
|
||||
number = str(number)
|
||||
letter = random.choice(string.ascii_lowercase)
|
||||
while Customer.objects.filter(account_id=number+letter).exists():
|
||||
while Customer.objects.filter(account_id=number + letter).exists():
|
||||
letter = random.choice(string.ascii_lowercase)
|
||||
return number+letter
|
||||
return number + letter
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.amount < 0:
|
||||
@ -118,6 +118,7 @@ class ProductType(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('counter:producttype_list')
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
"""
|
||||
This describes a product, with all its related informations
|
||||
@ -156,13 +157,14 @@ class Product(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('counter:product_list')
|
||||
|
||||
|
||||
class Counter(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
club = models.ForeignKey(Club, related_name="counters", verbose_name=_("club"))
|
||||
products = models.ManyToManyField(Product, related_name="counters", verbose_name=_("products"), blank=True)
|
||||
type = models.CharField(_('counter type'),
|
||||
max_length=255,
|
||||
choices=[('BAR',_('Bar')), ('OFFICE',_('Office')), ('EBOUTIC',_('Eboutic'))])
|
||||
choices=[('BAR', _('Bar')), ('OFFICE', _('Office')), ('EBOUTIC', _('Eboutic'))])
|
||||
sellers = models.ManyToManyField(User, verbose_name=_('sellers'), related_name='counters', blank=True)
|
||||
edit_groups = models.ManyToManyField(Group, related_name="editable_counters", blank=True)
|
||||
view_groups = models.ManyToManyField(Group, related_name="viewable_counters", blank=True)
|
||||
@ -173,7 +175,7 @@ class Counter(models.Model):
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name == "edit_groups":
|
||||
return Group.objects.filter(name=self.club.unix_name+settings.SITH_BOARD_SUFFIX).all()
|
||||
return Group.objects.filter(name=self.club.unix_name + settings.SITH_BOARD_SUFFIX).all()
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
def __str__(self):
|
||||
@ -265,6 +267,7 @@ class Counter(models.Model):
|
||||
"""
|
||||
return [b.id for b in self.get_barmen_list()]
|
||||
|
||||
|
||||
class Refilling(models.Model):
|
||||
"""
|
||||
Handle the refilling
|
||||
@ -309,6 +312,7 @@ class Refilling(models.Model):
|
||||
).save()
|
||||
super(Refilling, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class Selling(models.Model):
|
||||
"""
|
||||
Handle the sellings
|
||||
@ -331,7 +335,7 @@ class Selling(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return "Selling: %d x %s (%f) for %s" % (self.quantity, self.label,
|
||||
self.quantity*self.unit_price, self.customer.user.get_display_name())
|
||||
self.quantity * self.unit_price, self.customer.user.get_display_name())
|
||||
|
||||
def is_owned_by(self, user):
|
||||
return user.is_owner(self.counter) and self.payment_method != "CARD"
|
||||
@ -352,7 +356,7 @@ class Selling(models.Model):
|
||||
"You bought an eticket for the event %(event)s.\nYou can download it on this page %(url)s."
|
||||
) % {
|
||||
'event': event,
|
||||
'url':''.join((
|
||||
'url': ''.join((
|
||||
'<a href="',
|
||||
self.customer.get_full_url(),
|
||||
'">',
|
||||
@ -414,7 +418,8 @@ class Selling(models.Model):
|
||||
try:
|
||||
if self.product.eticket:
|
||||
self.send_mail_customer()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
Notification(
|
||||
user=self.customer.user,
|
||||
url=reverse('core:user_account_detail',
|
||||
@ -424,6 +429,7 @@ class Selling(models.Model):
|
||||
).save()
|
||||
super(Selling, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class Permanency(models.Model):
|
||||
"""
|
||||
This class aims at storing a traceability of who was barman where and when
|
||||
@ -444,6 +450,7 @@ class Permanency(models.Model):
|
||||
self.end.strftime("%Y-%m-%d %H:%M:%S") if self.end else "",
|
||||
)
|
||||
|
||||
|
||||
class CashRegisterSummary(models.Model):
|
||||
user = models.ForeignKey(User, related_name="cash_summaries", verbose_name=_("user"))
|
||||
counter = models.ForeignKey(Counter, related_name="cash_summaries", verbose_name=_("counter"))
|
||||
@ -515,6 +522,7 @@ class CashRegisterSummary(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('counter:cash_summary_list')
|
||||
|
||||
|
||||
class CashRegisterSummaryItem(models.Model):
|
||||
cash_summary = models.ForeignKey(CashRegisterSummary, related_name="items", verbose_name=_("cash summary"))
|
||||
value = CurrencyField(_("value"))
|
||||
@ -524,6 +532,7 @@ class CashRegisterSummaryItem(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("cash register summary item")
|
||||
|
||||
|
||||
class Eticket(models.Model):
|
||||
"""
|
||||
Eticket can be linked to a product an allows PDF generation
|
||||
@ -552,6 +561,6 @@ class Eticket(models.Model):
|
||||
return user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
||||
|
||||
def get_hash(self, string):
|
||||
import hashlib, hmac
|
||||
import hashlib
|
||||
import hmac
|
||||
return hmac.new(bytes(self.secret, 'utf-8'), bytes(string, 'utf-8'), hashlib.sha1).hexdigest()
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
import re
|
||||
|
||||
from pprint import pprint
|
||||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.management import call_command
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from counter.views import *
|
||||
|
||||
|
156
counter/views.py
156
counter/views.py
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.http import Http404
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
@ -31,7 +31,6 @@ from django.views.generic.edit import UpdateView, CreateView, DeleteView, Proces
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.utils import timezone
|
||||
from django import forms
|
||||
@ -43,16 +42,17 @@ import re
|
||||
import pytz
|
||||
from datetime import date, timedelta, datetime
|
||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select import make_ajax_field
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
|
||||
from core.views import CanViewMixin, TabedViewMixin
|
||||
from core.views.forms import LoginForm, SelectDate, SelectDateTime
|
||||
from core.models import User
|
||||
from subscription.models import Subscription
|
||||
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType, \
|
||||
CashRegisterSummary, CashRegisterSummaryItem, Eticket, Permanency
|
||||
from accounting.models import CurrencyField
|
||||
|
||||
|
||||
class CounterAdminMixin(View):
|
||||
"""
|
||||
This view is made to protect counter admin section
|
||||
@ -72,13 +72,13 @@ class CounterAdminMixin(View):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not (request.user.is_root or self._test_group(request.user)
|
||||
or self._test_club(request.user)):
|
||||
raise PermissionDenied
|
||||
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class GetUserForm(forms.Form):
|
||||
"""
|
||||
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
||||
@ -107,25 +107,29 @@ class GetUserForm(forms.Form):
|
||||
cleaned_data['user'] = cus.user
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class RefillForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class':'focus'}))
|
||||
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class': 'focus'}))
|
||||
|
||||
class Meta:
|
||||
model = Refilling
|
||||
fields = ['amount', 'payment_method', 'bank']
|
||||
|
||||
|
||||
class CounterTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
if hasattr(self.object, 'stock_owner') :
|
||||
if hasattr(self.object, 'stock_owner'):
|
||||
return self.object.stock_owner.counter
|
||||
else:
|
||||
return self.object
|
||||
|
||||
def get_list_of_tabs(self):
|
||||
tab_list = []
|
||||
tab_list.append({
|
||||
'url': reverse_lazy('counter:details',
|
||||
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id }),
|
||||
kwargs={'counter_id': self.object.stock_owner.counter.id if hasattr(self.object, 'stock_owner') else self.object.id}),
|
||||
'slug': 'counter',
|
||||
'name': _("Counter"),
|
||||
})
|
||||
@ -149,9 +153,11 @@ class CounterTabsMixin(TabedViewMixin):
|
||||
'slug': 'take_items_from_stock',
|
||||
'name': _("Take items from stock"),
|
||||
})
|
||||
except: pass # The counter just have no stock
|
||||
except:
|
||||
pass # The counter just have no stock
|
||||
return tab_list
|
||||
|
||||
|
||||
class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, FormMixin):
|
||||
"""
|
||||
The public (barman) view
|
||||
@ -167,7 +173,7 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
|
||||
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
|
||||
self.request.session['counter_token'] == self.object.token): # Check the token to avoid the bar to be stolen
|
||||
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
|
||||
kwargs={'counter_id': self.object.id})+'?bad_location')
|
||||
kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||
return super(CounterMain, self).post(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@ -210,6 +216,7 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('counter:click', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
The click view
|
||||
@ -228,7 +235,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
raise Http404
|
||||
if obj.type == "BAR":
|
||||
if not ('counter_token' in request.session.keys() and
|
||||
request.session['counter_token'] == obj.token) or len(obj.get_barmen_list())<1:
|
||||
request.session['counter_token'] == obj.token) or len(obj.get_barmen_list()) < 1:
|
||||
raise PermissionDenied
|
||||
else:
|
||||
if not request.user.is_authenticated():
|
||||
@ -263,7 +270,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
if self.object.type == "BAR" and not ('counter_token' in self.request.session.keys() and
|
||||
self.request.session['counter_token'] == self.object.token): # Also check the token to avoid the bar to be stolen
|
||||
return HttpResponseRedirect(reverse_lazy('counter:details', args=self.args,
|
||||
kwargs={'counter_id': self.object.id})+'?bad_location')
|
||||
kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||
if 'basket' not in request.session.keys():
|
||||
request.session['basket'] = {}
|
||||
request.session['basket_total'] = 0
|
||||
@ -312,7 +319,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
def sum_basket(self, request):
|
||||
total = 0
|
||||
for pid,infos in request.session['basket'].items():
|
||||
for pid, infos in request.session['basket'].items():
|
||||
total += infos['price'] * infos['qty']
|
||||
return total / 100
|
||||
|
||||
@ -323,7 +330,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
except:
|
||||
return 0
|
||||
|
||||
def add_product(self, request, q = 1, p=None):
|
||||
def add_product(self, request, q=1, p=None):
|
||||
"""
|
||||
Add a product to the basket
|
||||
q is the quantity passed as integer
|
||||
@ -349,7 +356,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
total_qty_mod_6 = self.get_total_quantity_for_pid(request, pid) % 6
|
||||
bq = int((total_qty_mod_6 + q) / 6) # Integer division
|
||||
q -= bq
|
||||
if self.customer.amount < (total + round(q*float(price),2)): # Check for enough money
|
||||
if self.customer.amount < (total + round(q * float(price), 2)): # Check for enough money
|
||||
request.session['not_enough'] = True
|
||||
return False
|
||||
if product.limit_age >= 18 and not self.customer.user.date_of_birth:
|
||||
@ -368,7 +375,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
request.session['basket'][pid]['qty'] += q
|
||||
request.session['basket'][pid]['bonus_qty'] += bq
|
||||
else: # or create if not
|
||||
request.session['basket'][pid] = {'qty': q, 'price': int(price*100), 'bonus_qty': bq}
|
||||
request.session['basket'][pid] = {'qty': q, 'price': int(price * 100), 'bonus_qty': bq}
|
||||
request.session.modified = True
|
||||
return True
|
||||
|
||||
@ -414,7 +421,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
""" Finish the click session, and validate the basket """
|
||||
with transaction.atomic():
|
||||
request.session['last_basket'] = []
|
||||
for pid,infos in request.session['basket'].items():
|
||||
for pid, infos in request.session['basket'].items():
|
||||
# This duplicates code for DB optimization (prevent to load many times the same object)
|
||||
p = Product.objects.filter(pk=pid).first()
|
||||
if self.is_barman_price():
|
||||
@ -423,7 +430,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
uprice = p.selling_price
|
||||
if uprice * infos['qty'] > self.customer.amount:
|
||||
raise DataError(_("You have not enough money to buy all the basket"))
|
||||
request.session['last_basket'].append("%d x %s" % (infos['qty']+infos['bonus_qty'], p.name))
|
||||
request.session['last_basket'].append("%d x %s" % (infos['qty'] + infos['bonus_qty'], p.name))
|
||||
s = Selling(label=p.name, product=p, club=p.club, counter=self.object, unit_price=uprice,
|
||||
quantity=infos['qty'], seller=self.operator, customer=self.customer)
|
||||
s.save()
|
||||
@ -470,6 +477,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['categories'] = ProductType.objects.all()
|
||||
return kwargs
|
||||
|
||||
|
||||
class CounterLogin(RedirectView):
|
||||
"""
|
||||
Handle the login of a barman
|
||||
@ -477,6 +485,7 @@ class CounterLogin(RedirectView):
|
||||
Logged barmen are stored in the Permanency model
|
||||
"""
|
||||
permanent = False
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Register the logged user as barman for this counter
|
||||
@ -499,10 +508,12 @@ class CounterLogin(RedirectView):
|
||||
return super(CounterLogin, self).post(request, *args, **kwargs)
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
return reverse_lazy('counter:details', args=args, kwargs=kwargs)+"?"+'&'.join(self.errors)
|
||||
return reverse_lazy('counter:details', args=args, kwargs=kwargs) + "?" + '&'.join(self.errors)
|
||||
|
||||
|
||||
class CounterLogout(RedirectView):
|
||||
permanent = False
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Unregister the user from the barman
|
||||
@ -515,7 +526,8 @@ class CounterLogout(RedirectView):
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
return reverse_lazy('counter:details', args=args, kwargs=kwargs)
|
||||
|
||||
## Counter admin views
|
||||
# Counter admin views
|
||||
|
||||
|
||||
class CounterAdminTabsMixin(TabedViewMixin):
|
||||
tabs_title = _("Counter administration")
|
||||
@ -562,6 +574,7 @@ class CounterAdminTabsMixin(TabedViewMixin):
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -570,6 +583,7 @@ class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
||||
template_name = 'counter/counter_list.jinja'
|
||||
current_tab = "counters"
|
||||
|
||||
|
||||
class CounterEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Counter
|
||||
@ -577,6 +591,7 @@ class CounterEditForm(forms.ModelForm):
|
||||
sellers = make_ajax_field(Counter, 'sellers', 'users', help_text="")
|
||||
products = make_ajax_field(Counter, 'products', 'products', help_text="")
|
||||
|
||||
|
||||
class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's manager)
|
||||
@ -595,6 +610,7 @@ class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('counter:admin', kwargs={'counter_id': self.object.id})
|
||||
|
||||
|
||||
class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's admin)
|
||||
@ -605,16 +621,18 @@ class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "counters"
|
||||
|
||||
|
||||
class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
Create a counter (for the admins)
|
||||
"""
|
||||
model = Counter
|
||||
form_class = modelform_factory(Counter, fields=['name', 'club', 'type', 'products'],
|
||||
widgets={'products':CheckboxSelectMultiple})
|
||||
widgets={'products': CheckboxSelectMultiple})
|
||||
template_name = 'core/create.jinja'
|
||||
current_tab = "counters"
|
||||
|
||||
|
||||
class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
||||
"""
|
||||
Delete a counter (for the admins)
|
||||
@ -627,6 +645,7 @@ class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
||||
|
||||
# Product management
|
||||
|
||||
|
||||
class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -635,6 +654,7 @@ class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
template_name = 'counter/producttype_list.jinja'
|
||||
current_tab = "product_types"
|
||||
|
||||
|
||||
class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
A create view for the admins
|
||||
@ -644,6 +664,7 @@ class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView
|
||||
template_name = 'core/create.jinja'
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -654,6 +675,7 @@ class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
pk_url_kwarg = "type_id"
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -664,6 +686,7 @@ class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView
|
||||
ordering = ['name']
|
||||
current_tab = "archive"
|
||||
|
||||
|
||||
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -674,6 +697,7 @@ class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
ordering = ['name']
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Product
|
||||
@ -702,6 +726,7 @@ class ProductEditForm(forms.ModelForm):
|
||||
c.save()
|
||||
return ret
|
||||
|
||||
|
||||
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
A create view for the admins
|
||||
@ -711,6 +736,7 @@ class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
template_name = 'core/create.jinja'
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -721,6 +747,7 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class RefillingDeleteView(DeleteView):
|
||||
"""
|
||||
Delete a refilling (for the admins)
|
||||
@ -745,6 +772,7 @@ class RefillingDeleteView(DeleteView):
|
||||
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
class SellingDeleteView(DeleteView):
|
||||
"""
|
||||
Delete a selling (for the admins)
|
||||
@ -771,6 +799,7 @@ class SellingDeleteView(DeleteView):
|
||||
|
||||
# Cash register summaries
|
||||
|
||||
|
||||
class CashRegisterSummaryForm(forms.Form):
|
||||
"""
|
||||
Provide the cash summary form
|
||||
@ -839,30 +868,46 @@ class CashRegisterSummaryForm(forms.Form):
|
||||
summary.save()
|
||||
summary.items.all().delete()
|
||||
# Cash
|
||||
if cd['ten_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
|
||||
if cd['twenty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
|
||||
if cd['fifty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
|
||||
if cd['one_euro']: CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
|
||||
if cd['two_euros']: CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
|
||||
if cd['five_euros']: CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
|
||||
if cd['ten_euros']: CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
|
||||
if cd['twenty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
|
||||
if cd['fifty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
|
||||
if cd['hundred_euros']: CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
|
||||
if cd['ten_cents']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
|
||||
if cd['twenty_cents']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
|
||||
if cd['fifty_cents']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
|
||||
if cd['one_euro']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
|
||||
if cd['two_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
|
||||
if cd['five_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
|
||||
if cd['ten_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
|
||||
if cd['twenty_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
|
||||
if cd['fifty_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
|
||||
if cd['hundred_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
|
||||
# Checks
|
||||
if cd['check_1_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
|
||||
if cd['check_1_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
|
||||
quantity=cd['check_1_quantity'], check=True).save()
|
||||
if cd['check_2_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
|
||||
if cd['check_2_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
|
||||
quantity=cd['check_2_quantity'], check=True).save()
|
||||
if cd['check_3_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
|
||||
if cd['check_3_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
|
||||
quantity=cd['check_3_quantity'], check=True).save()
|
||||
if cd['check_4_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
|
||||
if cd['check_4_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
|
||||
quantity=cd['check_4_quantity'], check=True).save()
|
||||
if cd['check_5_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
|
||||
if cd['check_5_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
|
||||
quantity=cd['check_5_quantity'], check=True).save()
|
||||
if summary.items.count() < 1:
|
||||
summary.delete()
|
||||
|
||||
|
||||
class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Provide the last operations to allow barmen to delete them
|
||||
@ -881,7 +926,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
request.session['counter_token'] and # check if not null for counters that have no token set
|
||||
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
||||
return super(CounterLastOperationsView, self).dispatch(request, *args, **kwargs)
|
||||
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id})+'?bad_location')
|
||||
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add form to the context """
|
||||
@ -891,6 +936,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['last_sellings'] = self.object.sellings.filter(date__gte=threshold).order_by('-id')[:20]
|
||||
return kwargs
|
||||
|
||||
|
||||
class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Provide the cash summary form
|
||||
@ -909,7 +955,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
request.session['counter_token'] and # check if not null for counters that have no token set
|
||||
Counter.objects.filter(token=request.session['counter_token']).exists()):
|
||||
return super(CounterCashSummaryView, self).dispatch(request, *args, **kwargs)
|
||||
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id})+'?bad_location')
|
||||
return HttpResponseRedirect(reverse('counter:details', kwargs={'counter_id': self.object.id}) + '?bad_location')
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
@ -933,6 +979,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['form'] = self.form
|
||||
return kwargs
|
||||
|
||||
|
||||
class CounterActivityView(DetailView):
|
||||
"""
|
||||
Show the bar activity
|
||||
@ -941,6 +988,7 @@ class CounterActivityView(DetailView):
|
||||
pk_url_kwarg = "counter_id"
|
||||
template_name = 'counter/activity.jinja'
|
||||
|
||||
|
||||
class CounterStatView(DetailView, CounterAdminMixin):
|
||||
"""
|
||||
Show the bar stats
|
||||
@ -957,14 +1005,14 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
||||
kwargs['User'] = User
|
||||
semester_start = Subscription.compute_start(d=date.today(), duration=3)
|
||||
kwargs['total_sellings'] = Selling.objects.filter(date__gte=semester_start,
|
||||
counter=self.object).aggregate(total_sellings=Sum(F('quantity')*F('unit_price'),
|
||||
counter=self.object).aggregate(total_sellings=Sum(F('quantity') * F('unit_price'),
|
||||
output_field=CurrencyField()))['total_sellings']
|
||||
kwargs['top'] = Selling.objects.values('customer__user').annotate(
|
||||
selling_sum=Sum(
|
||||
Case(When(counter=self.object,
|
||||
date__gte=semester_start,
|
||||
unit_price__gt=0,
|
||||
then=F('unit_price')*F('quantity')),
|
||||
then=F('unit_price') * F('quantity')),
|
||||
output_field=CurrencyField()
|
||||
)
|
||||
)
|
||||
@ -973,7 +1021,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
||||
perm_sum=Sum(
|
||||
Case(When(counter=self.object,
|
||||
end__gt=datetime(year=1999, month=1, day=1),
|
||||
then=F('end')-F('start')),
|
||||
then=F('end') - F('start')),
|
||||
output_field=models.DateTimeField()
|
||||
)
|
||||
)
|
||||
@ -983,14 +1031,14 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
||||
Case(When(counter=self.object,
|
||||
start__gt=semester_start,
|
||||
end__gt=datetime(year=1999, month=1, day=1),
|
||||
then=F('end')-F('start')),
|
||||
then=F('end') - F('start')),
|
||||
output_field=models.DateTimeField()
|
||||
)
|
||||
)
|
||||
).exclude(perm_sum=None).order_by('-perm_sum').all()[:100]
|
||||
|
||||
kwargs['sith_date']=settings.SITH_START_DATE[0]
|
||||
kwargs['semester_start']=semester_start
|
||||
kwargs['sith_date'] = settings.SITH_START_DATE[0]
|
||||
kwargs['semester_start'] = semester_start
|
||||
return kwargs
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@ -1003,6 +1051,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
||||
return super(CanEditMixin, self).dispatch(request, *args, **kwargs)
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""Edit cash summaries"""
|
||||
model = CashRegisterSummary
|
||||
@ -1015,10 +1064,12 @@ class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView)
|
||||
def get_success_url(self):
|
||||
return reverse('counter:cash_summary_list')
|
||||
|
||||
|
||||
class CashSummaryFormBase(forms.Form):
|
||||
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
||||
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
||||
|
||||
|
||||
class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""Display a list of cash summaries"""
|
||||
model = CashRegisterSummary
|
||||
@ -1056,6 +1107,7 @@ class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
kwargs['refilling_sums'][c.name] = sum([s.amount for s in refillings.all()])
|
||||
return kwargs
|
||||
|
||||
|
||||
class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
||||
template_name = 'counter/invoices_call.jinja'
|
||||
current_tab = 'invoices_call'
|
||||
@ -1069,24 +1121,25 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
||||
try:
|
||||
start_date = datetime.strptime(self.request.GET['month'], '%Y-%m')
|
||||
except:
|
||||
start_date = datetime(year=timezone.now().year, month=(timezone.now().month+10)%12+1, day=1)
|
||||
start_date = datetime(year=timezone.now().year, month=(timezone.now().month + 10) % 12 + 1, day=1)
|
||||
start_date = start_date.replace(tzinfo=pytz.UTC)
|
||||
end_date = (start_date + timedelta(days=32)).replace(day=1, hour=0, minute=0, microsecond=0)
|
||||
from django.db.models import Sum, Case, When, F, DecimalField
|
||||
kwargs['sum_cb'] = sum([r.amount for r in Refilling.objects.filter(payment_method='CARD', is_validated=True,
|
||||
date__gte=start_date, date__lte=end_date)])
|
||||
kwargs['sum_cb'] += sum([s.quantity*s.unit_price for s in Selling.objects.filter(payment_method='CARD', is_validated=True,
|
||||
kwargs['sum_cb'] += sum([s.quantity * s.unit_price for s in Selling.objects.filter(payment_method='CARD', is_validated=True,
|
||||
date__gte=start_date, date__lte=end_date)])
|
||||
kwargs['start_date'] = start_date
|
||||
kwargs['sums'] = Selling.objects.values('club__name').annotate(selling_sum=Sum(
|
||||
Case(When(date__gte=start_date,
|
||||
date__lt=end_date,
|
||||
then=F('unit_price')*F('quantity')),
|
||||
then=F('unit_price') * F('quantity')),
|
||||
output_field=CurrencyField()
|
||||
)
|
||||
)).exclude(selling_sum=None).order_by('-selling_sum')
|
||||
return kwargs
|
||||
|
||||
|
||||
class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -1096,6 +1149,7 @@ class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
ordering = ['id']
|
||||
current_tab = "etickets"
|
||||
|
||||
|
||||
class EticketForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Eticket
|
||||
@ -1105,6 +1159,7 @@ class EticketForm(forms.ModelForm):
|
||||
}
|
||||
product = AutoCompleteSelectField('products', show_help_text=False, label=_("Product"), required=True)
|
||||
|
||||
|
||||
class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
Create an eticket
|
||||
@ -1114,6 +1169,7 @@ class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
form_class = EticketForm
|
||||
current_tab = "etickets"
|
||||
|
||||
|
||||
class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit an eticket
|
||||
@ -1124,6 +1180,7 @@ class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
pk_url_kwarg = "eticket_id"
|
||||
current_tab = "etickets"
|
||||
|
||||
|
||||
class EticketPDFView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display the PDF of an eticket
|
||||
@ -1180,7 +1237,7 @@ class EticketPDFView(CanViewMixin, DetailView):
|
||||
bounds = qrcode.getBounds()
|
||||
width = bounds[2] - bounds[0]
|
||||
height = bounds[3] - bounds[1]
|
||||
d = Drawing(260, 260, transform=[260./width, 0, 0, 260./height, 0, 0])
|
||||
d = Drawing(260, 260, transform=[260. / width, 0, 0, 260. / height, 0, 0])
|
||||
d.add(qrcode)
|
||||
renderPDF.draw(d, p, 10.5 * cm - 130, 6.1 * cm)
|
||||
p.drawCentredString(10.5 * cm, 6 * cm, code)
|
||||
@ -1195,4 +1252,3 @@ class EticketPDFView(CanViewMixin, DetailView):
|
||||
p.showPage()
|
||||
p.save()
|
||||
return response
|
||||
|
||||
|
@ -27,9 +27,9 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from accounting.models import CurrencyField
|
||||
from counter.models import Counter, Product, Customer, Selling, Refilling
|
||||
from counter.models import Counter, Product, Selling, Refilling
|
||||
from core.models import User
|
||||
from subscription.models import Subscription
|
||||
|
||||
|
||||
class Basket(models.Model):
|
||||
"""
|
||||
@ -38,7 +38,7 @@ class Basket(models.Model):
|
||||
user = models.ForeignKey(User, related_name='baskets', verbose_name=_('user'), blank=False)
|
||||
date = models.DateTimeField(_('date'), auto_now=True)
|
||||
|
||||
def add_product(self, p, q = 1):
|
||||
def add_product(self, p, q=1):
|
||||
item = self.items.filter(product_id=p.id).first()
|
||||
if item is None:
|
||||
BasketItem(basket=self, product_id=p.id, product_name=p.name, type_id=p.product_type.id,
|
||||
@ -47,7 +47,7 @@ class Basket(models.Model):
|
||||
item.quantity += q
|
||||
item.save()
|
||||
|
||||
def del_product(self, p, q = 1):
|
||||
def del_product(self, p, q=1):
|
||||
item = self.items.filter(product_id=p.id).first()
|
||||
if item is not None:
|
||||
item.quantity -= q
|
||||
@ -64,6 +64,7 @@ class Basket(models.Model):
|
||||
def __str__(self):
|
||||
return "%s's basket (%d items)" % (self.user, self.items.all().count())
|
||||
|
||||
|
||||
class Invoice(models.Model):
|
||||
"""
|
||||
Invoices are generated once the payment has been validated
|
||||
@ -120,6 +121,7 @@ class Invoice(models.Model):
|
||||
self.validated = True
|
||||
self.save()
|
||||
|
||||
|
||||
class AbstractBaseItem(models.Model):
|
||||
product_id = models.IntegerField(_('product id'))
|
||||
product_name = models.CharField(_('product name'), max_length=255)
|
||||
@ -133,8 +135,10 @@ class AbstractBaseItem(models.Model):
|
||||
def __str__(self):
|
||||
return "Item: %s (%s) x%d" % (self.product_name, self.product_unit_price, self.quantity)
|
||||
|
||||
|
||||
class BasketItem(AbstractBaseItem):
|
||||
basket = models.ForeignKey(Basket, related_name='items', verbose_name=_('basket'))
|
||||
|
||||
|
||||
class InvoiceItem(AbstractBaseItem):
|
||||
invoice = models.ForeignKey(Invoice, related_name='items', verbose_name=_('invoice'))
|
||||
|
@ -34,7 +34,8 @@ from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
|
||||
from core.models import User
|
||||
from counter.models import Customer, ProductType, Product, Counter, Refilling
|
||||
from counter.models import Product, Counter, Refilling
|
||||
|
||||
|
||||
class EbouticTest(TestCase):
|
||||
def setUp(self):
|
||||
@ -93,7 +94,6 @@ class EbouticTest(TestCase):
|
||||
" <td>Barbar</td>\\n <td>1</td>\\n <td>1.70 \\xe2\\x82\\xac</td>\\n"
|
||||
" <td>Compte utilisateur</td>" in str(response.content))
|
||||
|
||||
|
||||
def test_buy_simple_product_with_credit_card(self):
|
||||
self.client.login(username='subscriber', password='plop')
|
||||
response = self.client.post(reverse("eboutic:main"), {
|
||||
@ -171,6 +171,3 @@ class EbouticTest(TestCase):
|
||||
" <td>15.00 \\xe2\\x82\\xac</td>" in str(response.content))
|
||||
response = self.client.get(reverse("core:user_profile", kwargs={"user_id": self.old_subscriber.id}))
|
||||
self.assertTrue("Cotisant jusqu\\'au" in str(response.content))
|
||||
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from eboutic.views import *
|
||||
|
||||
@ -33,6 +33,3 @@ urlpatterns = [
|
||||
url(r'^pay$', EbouticPayWithSith.as_view(), name='pay_with_sith'),
|
||||
url(r'^et_autoanswer$', EtransactionAutoAnswer.as_view(), name='etransation_autoanswer'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
@ -24,23 +24,21 @@
|
||||
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
import hmac
|
||||
import base64
|
||||
from OpenSSL import crypto
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.views.generic import TemplateView, View
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.shortcuts import render
|
||||
from django.db import transaction, DataError
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from counter.models import Customer, Counter, ProductType, Selling
|
||||
from eboutic.models import Basket, Invoice, BasketItem, InvoiceItem
|
||||
from eboutic.models import Basket, Invoice, InvoiceItem
|
||||
|
||||
|
||||
class EbouticMain(TemplateView):
|
||||
template_name = 'eboutic/eboutic_main.jinja'
|
||||
@ -77,7 +75,6 @@ class EbouticMain(TemplateView):
|
||||
self.del_product(request)
|
||||
return self.render_to_response(self.get_context_data(**kwargs))
|
||||
|
||||
|
||||
def add_product(self, request):
|
||||
""" Add a product to the basket """
|
||||
try:
|
||||
@ -108,6 +105,7 @@ class EbouticMain(TemplateView):
|
||||
kwargs['categories'] = kwargs['categories'].exclude(id=settings.SITH_PRODUCTTYPE_SUBSCRIPTION)
|
||||
return kwargs
|
||||
|
||||
|
||||
class EbouticCommand(TemplateView):
|
||||
template_name = 'eboutic/eboutic_makecommand.jinja'
|
||||
|
||||
@ -136,7 +134,7 @@ class EbouticCommand(TemplateView):
|
||||
kwargs['et_request']['PBX_SITE'] = settings.SITH_EBOUTIC_PBX_SITE
|
||||
kwargs['et_request']['PBX_RANG'] = settings.SITH_EBOUTIC_PBX_RANG
|
||||
kwargs['et_request']['PBX_IDENTIFIANT'] = settings.SITH_EBOUTIC_PBX_IDENTIFIANT
|
||||
kwargs['et_request']['PBX_TOTAL'] = int(self.basket.get_total()*100)
|
||||
kwargs['et_request']['PBX_TOTAL'] = int(self.basket.get_total() * 100)
|
||||
kwargs['et_request']['PBX_DEVISE'] = 978 # This is Euro. ET support only this value anyway
|
||||
kwargs['et_request']['PBX_CMD'] = self.basket.id
|
||||
kwargs['et_request']['PBX_PORTEUR'] = self.basket.user.email
|
||||
@ -146,10 +144,11 @@ class EbouticCommand(TemplateView):
|
||||
kwargs['et_request']['PBX_TYPECARTE'] = "CB"
|
||||
kwargs['et_request']['PBX_TIME'] = str(datetime.now().replace(microsecond=0).isoformat('T'))
|
||||
kwargs['et_request']['PBX_HMAC'] = hmac.new(settings.SITH_EBOUTIC_HMAC_KEY,
|
||||
bytes("&".join(["%s=%s"%(k,v) for k,v in kwargs['et_request'].items()]), 'utf-8'),
|
||||
bytes("&".join(["%s=%s" % (k, v) for k, v in kwargs['et_request'].items()]), 'utf-8'),
|
||||
"sha512").hexdigest().upper()
|
||||
return kwargs
|
||||
|
||||
|
||||
class EbouticPayWithSith(TemplateView):
|
||||
template_name = 'eboutic/eboutic_payment_result.jinja'
|
||||
|
||||
@ -189,6 +188,7 @@ class EbouticPayWithSith(TemplateView):
|
||||
kwargs['not_enough'] = True
|
||||
return self.render_to_response(self.get_context_data(**kwargs))
|
||||
|
||||
|
||||
class EtransactionAutoAnswer(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if (not 'Amount' in request.GET.keys() or
|
||||
@ -221,8 +221,7 @@ class EtransactionAutoAnswer(View):
|
||||
i.validate()
|
||||
b.delete()
|
||||
except Exception as e:
|
||||
return HttpResponse("Payment failed with error: "+repr(e), status=400)
|
||||
return HttpResponse("Payment failed with error: " + repr(e), status=400)
|
||||
return HttpResponse()
|
||||
else:
|
||||
return HttpResponse("Payment failed with error: "+request.GET['Error'], status=400)
|
||||
|
||||
return HttpResponse("Payment failed with error: " + request.GET['Error'], status=400)
|
||||
|
@ -23,11 +23,9 @@
|
||||
#
|
||||
|
||||
from django.db import models
|
||||
from django.core import validators
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
@ -35,9 +33,10 @@ from django.utils.functional import cached_property
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
|
||||
from core.models import User, MetaGroup, Group, SithFile
|
||||
from core.models import User, Group
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class Forum(models.Model):
|
||||
"""
|
||||
The Forum class, made as a tree to allow nice tidy organization
|
||||
@ -118,10 +117,12 @@ class Forum(models.Model):
|
||||
def is_owned_by(self, user):
|
||||
if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID):
|
||||
return True
|
||||
try: m = Forum._club_memberships[self.id][user.id]
|
||||
try:
|
||||
m = Forum._club_memberships[self.id][user.id]
|
||||
except:
|
||||
m = self.owner_club.get_membership_for(user)
|
||||
try: Forum._club_memberships[self.id][user.id] = m
|
||||
try:
|
||||
Forum._club_memberships[self.id][user.id] = m
|
||||
except:
|
||||
Forum._club_memberships[self.id] = {}
|
||||
Forum._club_memberships[self.id][user.id] = m
|
||||
@ -178,6 +179,7 @@ class Forum(models.Model):
|
||||
l += c.get_children_list()
|
||||
return l
|
||||
|
||||
|
||||
class ForumTopic(models.Model):
|
||||
forum = models.ForeignKey(Forum, related_name='topics')
|
||||
author = models.ForeignKey(User, related_name='forum_topics')
|
||||
@ -225,6 +227,7 @@ class ForumTopic(models.Model):
|
||||
def title(self):
|
||||
return self._title
|
||||
|
||||
|
||||
class ForumMessage(models.Model):
|
||||
"""
|
||||
"A ForumMessage object represents a message in the forum" -- Cpt. Obvious
|
||||
@ -282,10 +285,11 @@ class ForumMessage(models.Model):
|
||||
return int(self.topic.messages.filter(id__lt=self.id).count() / settings.SITH_FORUM_PAGE_LENGTH) + 1
|
||||
|
||||
def mark_as_read(self, user):
|
||||
try: # Need the try/except because of AnonymousUser
|
||||
try: # Need the try/except because of AnonymousUser
|
||||
if not self.is_read(user):
|
||||
self.readers.add(user)
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
|
||||
def is_read(self, user):
|
||||
return (self.date < user.forum_infos.last_read_date) or (user in self.readers.all())
|
||||
@ -296,11 +300,13 @@ class ForumMessage(models.Model):
|
||||
return meta.action == "DELETE"
|
||||
return False
|
||||
|
||||
|
||||
MESSAGE_META_ACTIONS = [
|
||||
('EDIT', _("Message edited by")),
|
||||
('DELETE', _("Message deleted by")),
|
||||
('UNDELETE', _("Message undeleted by")),
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class ForumMessageMeta(models.Model):
|
||||
user = models.ForeignKey(User, related_name="forum_message_metas")
|
||||
@ -326,4 +332,3 @@ class ForumUserInfo(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user)
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from forum.views import *
|
||||
|
||||
|
@ -22,30 +22,30 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic import ListView, DetailView, RedirectView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select import make_ajax_field
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
||||
from core.views.forms import MarkdownInput
|
||||
from core.models import Page
|
||||
from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
|
||||
|
||||
|
||||
class ForumMainView(ListView):
|
||||
queryset = Forum.objects.filter(parent=None).prefetch_related("children___last_message__author", "children___last_message__topic")
|
||||
template_name = "forum/main.jinja"
|
||||
|
||||
|
||||
class ForumMarkAllAsRead(RedirectView):
|
||||
permanent = False
|
||||
url = reverse_lazy('forum:last_unread')
|
||||
@ -57,9 +57,11 @@ class ForumMarkAllAsRead(RedirectView):
|
||||
fi.save()
|
||||
for m in request.user.read_messages.filter(date__lt=fi.last_read_date):
|
||||
m.readers.remove(request.user) # Clean up to keep table low in data
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ForumLastUnread(ListView):
|
||||
model = ForumTopic
|
||||
template_name = "forum/last_unread.jinja"
|
||||
@ -73,6 +75,7 @@ class ForumLastUnread(ListView):
|
||||
.prefetch_related('forum__edit_groups')
|
||||
return topic_list
|
||||
|
||||
|
||||
class ForumForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Forum
|
||||
@ -80,6 +83,7 @@ class ForumForm(forms.ModelForm):
|
||||
edit_groups = make_ajax_field(Forum, 'edit_groups', 'groups', help_text="")
|
||||
view_groups = make_ajax_field(Forum, 'view_groups', 'groups', help_text="")
|
||||
|
||||
|
||||
class ForumCreateView(CanCreateMixin, CreateView):
|
||||
model = Forum
|
||||
form_class = ForumForm
|
||||
@ -93,12 +97,15 @@ class ForumCreateView(CanCreateMixin, CreateView):
|
||||
init['owner_club'] = parent.owner_club
|
||||
init['edit_groups'] = parent.edit_groups.all()
|
||||
init['view_groups'] = parent.view_groups.all()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
|
||||
class ForumEditForm(ForumForm):
|
||||
recursive = forms.BooleanField(label=_("Apply rights and club owner recursively"), required=False)
|
||||
|
||||
|
||||
class ForumEditView(CanEditPropMixin, UpdateView):
|
||||
model = Forum
|
||||
pk_url_kwarg = "forum_id"
|
||||
@ -112,12 +119,14 @@ class ForumEditView(CanEditPropMixin, UpdateView):
|
||||
self.object.apply_rights_recursively()
|
||||
return ret
|
||||
|
||||
|
||||
class ForumDeleteView(CanEditPropMixin, DeleteView):
|
||||
model = Forum
|
||||
pk_url_kwarg = "forum_id"
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
success_url = reverse_lazy('forum:main')
|
||||
|
||||
|
||||
class ForumDetailView(CanViewMixin, DetailView):
|
||||
model = Forum
|
||||
template_name = "forum/forum.jinja"
|
||||
@ -139,6 +148,7 @@ class ForumDetailView(CanViewMixin, DetailView):
|
||||
kwargs["topics"] = paginator.page(paginator.num_pages)
|
||||
return kwargs
|
||||
|
||||
|
||||
class TopicForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = ForumMessage
|
||||
@ -148,6 +158,7 @@ class TopicForm(forms.ModelForm):
|
||||
}
|
||||
title = forms.CharField(required=True, label=_("Title"))
|
||||
|
||||
|
||||
class ForumTopicCreateView(CanCreateMixin, CreateView):
|
||||
model = ForumMessage
|
||||
form_class = TopicForm
|
||||
@ -166,12 +177,14 @@ class ForumTopicCreateView(CanCreateMixin, CreateView):
|
||||
form.instance.author = self.request.user
|
||||
return super(ForumTopicCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class ForumTopicEditView(CanEditMixin, UpdateView):
|
||||
model = ForumTopic
|
||||
fields = ['forum']
|
||||
pk_url_kwarg = "topic_id"
|
||||
template_name = "core/edit.jinja"
|
||||
|
||||
|
||||
class ForumTopicDetailView(CanViewMixin, DetailView):
|
||||
model = ForumTopic
|
||||
pk_url_kwarg = "topic_id"
|
||||
@ -186,7 +199,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
|
||||
kwargs['first_unread_message_id'] = msg.id
|
||||
except:
|
||||
kwargs['first_unread_message_id'] = float("inf")
|
||||
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')\
|
||||
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')
|
||||
.prefetch_related('topic__forum__edit_groups', 'readers').order_by('date'),
|
||||
settings.SITH_FORUM_PAGE_LENGTH)
|
||||
page = self.request.GET.get('page')
|
||||
@ -198,6 +211,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
|
||||
kwargs["msgs"] = paginator.page(paginator.num_pages)
|
||||
return kwargs
|
||||
|
||||
|
||||
class ForumMessageView(SingleObjectMixin, RedirectView):
|
||||
model = ForumMessage
|
||||
pk_url_kwarg = "message_id"
|
||||
@ -207,9 +221,10 @@ class ForumMessageView(SingleObjectMixin, RedirectView):
|
||||
self.object = self.get_object()
|
||||
return self.object.get_url()
|
||||
|
||||
|
||||
class ForumMessageEditView(CanEditMixin, UpdateView):
|
||||
model = ForumMessage
|
||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message',], widgets={'message': MarkdownInput})
|
||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
|
||||
template_name = "forum/reply.jinja"
|
||||
pk_url_kwarg = "message_id"
|
||||
|
||||
@ -222,6 +237,7 @@ class ForumMessageEditView(CanEditMixin, UpdateView):
|
||||
kwargs['topic'] = self.object.topic
|
||||
return kwargs
|
||||
|
||||
|
||||
class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
|
||||
model = ForumMessage
|
||||
pk_url_kwarg = "message_id"
|
||||
@ -233,6 +249,7 @@ class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
|
||||
ForumMessageMeta(message=self.object, user=self.request.user, action="DELETE").save()
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
|
||||
class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
|
||||
model = ForumMessage
|
||||
pk_url_kwarg = "message_id"
|
||||
@ -244,9 +261,10 @@ class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
|
||||
ForumMessageMeta(message=self.object, user=self.request.user, action="UNDELETE").save()
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
|
||||
class ForumMessageCreateView(CanCreateMixin, CreateView):
|
||||
model = ForumMessage
|
||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message',], widgets={'message': MarkdownInput})
|
||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
|
||||
template_name = "forum/reply.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@ -277,4 +295,3 @@ class ForumMessageCreateView(CanCreateMixin, CreateView):
|
||||
kwargs = super(ForumMessageCreateView, self).get_context_data(**kwargs)
|
||||
kwargs['topic'] = self.topic
|
||||
return kwargs
|
||||
|
||||
|
@ -27,12 +27,13 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from counter.models import Counter, Product
|
||||
from counter.models import Counter
|
||||
from core.models import User
|
||||
from club.models import Club
|
||||
|
||||
# Create your models here.
|
||||
|
||||
|
||||
class Launderette(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
counter = models.OneToOneField(Counter, verbose_name=_('counter'), related_name='launderette')
|
||||
@ -78,6 +79,7 @@ class Launderette(models.Model):
|
||||
def token_list(self):
|
||||
return [t.id for t in self.get_token_list()]
|
||||
|
||||
|
||||
class Machine(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
launderette = models.ForeignKey(Launderette, related_name='machines', verbose_name=_('launderette'))
|
||||
@ -103,6 +105,7 @@ class Machine(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('launderette:launderette_admin', kwargs={"launderette_id": self.launderette.id})
|
||||
|
||||
|
||||
class Token(models.Model):
|
||||
name = models.CharField(_('name'), max_length=5)
|
||||
launderette = models.ForeignKey(Launderette, related_name='tokens', verbose_name=_('launderette'))
|
||||
@ -140,6 +143,7 @@ class Token(models.Model):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class Slot(models.Model):
|
||||
start_date = models.DateTimeField(_('start date'))
|
||||
type = models.CharField(_('type'), max_length=10, choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES)
|
||||
@ -157,5 +161,3 @@ class Slot(models.Model):
|
||||
def __str__(self):
|
||||
return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % (self.user, self.start_date, self.get_type_display(),
|
||||
self.machine.name, self.token)
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from launderette.views import *
|
||||
|
||||
|
@ -26,11 +26,8 @@ from datetime import datetime, timedelta
|
||||
from collections import OrderedDict
|
||||
import pytz
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, BaseFormView
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils import dateparse, timezone
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
@ -38,7 +35,6 @@ from django.conf import settings
|
||||
from django.db import transaction, DataError
|
||||
from django import forms
|
||||
from django.template import defaultfilters
|
||||
from django.utils import formats
|
||||
|
||||
from core.models import Page, User
|
||||
from club.models import Club
|
||||
@ -49,6 +45,7 @@ from counter.views import GetUserForm
|
||||
|
||||
# For users
|
||||
|
||||
|
||||
class LaunderetteMainView(TemplateView):
|
||||
"""Main presentation view"""
|
||||
template_name = 'launderette/launderette_main.jinja'
|
||||
@ -59,11 +56,13 @@ class LaunderetteMainView(TemplateView):
|
||||
kwargs['page'] = Page.objects.filter(name='launderette').first()
|
||||
return kwargs
|
||||
|
||||
|
||||
class LaunderetteBookMainView(CanViewMixin, ListView):
|
||||
"""Choose which launderette to book"""
|
||||
model = Launderette
|
||||
template_name = 'launderette/launderette_book_choose.jinja'
|
||||
|
||||
|
||||
class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
"""Display the launderette schedule"""
|
||||
model = Launderette
|
||||
@ -100,7 +99,8 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
return super(LaunderetteBookView, self).get(request, *args, **kwargs)
|
||||
|
||||
def check_slot(self, type, date=None):
|
||||
if date is None: date = self.date
|
||||
if date is None:
|
||||
date = self.date
|
||||
for m in self.object.machines.filter(is_working=True, type=type).all():
|
||||
slot = Slot.objects.filter(start_date=date, machine=m).first()
|
||||
if slot is None:
|
||||
@ -121,9 +121,9 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
kwargs['planning'] = OrderedDict()
|
||||
kwargs['slot_type'] = self.slot_type
|
||||
start_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=pytz.UTC)
|
||||
for date in LaunderetteBookView.date_iterator(start_date, start_date+timedelta(days=6), timedelta(days=1)):
|
||||
for date in LaunderetteBookView.date_iterator(start_date, start_date + timedelta(days=6), timedelta(days=1)):
|
||||
kwargs['planning'][date] = []
|
||||
for h in LaunderetteBookView.date_iterator(date, date+timedelta(days=1), timedelta(hours=1)):
|
||||
for h in LaunderetteBookView.date_iterator(date, date + timedelta(days=1), timedelta(hours=1)):
|
||||
free = False
|
||||
if self.slot_type == "BOTH" and self.check_slot("WASHING", h) and self.check_slot("DRYING", h + timedelta(hours=1)):
|
||||
free = True
|
||||
@ -137,6 +137,7 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
kwargs['planning'][date].append(None)
|
||||
return kwargs
|
||||
|
||||
|
||||
class SlotDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""Delete a slot"""
|
||||
model = Slot
|
||||
@ -154,6 +155,7 @@ class LaunderetteListView(CanEditPropMixin, ListView):
|
||||
model = Launderette
|
||||
template_name = 'launderette/launderette_list.jinja'
|
||||
|
||||
|
||||
class LaunderetteEditView(CanEditPropMixin, UpdateView):
|
||||
"""Edit a launderette"""
|
||||
model = Launderette
|
||||
@ -161,6 +163,7 @@ class LaunderetteEditView(CanEditPropMixin, UpdateView):
|
||||
fields = ['name']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class LaunderetteCreateView(CanCreateMixin, CreateView):
|
||||
"""Create a new launderette"""
|
||||
model = Launderette
|
||||
@ -174,6 +177,7 @@ class LaunderetteCreateView(CanCreateMixin, CreateView):
|
||||
form.instance.counter = c
|
||||
return super(LaunderetteCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class ManageTokenForm(forms.Form):
|
||||
action = forms.ChoiceField(choices=[("BACK", _("Back")), ("ADD", _("Add")), ("DEL", _("Delete"))], initial="BACK",
|
||||
label=_("Action"), widget=forms.RadioSelect)
|
||||
@ -210,6 +214,7 @@ class ManageTokenForm(forms.Form):
|
||||
except:
|
||||
self.add_error(None, _("Token %(token_name)s does not exists") % {'token_name': t})
|
||||
|
||||
|
||||
class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
||||
"""The admin page of the launderette"""
|
||||
model = Launderette
|
||||
@ -253,6 +258,7 @@ class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('launderette:launderette_admin', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
class GetLaunderetteUserForm(GetUserForm):
|
||||
def clean(self):
|
||||
cleaned_data = super(GetLaunderetteUserForm, self).clean()
|
||||
@ -261,6 +267,7 @@ class GetLaunderetteUserForm(GetUserForm):
|
||||
raise forms.ValidationError(_("User has booked no slot"))
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
||||
"""The click page of the launderette"""
|
||||
model = Launderette
|
||||
@ -301,6 +308,7 @@ class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('launderette:click', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
class ClickTokenForm(forms.BaseForm):
|
||||
def clean(self):
|
||||
with transaction.atomic():
|
||||
@ -313,7 +321,7 @@ class ClickTokenForm(forms.BaseForm):
|
||||
'last_customer': customer.user.get_display_name(),
|
||||
}
|
||||
total = 0
|
||||
for k,t in self.cleaned_data.items():
|
||||
for k, t in self.cleaned_data.items():
|
||||
if t is not None:
|
||||
slot_id = int(k[5:])
|
||||
slot = Slot.objects.filter(id=slot_id).first()
|
||||
@ -323,15 +331,16 @@ class ClickTokenForm(forms.BaseForm):
|
||||
t.borrow_date = datetime.now().replace(tzinfo=pytz.UTC)
|
||||
t.save()
|
||||
price = settings.SITH_LAUNDERETTE_PRICES[t.type]
|
||||
s = Selling(label="Jeton "+t.get_type_display()+" N°"+t.name, club=counter.club, product=None, counter=counter, unit_price=price,
|
||||
s = Selling(label="Jeton " + t.get_type_display() + " N°" + t.name, club=counter.club, product=None, counter=counter, unit_price=price,
|
||||
quantity=1, seller=operator, customer=customer)
|
||||
s.save()
|
||||
total += price
|
||||
self.last_basket['last_basket'].append("Jeton "+t.get_type_display()+" N°"+t.name)
|
||||
self.last_basket['last_basket'].append("Jeton " + t.get_type_display() + " N°" + t.name)
|
||||
self.last_basket['new_customer_amount'] = str(customer.amount)
|
||||
self.last_basket['last_total'] = str(total)
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
||||
"""The click page of the launderette"""
|
||||
model = Launderette
|
||||
@ -356,7 +365,7 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
||||
fields[field_name] = forms.CharField(max_length=5, required=False,
|
||||
label="%s - %s" % (s.get_type_display(), defaultfilters.date(s.start_date, "j N Y H:i")))
|
||||
# XXX l10n settings.DATETIME_FORMAT didn't work here :/
|
||||
kwargs["clean_"+field_name] = clean_field_factory(field_name, s)
|
||||
kwargs["clean_" + field_name] = clean_field_factory(field_name, s)
|
||||
kwargs['subscriber_id'] = self.subscriber.id
|
||||
kwargs['counter_id'] = self.object.counter.id
|
||||
kwargs['operator_id'] = self.operator.id
|
||||
@ -401,7 +410,6 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
||||
return reverse_lazy('launderette:main_click', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
|
||||
class MachineEditView(CanEditPropMixin, UpdateView):
|
||||
"""Edit a machine"""
|
||||
model = Machine
|
||||
@ -409,6 +417,7 @@ class MachineEditView(CanEditPropMixin, UpdateView):
|
||||
fields = ['name', 'launderette', 'type', 'is_working']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class MachineDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""Edit a machine"""
|
||||
model = Machine
|
||||
@ -416,6 +425,7 @@ class MachineDeleteView(CanEditPropMixin, DeleteView):
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('launderette:launderette_list')
|
||||
|
||||
|
||||
class MachineCreateView(CanCreateMixin, CreateView):
|
||||
"""Create a new machine"""
|
||||
model = Machine
|
||||
@ -429,6 +439,3 @@ class MachineCreateView(CanCreateMixin, CreateView):
|
||||
if obj is not None:
|
||||
ret['launderette'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
|
@ -30,5 +30,3 @@ from sas.models import *
|
||||
admin.site.register(Album)
|
||||
# admin.site.register(Picture)
|
||||
admin.site.register(PeoplePictureRelation)
|
||||
|
||||
|
||||
|
@ -23,11 +23,9 @@
|
||||
#
|
||||
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
@ -36,6 +34,7 @@ import os
|
||||
from core.models import SithFile, User
|
||||
from core.utils import resize_image, exif_auto_rotate
|
||||
|
||||
|
||||
class Picture(SithFile):
|
||||
class Meta:
|
||||
proxy = True
|
||||
@ -50,12 +49,12 @@ class Picture(SithFile):
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
# file = SithFile.objects.filter(id=self.id).first()
|
||||
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID)# or user.can_edit(file)
|
||||
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) # or user.can_edit(file)
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
# file = SithFile.objects.filter(id=self.id).first()
|
||||
return self.can_be_edited_by(user) or (self.is_in_sas and self.is_moderated and
|
||||
user.was_subscribed)# or user.can_view(file)
|
||||
user.was_subscribed) # or user.can_view(file)
|
||||
|
||||
def get_download_url(self):
|
||||
return reverse('sas:download', kwargs={'picture_id': self.id})
|
||||
@ -73,7 +72,8 @@ class Picture(SithFile):
|
||||
im = Image.open(BytesIO(self.file.read()))
|
||||
try:
|
||||
im = exif_auto_rotate(im)
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
file = resize_image(im, max(im.size), self.mime_type.split('/')[-1])
|
||||
thumb = resize_image(im, 200, self.mime_type.split('/')[-1])
|
||||
compressed = resize_image(im, 1200, self.mime_type.split('/')[-1])
|
||||
@ -113,6 +113,7 @@ class Picture(SithFile):
|
||||
else:
|
||||
return Picture.objects.filter(id__lt=self.id, is_moderated=False, is_in_sas=True).order_by('-id').first()
|
||||
|
||||
|
||||
class Album(SithFile):
|
||||
class Meta:
|
||||
proxy = True
|
||||
@ -127,12 +128,12 @@ class Album(SithFile):
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
# file = SithFile.objects.filter(id=self.id).first()
|
||||
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID)# or user.can_edit(file)
|
||||
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) # or user.can_edit(file)
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
# file = SithFile.objects.filter(id=self.id).first()
|
||||
return self.can_be_edited_by(user) or (self.is_in_sas and self.is_moderated and
|
||||
user.was_subscribed)# or user.can_view(file)
|
||||
user.was_subscribed) # or user.can_view(file)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('sas:album', kwargs={'album_id': self.id})
|
||||
@ -148,6 +149,7 @@ class Album(SithFile):
|
||||
self.file.name = self.name + '/thumb.jpg'
|
||||
self.save()
|
||||
|
||||
|
||||
class PeoplePictureRelation(models.Model):
|
||||
"""
|
||||
The PeoplePictureRelation class makes the connection between User and Picture
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from sas.views import *
|
||||
|
||||
@ -40,4 +40,3 @@ urlpatterns = [
|
||||
# url(r'^album/new$', AlbumCreateView.as_view(), name='album_new'),
|
||||
# url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
|
||||
]
|
||||
|
||||
|
57
sas/views.py
57
sas/views.py
@ -22,31 +22,27 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.http import HttpResponse
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, ProcessFormView, FormMixin, FormView
|
||||
from core.views.forms import SelectDate
|
||||
from django.views.generic import DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, FormMixin, FormView
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.forms.models import modelform_factory
|
||||
from django import forms
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||
from ajax_select import make_ajax_field
|
||||
from ajax_select.fields import AutoCompleteSelectMultipleField
|
||||
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
|
||||
from core.views import CanViewMixin, CanEditMixin
|
||||
from core.views.files import send_file, FileView
|
||||
from core.models import SithFile, User, Notification, RealGroup
|
||||
|
||||
from sas.models import Picture, Album, PeoplePictureRelation
|
||||
|
||||
|
||||
class SASForm(forms.Form):
|
||||
album_name = forms.CharField(label=_("Add a new album"), max_length=30, required=False)
|
||||
images = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Upload images"),
|
||||
@ -80,6 +76,7 @@ class SASForm(forms.Form):
|
||||
if not u.notifications.filter(type="SAS_MODERATION", viewed=False).exists():
|
||||
Notification(user=u, url=reverse("sas:moderation"), type="SAS_MODERATION").save()
|
||||
|
||||
|
||||
class RelationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = PeoplePictureRelation
|
||||
@ -87,6 +84,7 @@ class RelationForm(forms.ModelForm):
|
||||
widgets = {'picture': forms.HiddenInput}
|
||||
users = AutoCompleteSelectMultipleField('users', show_help_text=False, help_text="", label=_("Add user"), required=False)
|
||||
|
||||
|
||||
class SASMainView(FormView):
|
||||
form_class = SASForm
|
||||
template_name = "sas/main.jinja"
|
||||
@ -112,6 +110,7 @@ class SASMainView(FormView):
|
||||
kwargs['latest'] = SithFile.objects.filter(is_in_sas=True, is_folder=True, is_moderated=True).order_by('-id')[:5]
|
||||
return kwargs
|
||||
|
||||
|
||||
class PictureView(CanViewMixin, DetailView, FormMixin):
|
||||
model = Picture
|
||||
form_class = RelationForm
|
||||
@ -132,8 +131,9 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
|
||||
try:
|
||||
user = User.objects.filter(id=int(request.GET['remove_user'])).first()
|
||||
if user.id == request.user.id or request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID):
|
||||
r = PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
|
||||
except: pass
|
||||
PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
|
||||
except:
|
||||
pass
|
||||
if 'ask_removal' in request.GET.keys():
|
||||
self.object.is_moderated = False
|
||||
self.object.asked_for_removal = True
|
||||
@ -165,15 +165,19 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
|
||||
def get_success_url(self):
|
||||
return reverse('sas:picture', kwargs={'picture_id': self.object.id})
|
||||
|
||||
|
||||
def send_pict(request, picture_id):
|
||||
return send_file(request, picture_id, Picture)
|
||||
|
||||
|
||||
def send_compressed(request, picture_id):
|
||||
return send_file(request, picture_id, Picture, "compressed")
|
||||
|
||||
|
||||
def send_thumb(request, picture_id):
|
||||
return send_file(request, picture_id, Picture, "thumbnail")
|
||||
|
||||
|
||||
class AlbumUploadView(CanViewMixin, DetailView, FormMixin):
|
||||
model = Album
|
||||
form_class = SASForm
|
||||
@ -239,6 +243,7 @@ class AlbumView(CanViewMixin, DetailView, FormMixin):
|
||||
|
||||
# Admin views
|
||||
|
||||
|
||||
class ModerationView(TemplateView):
|
||||
template_name = "sas/moderation.jinja"
|
||||
|
||||
@ -257,7 +262,8 @@ class ModerationView(TemplateView):
|
||||
a.save()
|
||||
elif 'delete' in request.POST.keys():
|
||||
a.delete()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(ModerationView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@ -268,12 +274,14 @@ class ModerationView(TemplateView):
|
||||
kwargs['albums'] = Album.objects.filter(id__in=kwargs['pictures'].values('parent').distinct('parent'))
|
||||
return kwargs
|
||||
|
||||
|
||||
class PictureEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Picture
|
||||
fields=['name', 'parent']
|
||||
fields = ['name', 'parent']
|
||||
parent = make_ajax_field(Picture, 'parent', 'files', help_text="")
|
||||
|
||||
|
||||
class AlbumEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Album
|
||||
@ -283,16 +291,18 @@ class AlbumEditForm(forms.ModelForm):
|
||||
edit_groups = make_ajax_field(Album, 'edit_groups', 'groups', help_text="")
|
||||
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
||||
|
||||
|
||||
class PictureEditView(CanEditMixin, UpdateView):
|
||||
model=Picture
|
||||
form_class=PictureEditForm
|
||||
template_name='core/edit.jinja'
|
||||
model = Picture
|
||||
form_class = PictureEditForm
|
||||
template_name = 'core/edit.jinja'
|
||||
pk_url_kwarg = "picture_id"
|
||||
|
||||
|
||||
class AlbumEditView(CanEditMixin, UpdateView):
|
||||
model=Album
|
||||
form_class=AlbumEditForm
|
||||
template_name='core/edit.jinja'
|
||||
model = Album
|
||||
form_class = AlbumEditForm
|
||||
template_name = 'core/edit.jinja'
|
||||
pk_url_kwarg = "album_id"
|
||||
|
||||
def form_valid(self, form):
|
||||
@ -300,4 +310,3 @@ class AlbumEditView(CanEditMixin, UpdateView):
|
||||
if form.cleaned_data['recursive']:
|
||||
self.object.apply_rights_recursively(True)
|
||||
return ret
|
||||
|
||||
|
@ -180,7 +180,7 @@ HAYSTACK_CONNECTIONS = {
|
||||
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||
'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
HAYSTACK_SIGNAL_PROCESSOR = 'core.search_indexes.UserOnlySignalProcessor'
|
||||
|
||||
@ -208,7 +208,7 @@ LANGUAGE_CODE = 'fr-FR'
|
||||
LANGUAGES = [
|
||||
('en', _('English')),
|
||||
('fr', _('French')),
|
||||
]
|
||||
]
|
||||
|
||||
TIME_ZONE = 'Europe/Paris'
|
||||
|
||||
@ -247,13 +247,13 @@ AUTH_ANONYMOUS_MODEL = 'core.models.AnonymousUser'
|
||||
LOGIN_URL = '/login'
|
||||
LOGOUT_URL = '/logout'
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
DEFAULT_FROM_EMAIL="bibou@git.an"
|
||||
SITH_COM_EMAIL="bibou_com@git.an"
|
||||
DEFAULT_FROM_EMAIL = "bibou@git.an"
|
||||
SITH_COM_EMAIL = "bibou_com@git.an"
|
||||
|
||||
# Email
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
EMAIL_HOST="localhost"
|
||||
EMAIL_PORT=25
|
||||
EMAIL_HOST = "localhost"
|
||||
EMAIL_PORT = 25
|
||||
|
||||
# Below this line, only Sith-specific variables are defined
|
||||
|
||||
@ -265,7 +265,7 @@ OLD_MYSQL_INFOS = {
|
||||
'db': "ae2-db",
|
||||
'charset': 'utf8',
|
||||
'use_unicode': True,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SITH_URL = "my.url.git.an"
|
||||
@ -277,21 +277,21 @@ SITH_MAIN_CLUB = {
|
||||
'name': "AE",
|
||||
'unix_name': "ae",
|
||||
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
||||
}
|
||||
}
|
||||
|
||||
# Bar managers
|
||||
SITH_BAR_MANAGER = {
|
||||
'name': "BdF",
|
||||
'unix_name': "bdf",
|
||||
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
||||
}
|
||||
}
|
||||
|
||||
# Launderette managers
|
||||
SITH_LAUNDERETTE_MANAGER = {
|
||||
'name': "Laverie",
|
||||
'unix_name': "laverie",
|
||||
'address': "6 Boulevard Anatole France, 90000 Belfort"
|
||||
}
|
||||
}
|
||||
|
||||
# Define the date in the year serving as reference for the subscriptions calendar
|
||||
# (month, day)
|
||||
@ -328,11 +328,11 @@ SITH_FORUM_PAGE_LENGTH = 30
|
||||
# SAS variables
|
||||
SITH_SAS_ROOT_DIR_ID = 4
|
||||
|
||||
SITH_BOARD_SUFFIX="-bureau"
|
||||
SITH_MEMBER_SUFFIX="-membres"
|
||||
SITH_BOARD_SUFFIX = "-bureau"
|
||||
SITH_MEMBER_SUFFIX = "-membres"
|
||||
|
||||
SITH_MAIN_BOARD_GROUP=SITH_MAIN_CLUB['unix_name']+SITH_BOARD_SUFFIX
|
||||
SITH_MAIN_MEMBERS_GROUP=SITH_MAIN_CLUB['unix_name']+SITH_MEMBER_SUFFIX
|
||||
SITH_MAIN_BOARD_GROUP = SITH_MAIN_CLUB['unix_name'] + SITH_BOARD_SUFFIX
|
||||
SITH_MAIN_MEMBERS_GROUP = SITH_MAIN_CLUB['unix_name'] + SITH_MEMBER_SUFFIX
|
||||
|
||||
SITH_PROFILE_DEPARTMENTS = [
|
||||
("TC", _("TC")),
|
||||
@ -348,14 +348,14 @@ SITH_PROFILE_DEPARTMENTS = [
|
||||
("EDIM", _("EDIM")),
|
||||
("HUMA", _("Humanities")),
|
||||
("NA", _("N/A")),
|
||||
]
|
||||
]
|
||||
|
||||
SITH_ACCOUNTING_PAYMENT_METHOD = [
|
||||
('CHECK', _('Check')),
|
||||
('CASH', _('Cash')),
|
||||
('TRANSFERT', _('Transfert')),
|
||||
('CARD', _('Credit card')),
|
||||
]
|
||||
]
|
||||
|
||||
SITH_SUBSCRIPTION_PAYMENT_METHOD = [
|
||||
('CHECK', _('Check')),
|
||||
@ -363,26 +363,26 @@ SITH_SUBSCRIPTION_PAYMENT_METHOD = [
|
||||
('CASH', _('Cash')),
|
||||
('EBOUTIC', _('Eboutic')),
|
||||
('OTHER', _('Other')),
|
||||
]
|
||||
]
|
||||
|
||||
SITH_SUBSCRIPTION_LOCATIONS = [
|
||||
('BELFORT', _('Belfort')),
|
||||
('SEVENANS', _('Sevenans')),
|
||||
('MONTBELIARD', _('Montbéliard')),
|
||||
('EBOUTIC', _('Eboutic')),
|
||||
]
|
||||
]
|
||||
|
||||
SITH_COUNTER_BARS = [
|
||||
(1, "MDE"),
|
||||
(2, "Foyer"),
|
||||
(35, "La Gommette"),
|
||||
]
|
||||
]
|
||||
|
||||
SITH_COUNTER_PAYMENT_METHOD = [
|
||||
('CHECK', _('Check')),
|
||||
('CASH', _('Cash')),
|
||||
('CARD', _('Credit card')),
|
||||
]
|
||||
]
|
||||
|
||||
SITH_COUNTER_BANK = [
|
||||
('OTHER', 'Autre'),
|
||||
@ -395,7 +395,7 @@ SITH_COUNTER_BANK = [
|
||||
('CREDIT-MUTUEL', 'Credit Mutuel'),
|
||||
('CREDIT-LYONNAIS', 'Credit Lyonnais'),
|
||||
('LA-POSTE', 'La Poste'),
|
||||
]
|
||||
]
|
||||
|
||||
# Defines pagination for cash summary
|
||||
SITH_COUNTER_CASH_SUMMARY_LENGTH = 50
|
||||
@ -466,7 +466,7 @@ SITH_SUBSCRIPTIONS = {
|
||||
'price': 15,
|
||||
'duration': 2,
|
||||
},
|
||||
# To be completed....
|
||||
# To be completed....
|
||||
}
|
||||
|
||||
SITH_CLUB_ROLES = {}
|
||||
@ -497,16 +497,16 @@ SITH_CLUB_ROLES = {
|
||||
|
||||
# This corresponds to the maximum role a user can freely subscribe to
|
||||
# In this case, SITH_MAXIMUM_FREE_ROLE=1 means that a user can set himself as "Membre actif" or "Curieux", but not higher
|
||||
SITH_MAXIMUM_FREE_ROLE=1
|
||||
SITH_MAXIMUM_FREE_ROLE = 1
|
||||
|
||||
# Minutes to timeout the logged barmen
|
||||
SITH_BARMAN_TIMEOUT=20
|
||||
SITH_BARMAN_TIMEOUT = 20
|
||||
|
||||
# Minutes to delete the last operations
|
||||
SITH_LAST_OPERATIONS_LIMIT=10
|
||||
SITH_LAST_OPERATIONS_LIMIT = 10
|
||||
|
||||
# Minutes for a counter to be inactive
|
||||
SITH_COUNTER_MINUTE_INACTIVE=10
|
||||
SITH_COUNTER_MINUTE_INACTIVE = 10
|
||||
|
||||
# ET variables
|
||||
SITH_EBOUTIC_ET_URL = "https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi"
|
||||
@ -523,7 +523,7 @@ SITH_LAUNDERETTE_MACHINE_TYPES = [('WASHING', _('Washing')), ('DRYING', _('Dryin
|
||||
SITH_LAUNDERETTE_PRICES = {
|
||||
'WASHING': 1.0,
|
||||
'DRYING': 0.75,
|
||||
}
|
||||
}
|
||||
|
||||
SITH_NOTIFICATIONS = [
|
||||
('NEWS_MODERATION', _("A fresh new to be moderated")),
|
||||
@ -533,7 +533,7 @@ SITH_NOTIFICATIONS = [
|
||||
('REFILLING', _("You just refilled of %s €")),
|
||||
('SELLING', _("You just bought %s")),
|
||||
('GENERIC', _("You have a notification")),
|
||||
]
|
||||
]
|
||||
|
||||
SITH_QUICK_NOTIF = {
|
||||
'qn_success': _("Success!"),
|
||||
@ -541,7 +541,7 @@ SITH_QUICK_NOTIF = {
|
||||
'qn_weekmail_new_article': _("You successfully posted an article in the Weekmail"),
|
||||
'qn_weekmail_article_edit': _("You successfully edited an article in the Weekmail"),
|
||||
'qn_weekmail_send_success': _("You successfully sent the Weekmail"),
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
from .settings_custom import *
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
from debug_toolbar.panels.templates import TemplatesPanel as BaseTemplatesPanel
|
||||
|
||||
|
||||
class TemplatesPanel(BaseTemplatesPanel):
|
||||
def generate_stats(self, *args):
|
||||
template = self.templates[0]['template']
|
||||
|
@ -80,4 +80,3 @@ if settings.DEBUG:
|
||||
urlpatterns += [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
]
|
||||
|
||||
|
@ -26,6 +26,4 @@ from django.contrib import admin
|
||||
|
||||
from subscription.models import Subscription
|
||||
|
||||
|
||||
|
||||
admin.site.register(Subscription)
|
||||
|
@ -35,20 +35,21 @@ from core.models import User
|
||||
from core.utils import get_start_of_semester
|
||||
|
||||
|
||||
|
||||
def validate_type(value):
|
||||
if value not in settings.SITH_SUBSCRIPTIONS.keys():
|
||||
raise ValidationError(_('Bad subscription type'))
|
||||
|
||||
|
||||
def validate_payment(value):
|
||||
if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD:
|
||||
raise ValidationError(_('Bad payment method'))
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
member = models.ForeignKey(User, related_name='subscriptions')
|
||||
subscription_type = models.CharField(_('subscription type'),
|
||||
max_length=255,
|
||||
choices=((k, v['name']) for k,v in sorted(settings.SITH_SUBSCRIPTIONS.items())))
|
||||
choices=((k, v['name']) for k, v in sorted(settings.SITH_SUBSCRIPTIONS.items())))
|
||||
subscription_start = models.DateField(_('subscription start'))
|
||||
subscription_end = models.DateField(_('subscription end'))
|
||||
payment_method = models.CharField(_('payment method'),
|
||||
@ -58,7 +59,7 @@ class Subscription(models.Model):
|
||||
max_length=20, verbose_name=_('location'))
|
||||
|
||||
class Meta:
|
||||
ordering = ['subscription_start',]
|
||||
ordering = ['subscription_start', ]
|
||||
|
||||
def clean(self):
|
||||
try:
|
||||
@ -75,7 +76,7 @@ class Subscription(models.Model):
|
||||
from counter.models import Customer
|
||||
if not Customer.objects.filter(user=self.member).exists():
|
||||
last_id = Customer.objects.count() + 1504 # Number to keep a continuity with the old site
|
||||
Customer(user=self.member, account_id=Customer.generate_account_id(last_id+1), amount=0).save()
|
||||
Customer(user=self.member, account_id=Customer.generate_account_id(last_id + 1), amount=0).save()
|
||||
form = PasswordResetForm({'email': self.member.email})
|
||||
if form.is_valid():
|
||||
form.save(use_https=True, email_template_name='core/new_user_email.jinja',
|
||||
@ -91,25 +92,25 @@ class Subscription(models.Model):
|
||||
"EBOUTIC": 5,
|
||||
}
|
||||
TYPE = {
|
||||
'un-semestre' : 0,
|
||||
'deux-semestres' : 1,
|
||||
'cursus-tronc-commun' : 2,
|
||||
'cursus-branche' : 3,
|
||||
'membre-honoraire' : 4,
|
||||
'assidu' : 5,
|
||||
'amicale/doceo' : 6,
|
||||
'reseau-ut' : 7,
|
||||
'crous' : 8,
|
||||
'sbarro/esta' : 9,
|
||||
'cursus-alternant' : 10,
|
||||
'un-semestre': 0,
|
||||
'deux-semestres': 1,
|
||||
'cursus-tronc-commun': 2,
|
||||
'cursus-branche': 3,
|
||||
'membre-honoraire': 4,
|
||||
'assidu': 5,
|
||||
'amicale/doceo': 6,
|
||||
'reseau-ut': 7,
|
||||
'crous': 8,
|
||||
'sbarro/esta': 9,
|
||||
'cursus-alternant': 10,
|
||||
}
|
||||
PAYMENT = {
|
||||
"CHECK" : 1,
|
||||
"CARD" : 2,
|
||||
"CASH" : 3,
|
||||
"OTHER" : 4,
|
||||
"EBOUTIC" : 5,
|
||||
"OTHER" : 0,
|
||||
"CHECK": 1,
|
||||
"CARD": 2,
|
||||
"CASH": 3,
|
||||
"OTHER": 4,
|
||||
"EBOUTIC": 5,
|
||||
"OTHER": 0,
|
||||
}
|
||||
|
||||
db = MySQLdb.connect(**settings.OLD_MYSQL_INFOS)
|
||||
@ -120,7 +121,7 @@ class Subscription(models.Model):
|
||||
LOCATION[self.location]))
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
with open(settings.BASE_DIR+"/subscription_fail.log", "a") as f:
|
||||
with open(settings.BASE_DIR + "/subscription_fail.log", "a") as f:
|
||||
print("FAIL to add subscription to %s to old site" % (self.member), file=f)
|
||||
print("Reason: %s" % (repr(e)), file=f)
|
||||
db.rollback()
|
||||
@ -130,10 +131,9 @@ class Subscription(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
if hasattr(self, "member") and self.member is not None:
|
||||
return self.member.username+' - '+str(self.pk)
|
||||
return self.member.username + ' - ' + str(self.pk)
|
||||
else:
|
||||
return 'No user - '+str(self.pk)
|
||||
|
||||
return 'No user - ' + str(self.pk)
|
||||
|
||||
@staticmethod
|
||||
def compute_start(d=date.today(), duration=1):
|
||||
@ -165,12 +165,11 @@ class Subscription(models.Model):
|
||||
start = Subscription.compute_start(duration=duration)
|
||||
# This can certainly be simplified, but it works like this
|
||||
try:
|
||||
return start.replace(month=(start.month-1+6*duration)%12+1,
|
||||
year=start.year+int(duration/2)+(1 if start.month > 6 and duration%2 == 1 else 0))
|
||||
return start.replace(month=(start.month - 1 + 6 * duration) % 12 + 1,
|
||||
year=start.year + int(duration / 2) + (1 if start.month > 6 and duration % 2 == 1 else 0))
|
||||
except ValueError as e:
|
||||
return start.replace(day=1, month=(start.month+6*duration)%12+1,
|
||||
year=start.year+int(duration/2)+(1 if start.month > 6 and duration%2 == 1 else 0))
|
||||
|
||||
return start.replace(day=1, month=(start.month + 6 * duration) % 12 + 1,
|
||||
year=start.year + int(duration / 2) + (1 if start.month > 6 and duration % 2 == 1 else 0))
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
|
||||
@ -178,10 +177,15 @@ class Subscription(models.Model):
|
||||
def is_valid_now(self):
|
||||
return self.subscription_start <= date.today() and date.today() <= self.subscription_end
|
||||
|
||||
|
||||
def guy_test(date, duration=4):
|
||||
print(str(date)+" - "+str(duration)+" -> "+str(Subscription.compute_start(date, duration)))
|
||||
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_start(date, duration)))
|
||||
|
||||
|
||||
def bibou_test(duration, date=date.today()):
|
||||
print(str(date)+" - "+str(duration)+" -> "+str(Subscription.compute_end(duration, Subscription.compute_start(date, duration))))
|
||||
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_end(duration, Subscription.compute_start(date, duration))))
|
||||
|
||||
|
||||
def guy():
|
||||
guy_test(date(2015, 7, 11))
|
||||
guy_test(date(2015, 8, 11))
|
||||
@ -191,7 +195,7 @@ def guy():
|
||||
guy_test(date(2015, 2, 11))
|
||||
guy_test(date(2015, 8, 17))
|
||||
guy_test(date(2015, 9, 17))
|
||||
print('='*80)
|
||||
print('=' * 80)
|
||||
guy_test(date(2015, 7, 11), 1)
|
||||
guy_test(date(2015, 8, 11), 2)
|
||||
guy_test(date(2015, 2, 17), 3)
|
||||
@ -200,7 +204,7 @@ def guy():
|
||||
guy_test(date(2015, 2, 11), 2)
|
||||
guy_test(date(2015, 8, 17), 3)
|
||||
guy_test(date(2015, 9, 17), 4)
|
||||
print('='*80)
|
||||
print('=' * 80)
|
||||
bibou_test(1, date(2015, 2, 18))
|
||||
bibou_test(2, date(2015, 2, 18))
|
||||
bibou_test(3, date(2015, 2, 18))
|
||||
@ -209,7 +213,7 @@ def guy():
|
||||
bibou_test(2, date(2015, 9, 18))
|
||||
bibou_test(3, date(2015, 9, 18))
|
||||
bibou_test(4, date(2015, 9, 18))
|
||||
print('='*80)
|
||||
print('=' * 80)
|
||||
bibou_test(1, date(2000, 2, 29))
|
||||
bibou_test(2, date(2000, 2, 29))
|
||||
bibou_test(1, date(2000, 5, 31))
|
||||
@ -219,5 +223,6 @@ def guy():
|
||||
bibou_test(3)
|
||||
bibou_test(4)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
guy()
|
||||
|
@ -31,6 +31,3 @@ urlpatterns = [
|
||||
url(r'^$', NewSubscription.as_view(), name='subscription'),
|
||||
url(r'stats', SubscriptionsStatsView.as_view(), name='stats'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
@ -26,16 +26,13 @@ from django.views.generic.edit import CreateView, FormView
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.db import IntegrityError
|
||||
from django import forms
|
||||
from django.forms import Select
|
||||
from django.conf import settings
|
||||
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
import random
|
||||
|
||||
from subscription.models import Subscription
|
||||
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
|
||||
from core.views.forms import SelectDateTime
|
||||
from core.models import User
|
||||
|
||||
@ -85,9 +82,9 @@ class SubscriptionForm(forms.ModelForm):
|
||||
if User.objects.filter(email=cleaned_data.get("email")).first() is not None:
|
||||
self.add_error("email", ValidationError(_("A user with that email address already exists")))
|
||||
else:
|
||||
u = User(last_name = self.cleaned_data.get("last_name"),
|
||||
first_name = self.cleaned_data.get("first_name"),
|
||||
email = self.cleaned_data.get("email"))
|
||||
u = User(last_name=self.cleaned_data.get("last_name"),
|
||||
first_name=self.cleaned_data.get("first_name"),
|
||||
email=self.cleaned_data.get("email"))
|
||||
u.generate_username()
|
||||
u.set_password(str(random.randrange(1000000, 10000000)))
|
||||
u.save()
|
||||
@ -102,6 +99,7 @@ class SubscriptionForm(forms.ModelForm):
|
||||
raise ValidationError(_("You must either choose an existing user or create a new one properly"))
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class NewSubscription(CreateView):
|
||||
template_name = 'subscription/subscription.jinja'
|
||||
form_class = SubscriptionForm
|
||||
|
@ -34,15 +34,18 @@ from core.models import User
|
||||
from core.utils import get_start_of_semester, get_semester
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class TrombiManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(TrombiManager, self).get_queryset()
|
||||
|
||||
|
||||
class AvailableTrombiManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(AvailableTrombiManager,
|
||||
self).get_queryset().filter(subscription_deadline__gte=date.today())
|
||||
|
||||
|
||||
class Trombi(models.Model):
|
||||
"""
|
||||
This is the main class, the Trombi itself.
|
||||
@ -81,6 +84,7 @@ class Trombi(models.Model):
|
||||
def can_be_viewed_by(self, user):
|
||||
return user.id in [u.user.id for u in self.users.all()]
|
||||
|
||||
|
||||
class TrombiUser(models.Model):
|
||||
"""
|
||||
This class is only here to avoid cross references between the core, club,
|
||||
@ -120,6 +124,7 @@ class TrombiUser(models.Model):
|
||||
end=end_date,
|
||||
).save()
|
||||
|
||||
|
||||
class TrombiComment(models.Model):
|
||||
"""
|
||||
This represent a comment given by someone to someone else in the same Trombi
|
||||
@ -135,6 +140,7 @@ class TrombiComment(models.Model):
|
||||
return False
|
||||
return user.id == self.author.user.id or user.can_edit(self.author.trombi)
|
||||
|
||||
|
||||
class TrombiClubMembership(models.Model):
|
||||
"""
|
||||
This represent a membership to a club
|
||||
@ -156,4 +162,3 @@ class TrombiClubMembership(models.Model):
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('trombi:profile')
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from trombi.views import *
|
||||
|
||||
@ -43,4 +43,3 @@ urlpatterns = [
|
||||
url(r'^membership/(?P<membership_id>[0-9]+)/edit$', UserTrombiEditMembershipView.as_view(), name='edit_membership'),
|
||||
url(r'^membership/(?P<membership_id>[0-9]+)/delete$', UserTrombiDeleteMembershipView.as_view(), name='delete_membership'),
|
||||
]
|
||||
|
||||
|
@ -23,10 +23,10 @@
|
||||
#
|
||||
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView, SingleObjectMixin
|
||||
from django.views.generic import DetailView, RedirectView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
@ -35,11 +35,12 @@ from django.forms.models import modelform_factory
|
||||
from datetime import date
|
||||
|
||||
from trombi.models import Trombi, TrombiUser, TrombiComment, TrombiClubMembership
|
||||
from core.views.forms import SelectFile, SelectDate
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
|
||||
from core.views.forms import SelectDate
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
|
||||
from core.models import User
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class TrombiTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
return _("Trombi")
|
||||
@ -69,9 +70,11 @@ class TrombiTabsMixin(TabedViewMixin):
|
||||
'slug': 'admin_tools',
|
||||
'name': _("Admin tools"),
|
||||
})
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return tab_list
|
||||
|
||||
|
||||
class TrombiForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Trombi
|
||||
@ -81,6 +84,7 @@ class TrombiForm(forms.ModelForm):
|
||||
'comments_deadline': SelectDate,
|
||||
}
|
||||
|
||||
|
||||
class TrombiCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a trombi for a club
|
||||
@ -102,6 +106,7 @@ class TrombiCreateView(CanCreateMixin, CreateView):
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
|
||||
model = Trombi
|
||||
form_class = TrombiForm
|
||||
@ -110,7 +115,8 @@ class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
|
||||
current_tab = "admin_tools"
|
||||
|
||||
def get_success_url(self):
|
||||
return super(TrombiEditView, self).get_success_url()+"?qn_success"
|
||||
return super(TrombiEditView, self).get_success_url() + "?qn_success"
|
||||
|
||||
|
||||
class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
||||
model = Trombi
|
||||
@ -118,6 +124,7 @@ class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailVie
|
||||
pk_url_kwarg = 'trombi_id'
|
||||
current_tab = "admin_tools"
|
||||
|
||||
|
||||
class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
|
||||
model = TrombiUser
|
||||
pk_url_kwarg = 'user_id'
|
||||
@ -127,6 +134,7 @@ class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:detail', kwargs={'trombi_id': self.object.trombi.id}) + "?qn_success"
|
||||
|
||||
|
||||
class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
||||
model = Trombi
|
||||
template_name = 'trombi/comment_moderation.jinja'
|
||||
@ -139,10 +147,12 @@ class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMi
|
||||
author__trombi__id=self.object.id).exclude(target__user__id=self.request.user.id)
|
||||
return kwargs
|
||||
|
||||
|
||||
class TrombiModerateForm(forms.Form):
|
||||
reason = forms.CharField(help_text=_("Explain why you rejected the comment"))
|
||||
action = forms.CharField(initial="delete", widget=forms.widgets.HiddenInput)
|
||||
|
||||
|
||||
class TrombiModerateCommentView(DetailView):
|
||||
model = TrombiComment
|
||||
template_name = 'core/edit.jinja'
|
||||
@ -159,7 +169,7 @@ class TrombiModerateCommentView(DetailView):
|
||||
if request.POST['action'] == "accept":
|
||||
self.object.is_moderated = True
|
||||
self.object.save()
|
||||
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id})+"?qn_success")
|
||||
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id}) + "?qn_success")
|
||||
elif request.POST['action'] == "reject":
|
||||
return super(TrombiModerateCommentView, self).get(request, *args, **kwargs)
|
||||
elif request.POST['action'] == "delete" and "reason" in request.POST.keys():
|
||||
@ -176,26 +186,29 @@ class TrombiModerateCommentView(DetailView):
|
||||
},
|
||||
)
|
||||
self.object.delete()
|
||||
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id})+"?qn_success")
|
||||
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id}) + "?qn_success")
|
||||
raise Http404
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(TrombiModerateCommentView, self).get_context_data(**kwargs)
|
||||
kwargs['form'] = TrombiModerateForm()
|
||||
return kwargs
|
||||
|
||||
# User side
|
||||
|
||||
|
||||
class TrombiModelChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self, obj):
|
||||
return _("%(name)s (deadline: %(date)s)") % {'name': str(obj), 'date': str(obj.subscription_deadline)}
|
||||
|
||||
|
||||
class UserTrombiForm(forms.Form):
|
||||
trombi = TrombiModelChoiceField(Trombi.availables.all(), required=False, label=_("Select trombi"),
|
||||
help_text=_("This allows you to subscribe to a Trombi. "
|
||||
"Be aware that you can subscribe only once, so don't play with that, "
|
||||
"or you will expose yourself to the admins' wrath!"))
|
||||
|
||||
|
||||
class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
||||
"""
|
||||
Display a user's trombi tools
|
||||
@ -222,6 +235,7 @@ class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
||||
kwargs['date'] = date
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
|
||||
model = TrombiUser
|
||||
fields = ['profile_pict', 'scrub_pict']
|
||||
@ -232,7 +246,8 @@ class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
|
||||
return self.request.user.trombi_user
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:user_tools')+"?qn_success"
|
||||
return reverse('trombi:user_tools') + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
|
||||
model = User
|
||||
@ -251,7 +266,8 @@ class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
|
||||
return self.request.user
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:user_tools')+"?qn_success"
|
||||
return reverse('trombi:user_tools') + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiResetClubMembershipsView(RedirectView):
|
||||
permanent = False
|
||||
@ -262,7 +278,8 @@ class UserTrombiResetClubMembershipsView(RedirectView):
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:profile')+"?qn_success"
|
||||
return reverse('trombi:profile') + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
|
||||
model = TrombiClubMembership
|
||||
@ -274,6 +291,7 @@ class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
|
||||
def get_success_url(self):
|
||||
return super(UserTrombiDeleteMembershipView, self).get_success_url() + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiEditMembershipView(CanEditMixin, TrombiTabsMixin, UpdateView):
|
||||
model = TrombiClubMembership
|
||||
pk_url_kwarg = "membership_id"
|
||||
@ -300,6 +318,7 @@ class UserTrombiProfileView(TrombiTabsMixin, DetailView):
|
||||
raise Http404()
|
||||
return super(UserTrombiProfileView, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class TrombiCommentFormView():
|
||||
"""
|
||||
Create/edit a trombi comment
|
||||
@ -325,7 +344,7 @@ class TrombiCommentFormView():
|
||||
})
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:user_tools')+"?qn_success"
|
||||
return reverse('trombi:user_tools') + "?qn_success"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(TrombiCommentFormView, self).get_context_data(**kwargs)
|
||||
@ -335,6 +354,7 @@ class TrombiCommentFormView():
|
||||
kwargs['target'] = self.object.target
|
||||
return kwargs
|
||||
|
||||
|
||||
class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
|
||||
def form_valid(self, form):
|
||||
target = get_object_or_404(TrombiUser, id=self.kwargs['user_id'])
|
||||
@ -342,11 +362,10 @@ class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
|
||||
form.instance.target = target
|
||||
return super(TrombiCommentCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class TrombiCommentEditView(TrombiCommentFormView, CanViewMixin, UpdateView):
|
||||
pk_url_kwarg = "comment_id"
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.is_moderated = False
|
||||
return super(TrombiCommentEditView, self).form_valid(form)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user