diff --git a/accounting/admin.py b/accounting/admin.py index 3c3e5f12..12999c6a 100644 --- a/accounting/admin.py +++ b/accounting/admin.py @@ -6,7 +6,9 @@ from accounting.models import * admin.site.register(Customer) admin.site.register(ProductType) admin.site.register(Product) +admin.site.register(BankAccount) +admin.site.register(ClubAccount) admin.site.register(GeneralJournal) -admin.site.register(GenericInvoice) +admin.site.register(Operation) diff --git a/accounting/migrations/0001_initial.py b/accounting/migrations/0001_initial.py index 32694f52..5e1b303a 100644 --- a/accounting/migrations/0001_initial.py +++ b/accounting/migrations/0001_initial.py @@ -2,71 +2,91 @@ from __future__ import unicode_literals from django.db import migrations, models -from django.conf import settings import accounting.models +from django.conf import settings class Migration(migrations.Migration): dependencies = [ + ('club', '__first__'), ('core', '0001_initial'), ] operations = [ + migrations.CreateModel( + name='BankAccount', + fields=[ + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), + ('name', models.CharField(verbose_name='name', max_length=30)), + ('rib', models.CharField(verbose_name='rib', max_length=255)), + ('number', models.CharField(verbose_name='account number', max_length=255)), + ], + ), + migrations.CreateModel( + name='ClubAccount', + fields=[ + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), + ('name', models.CharField(verbose_name='name', max_length=30)), + ('bank_account', models.ForeignKey(related_name='club_accounts', to='accounting.BankAccount')), + ('club', models.OneToOneField(related_name='club_accounts', to='club.Club')), + ], + ), migrations.CreateModel( name='Customer', fields=[ - ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, serialize=False, primary_key=True)), - ('account_id', models.CharField(max_length=10, verbose_name='account id', unique=True)), + ('user', models.OneToOneField(primary_key=True, to=settings.AUTH_USER_MODEL, serialize=False)), + ('account_id', models.CharField(verbose_name='account id', unique=True, max_length=10)), ], options={ - 'verbose_name': 'customer', 'verbose_name_plural': 'customers', + 'verbose_name': 'customer', }, ), migrations.CreateModel( name='GeneralJournal', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), ('start_date', models.DateField(verbose_name='start date')), - ('end_date', models.DateField(default=None, blank=True, null=True, verbose_name='end date')), - ('name', models.CharField(max_length=30, verbose_name='name')), + ('end_date', models.DateField(default=None, verbose_name='end date', null=True, blank=True)), + ('name', models.CharField(verbose_name='name', max_length=30)), ('closed', models.BooleanField(default=False, verbose_name='is closed')), + ('club_account', models.ForeignKey(related_name='journals', to='accounting.ClubAccount')), ], ), migrations.CreateModel( - name='GenericInvoice', + name='Operation', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), - ('name', models.CharField(max_length=100, verbose_name='name')), - ('journal', models.ForeignKey(to='accounting.GeneralJournal', related_name='invoices')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), + ('name', models.CharField(verbose_name='name', max_length=100)), + ('journal', models.ForeignKey(related_name='invoices', to='accounting.GeneralJournal')), ], ), migrations.CreateModel( name='Product', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), - ('name', models.CharField(max_length=30, verbose_name='name')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), + ('name', models.CharField(verbose_name='name', max_length=30)), ('description', models.TextField(blank=True, verbose_name='description')), - ('code', models.CharField(max_length=10, verbose_name='code')), - ('purchase_price', accounting.models.CurrencyField(max_digits=12, verbose_name='purchase price', decimal_places=2)), - ('selling_price', accounting.models.CurrencyField(max_digits=12, verbose_name='selling price', decimal_places=2)), - ('special_selling_price', accounting.models.CurrencyField(max_digits=12, verbose_name='special selling price', decimal_places=2)), - ('icon', models.ImageField(upload_to='products', blank=True, null=True)), + ('code', models.CharField(verbose_name='code', max_length=10)), + ('purchase_price', accounting.models.CurrencyField(verbose_name='purchase price', decimal_places=2, max_digits=12)), + ('selling_price', accounting.models.CurrencyField(verbose_name='selling price', decimal_places=2, max_digits=12)), + ('special_selling_price', accounting.models.CurrencyField(verbose_name='special selling price', decimal_places=2, max_digits=12)), + ('icon', models.ImageField(blank=True, null=True, upload_to='products')), ], ), migrations.CreateModel( name='ProductType', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), - ('name', models.CharField(max_length=30, verbose_name='name')), - ('description', models.TextField(blank=True, null=True, verbose_name='description')), - ('icon', models.ImageField(upload_to='products', blank=True, null=True)), + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), + ('name', models.CharField(verbose_name='name', max_length=30)), + ('description', models.TextField(blank=True, verbose_name='description', null=True)), + ('icon', models.ImageField(blank=True, null=True, upload_to='products')), ], ), migrations.AddField( model_name='product', name='product_type', - field=models.ForeignKey(blank=True, related_name='products', to='accounting.ProductType', null=True), + field=models.ForeignKey(blank=True, related_name='products', null=True, to='accounting.ProductType'), ), ] diff --git a/accounting/migrations/0002_auto_20160419_2327.py b/accounting/migrations/0002_auto_20160419_2327.py new file mode 100644 index 00000000..8d8ac498 --- /dev/null +++ b/accounting/migrations/0002_auto_20160419_2327.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounting', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='bankaccount', + name='number', + field=models.CharField(max_length=255, blank=True, verbose_name='account number'), + ), + migrations.AlterField( + model_name='bankaccount', + name='rib', + field=models.CharField(max_length=255, blank=True, verbose_name='rib'), + ), + ] diff --git a/accounting/models.py b/accounting/models.py index f95051ae..02b78f33 100644 --- a/accounting/models.py +++ b/accounting/models.py @@ -1,8 +1,10 @@ from django.db import models +from django.conf import settings from django.utils.translation import ugettext_lazy as _ from decimal import Decimal from core.models import User +from club.models import Club class CurrencyField(models.DecimalField): """ @@ -64,31 +66,58 @@ class Product(models.Model): def __str__(self): return self.name +class BankAccount(models.Model): + name = models.CharField(_('name'), max_length=30) + rib = models.CharField(_('rib'), max_length=255, blank=True) + number = models.CharField(_('account number'), max_length=255, blank=True) + + def __str__(self): + return self.name + +class ClubAccount(models.Model): + name = models.CharField(_('name'), max_length=30) + club = models.OneToOneField(Club, related_name="club_accounts") + bank_account = models.ForeignKey(BankAccount, related_name="club_accounts") + + def __str__(self): + return self.name + class GeneralJournal(models.Model): """ - Class storing all the invoices for a period of time + Class storing all the operations for a period of time """ start_date = models.DateField(_('start date')) end_date = models.DateField(_('end date'), null=True, blank=True, default=None) name = models.CharField(_('name'), max_length=30) closed = models.BooleanField(_('is closed'), default=False) - # When clubs are done: ForeignKey(Proprietary) + club_account = models.ForeignKey(ClubAccount, related_name="journals", null=False) def __str__(self): return self.name - -class GenericInvoice(models.Model): +class AccountingType(models.Model): """ - This class is a generic invoice, made to be extended with some special cases (eg: for the internal accounting, payment - system, etc...) + Class describing the accounting types. + + Thoses are numbers used in accounting to classify operations + """ + code = models.CharField(_('code'), max_length=16) # TODO: add number validator + label = models.CharField(_('label'), max_length=60) + movement_type = models.CharField(_('movement type'), choices=[('credit', 'Credit'), ('debit', 'Debit'), ('neutral', 'Neutral')]) + +class Operation(models.Model): + """ + An operation is a line in the journal, a debit or a credit """ journal = models.ForeignKey(GeneralJournal, related_name="invoices", null=False) - name = models.CharField(_('name'), max_length=100) + date = models.DateField(_('date')) + remark = models.TextField(_('remark'), max_length=255) + mode = models.CharField(_('payment method'), max_length=255, choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD) + cheque_number = models.IntegerField(_('cheque number')) + invoice = models.FileField(upload_to='invoices', null=True, blank=True) + done = models.BooleanField(_('is done'), default=False) + type = models.ForeignKey(AccountingType, related_name="operations") def __str__(self): return self.journal.name+' - '+self.name - -# TODO: CountingInvoice in Counting app extending GenericInvoice -# - ManyToMany Product diff --git a/club/models.py b/club/models.py index 087c7402..99d783de 100644 --- a/club/models.py +++ b/club/models.py @@ -111,8 +111,8 @@ class Membership(models.Model): club = models.ForeignKey(Club, related_name="members", null=False, blank=False) start_date = models.DateField(_('start date'), auto_now=True) end_date = models.DateField(_('end date'), null=True, blank=True) - role = models.IntegerField(_('role'), choices=sorted(settings.CLUB_ROLES.items()), - default=sorted(settings.CLUB_ROLES.items())[0][0]) + role = models.IntegerField(_('role'), choices=sorted(settings.SITH_CLUB_ROLES.items()), + default=sorted(settings.SITH_CLUB_ROLES.items())[0][0]) description = models.CharField(_('description'), max_length=30, null=False, blank=True) def clean(self): @@ -123,7 +123,7 @@ class Membership(models.Model): raise ValidationError(_('User is already member of that club')) def __str__(self): - return self.club.name+' - '+self.user.username+' - '+settings.CLUB_ROLES[self.role]+str( + return self.club.name+' - '+self.user.username+' - '+settings.SITH_CLUB_ROLES[self.role]+str( " - "+str(_('past member')) if self.end_date is not None else "" ) diff --git a/club/views.py b/club/views.py index 0431f0c5..a75bb60e 100644 --- a/club/views.py +++ b/club/views.py @@ -8,7 +8,7 @@ from django.core.exceptions import ValidationError from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin from club.models import Club, Membership -from sith.settings import MAXIMUM_FREE_ROLE, SITH_MAIN_BOARD_GROUP +from sith.settings import SITH_MAXIMUM_FREE_ROLE, SITH_MAIN_BOARD_GROUP class ClubListView(CanViewMixin, ListView): """ @@ -42,7 +42,7 @@ class ClubMemberForm(forms.ModelForm): """ ret = super(ClubMemberForm, self).clean() ms = self.instance.club.get_membership_for(self._user) - if (self.cleaned_data['role'] <= MAXIMUM_FREE_ROLE or + if (self.cleaned_data['role'] <= SITH_MAXIMUM_FREE_ROLE or (ms is not None and ms.role >= self.cleaned_data['role']) or self._user.is_in_group(SITH_MAIN_BOARD_GROUP) or self._user.is_superuser): diff --git a/core/management/commands/populate.py b/core/management/commands/populate.py index 15e76fc2..2fb5463e 100644 --- a/core/management/commands/populate.py +++ b/core/management/commands/populate.py @@ -5,7 +5,7 @@ from django.conf import settings from core.models import Group, User, Page, PageRev -from accounting.models import Customer, GeneralJournal, ProductType, Product +from accounting.models import Customer, GeneralJournal, ProductType, Product, BankAccount, ClubAccount, Operation from club.models import Club, Membership from subscription.models import Subscription, Subscriber @@ -84,10 +84,10 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site. # Subscription ## Skia Subscription(member=Subscriber.objects.filter(pk=s.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0], - payment_method=settings.SITH_PAYMENT_METHOD[0]).save() + payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0]).save() ## Richard Subscription(member=Subscriber.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0], - payment_method=settings.SITH_PAYMENT_METHOD[0]).save() + payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0]).save() # Clubs Club(name="Bibo'UT", unix_name="bibout", @@ -95,6 +95,9 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site. guyut = Club(name="Guy'UT", unix_name="guyut", address="42 de la Boustifaille", parent=ae) guyut.save() + troll = Club(name="Troll Penché", unix_name="troll", + address="Terre Du Milieu", parent=ae) + troll.save() Club(name="Woenzel'UT", unix_name="woenzel", address="Woenzel", parent=guyut).save() Club(name="BdF", unix_name="bdf", @@ -113,5 +116,11 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site. special_selling_price="1.6").save() Product(name="Carolus", code="CARO", product_type=p, purchase_price="1.50", selling_price="1.7", special_selling_price="1.6").save() - GeneralJournal(start_date="2015-06-12", name="A15").save() + BankAccount(name="AE TG").save() + BankAccount(name="Carte AE").save() + ba = BankAccount(name="AE TI") + ba.save() + ca = ClubAccount(name="Troll Penché", bank_account=ba, club=troll) + ca.save() + diff --git a/sith/settings.py b/sith/settings.py index 6d27972d..acec74ad 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -201,10 +201,18 @@ SITH_MEMBER_SUFFIX="-members" 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_PAYMENT_METHOD = [('cheque', 'Chèque'), - ('cash', 'Espèce'), - ('other', 'Autre'), - ] +SITH_ACCOUNTING_PAYMENT_METHOD = [ + ('cheque', 'Chèque'), + ('cash', 'Espèce'), + ('transfert', 'Virement'), + ('card', 'Carte banquaire'), + ] + +SITH_SUBSCRIPTION_PAYMENT_METHOD = [ + ('cheque', 'Chèque'), + ('cash', 'Espèce'), + ('other', 'Autre'), + ] # Subscription durations are in semestres (should be settingized) SITH_SUBSCRIPTIONS = { @@ -231,7 +239,7 @@ SITH_SUBSCRIPTIONS = { # To be completed.... } -CLUB_ROLES = { +SITH_CLUB_ROLES = { 10: 'Président', 9: 'Vice-Président', 7: 'Trésorier', @@ -244,8 +252,8 @@ CLUB_ROLES = { } # This corresponds to the maximum role a user can freely subscribe to -# In this case, MAXIMUM_FREE_ROLE=1 means that a user can set himself as "Membre actif" or "Curieux", but not higher -MAXIMUM_FREE_ROLE=1 +# 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 # Minutes to timeout the logged barmen SITH_BARMAN_TIMEOUT=20 diff --git a/subscription/models.py b/subscription/models.py index df868386..b8e624b2 100644 --- a/subscription/models.py +++ b/subscription/models.py @@ -13,7 +13,7 @@ def validate_type(value): raise ValidationError(_('Bad subscription type')) def validate_payment(value): - if value not in settings.SITH_PAYMENT_METHOD: + if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD: raise ValidationError(_('Bad payment method')) class Subscriber(User): @@ -34,7 +34,7 @@ class Subscription(models.Model): 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'), max_length=255, choices=settings.SITH_PAYMENT_METHOD) + payment_method = models.CharField(_('payment method'), max_length=255, choices=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD) # TODO add location! class Meta: