Commit a0f7150c authored by Skia's avatar Skia
Browse files

Update accounting to have a target

parent d824d0d9
Pipeline #97 failed with stage
in 3 minutes and 6 seconds
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0012_auto_20160720_1847'),
]
operations = [
migrations.CreateModel(
name='Company',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
('name', models.CharField(max_length=60, verbose_name='name')),
],
options={
'verbose_name': 'company',
},
),
migrations.AddField(
model_name='operation',
name='target_id',
field=models.IntegerField(blank=True, null=True, verbose_name='target id'),
),
migrations.AddField(
model_name='operation',
name='target_label',
field=models.CharField(max_length=32, blank=True, default='', verbose_name='target label'),
),
migrations.AddField(
model_name='operation',
name='target_type',
field=models.CharField(max_length=10, default='OTHER', choices=[('USER', 'User'), ('CLUB', 'Club'), ('ACCOUNT', 'Account'), ('COMPANY', 'Company'), ('OTHER', 'Other')], verbose_name='target type'),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0014_auto_20160807_1954'),
]
operations = [
migrations.AlterField(
model_name='operation',
name='accounting_type',
field=models.ForeignKey(related_name='operations', verbose_name='accounting type', to='accounting.AccountingType'),
),
migrations.AlterField(
model_name='operation',
name='invoice',
field=models.FileField(upload_to='invoices', verbose_name='invoice', null=True, blank=True),
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0015_auto_20160807_1959'),
]
operations = [
migrations.AlterModelOptions(
name='accountingtype',
options={'verbose_name': 'accounting type'},
),
]
...@@ -27,6 +27,19 @@ class CurrencyField(models.DecimalField): ...@@ -27,6 +27,19 @@ class CurrencyField(models.DecimalField):
except AttributeError: except AttributeError:
return None return None
# Accounting classes
class Company(models.Model):
name = models.CharField(_('name'), max_length=60)
class Meta:
verbose_name = _("company")
def get_absolute_url(self):
return reverse('accounting:co_edit', kwargs={'co_id': self.id})
def get_display_name(self):
return self.name
class BankAccount(models.Model): class BankAccount(models.Model):
name = models.CharField(_('name'), max_length=30) name = models.CharField(_('name'), max_length=30)
...@@ -85,6 +98,10 @@ class ClubAccount(models.Model): ...@@ -85,6 +98,10 @@ class ClubAccount(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
def get_display_name(self):
return _("%(club_account)s on %(bank_account)s") % {"club_account": self.name, "bank_account": self.bank_account}
class GeneralJournal(models.Model): class GeneralJournal(models.Model):
""" """
Class storing all the operations for a period of time Class storing all the operations for a period of time
...@@ -139,19 +156,45 @@ class Operation(models.Model): ...@@ -139,19 +156,45 @@ class Operation(models.Model):
remark = models.TextField(_('remark'), max_length=255) remark = models.TextField(_('remark'), max_length=255)
mode = models.CharField(_('payment method'), max_length=255, choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD) mode = models.CharField(_('payment method'), max_length=255, choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD)
cheque_number = models.IntegerField(_('cheque number'), default=-1) cheque_number = models.IntegerField(_('cheque number'), default=-1)
invoice = models.FileField(upload_to='invoices', null=True, blank=True) invoice = models.FileField(upload_to='invoices', verbose_name=_("invoice"), null=True, blank=True)
done = models.BooleanField(_('is done'), default=False) done = models.BooleanField(_('is done'), default=False)
accounting_type = models.ForeignKey('AccountingType', related_name="operations") accounting_type = models.ForeignKey('AccountingType', related_name="operations", verbose_name=_("accounting type"))
target_type = models.CharField(_('target type'), max_length=10,
choices=[('USER', _('User')), ('CLUB', _('Club')), ('ACCOUNT', _('Account')), ('COMPANY', _('Company')), ('OTHER', _('Other'))])
target_id = models.IntegerField(_('target id'), null=True, blank=True)
target_label = models.CharField(_('target label'), max_length=32, default="", blank=True)
class Meta: class Meta:
unique_together = ('number', 'journal') unique_together = ('number', 'journal')
ordering = ['-number'] ordering = ['-number']
def __getattribute__(self, attr):
if attr == "target":
return self.get_target()
else:
return object.__getattribute__(self, attr)
def clean(self): def clean(self):
super(Operation, self).clean() super(Operation, self).clean()
if self.date < self.journal.start_date: if self.date < self.journal.start_date:
raise ValidationError(_("""The date can not be before the start date of the journal, which is raise ValidationError(_("""The date can not be before the start date of the journal, which is
%(start_date)s.""") % {'start_date': defaultfilters.date(self.journal.start_date, settings.DATE_FORMAT)}) %(start_date)s.""") % {'start_date': defaultfilters.date(self.journal.start_date, settings.DATE_FORMAT)})
if self.target_type != "OTHER" and self.get_target() is None:
raise ValidationError(_("Target does not exists"))
if self.target_type == "OTHER" and self.target_label == "":
raise ValidationError(_("Please add a target label if you set no existing target"))
def get_target(self):
tar = None
if self.target_type == "USER":
tar = User.objects.filter(id=self.target_id).first()
elif self.target_type == "CLUB":
tar = Club.objects.filter(id=self.target_id).first()
elif self.target_type == "ACCOUNT":
tar = ClubAccount.objects.filter(id=self.target_id).first()
elif self.target_type == "COMPANY":
tar = Company.objects.filter(id=self.target_id).first()
return tar
def save(self): def save(self):
if self.number is None: if self.number is None:
...@@ -198,6 +241,9 @@ class AccountingType(models.Model): ...@@ -198,6 +241,9 @@ class AccountingType(models.Model):
label = models.CharField(_('label'), max_length=60) label = models.CharField(_('label'), max_length=60)
movement_type = models.CharField(_('movement type'), choices=[('credit', 'Credit'), ('debit', 'Debit'), ('neutral', 'Neutral')], max_length=12) movement_type = models.CharField(_('movement type'), choices=[('credit', 'Credit'), ('debit', 'Debit'), ('neutral', 'Neutral')], max_length=12)
class Meta:
verbose_name = _("accounting type")
def is_owned_by(self, user): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given user Method to see if that object can be edited by the given user
...@@ -211,4 +257,3 @@ class AccountingType(models.Model): ...@@ -211,4 +257,3 @@ class AccountingType(models.Model):
def __str__(self): def __str__(self):
return self.movement_type+" - "+self.code+" - "+self.label return self.movement_type+" - "+self.code+" - "+self.label
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
<td>{% trans %}Label{% endtrans %}</td> <td>{% trans %}Label{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td> <td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Payment mode{% endtrans %}</td> <td>{% trans %}Payment mode{% endtrans %}</td>
<!-- TODO: <td>Target</td> --> <td>{% trans %}Target{% endtrans %}</td>
<td>{% trans %}Code{% endtrans %}</td> <td>{% trans %}Code{% endtrans %}</td>
<td>{% trans %}Nature{% endtrans %}</td> <td>{% trans %}Nature{% endtrans %}</td>
<td>{% trans %}Done{% endtrans %}</td> <td>{% trans %}Done{% endtrans %}</td>
...@@ -37,7 +37,12 @@ ...@@ -37,7 +37,12 @@
<td>{{ o.date }}</td> <td>{{ o.date }}</td>
<td>{{ o.label }}</td> <td>{{ o.label }}</td>
<td>{{ o.amount }}</td> <td>{{ o.amount }}</td>
<td>{{ o.mode }}</td> <td>{{ o.get_mode_display() }}</td>
{% if o.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% else %}
<td><a href="{{ o.target.get_absolute_url() }}">{{ o.target.get_display_name() }}</a></td>
{% endif %}
<td>{{ o.accounting_type.code }}</td> <td>{{ o.accounting_type.code }}</td>
<td>{{ o.accounting_type.label }}</td> <td>{{ o.accounting_type.label }}</td>
{% if o.done %} {% if o.done %}
......
...@@ -25,6 +25,9 @@ urlpatterns = [ ...@@ -25,6 +25,9 @@ urlpatterns = [
# Operations # Operations
url(r'^operation/create$', OperationCreateView.as_view(), name='op_new'), url(r'^operation/create$', OperationCreateView.as_view(), name='op_new'),
url(r'^operation/(?P<op_id>[0-9]+)$', OperationEditView.as_view(), name='op_edit'), url(r'^operation/(?P<op_id>[0-9]+)$', OperationEditView.as_view(), name='op_edit'),
# Companies
url(r'^company/create$', CompanyCreateView.as_view(), name='co_new'),
url(r'^company/(?P<co_id>[0-9]+)$', CompanyEditView.as_view(), name='co_edit'),
] ]
...@@ -6,7 +6,7 @@ from django.forms.models import modelform_factory ...@@ -6,7 +6,7 @@ from django.forms.models import modelform_factory
from django.forms import HiddenInput from django.forms import HiddenInput
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
from accounting.models import BankAccount, ClubAccount, GeneralJournal, Operation, AccountingType from accounting.models import BankAccount, ClubAccount, GeneralJournal, Operation, AccountingType, Company
# Accounting types # Accounting types
...@@ -164,7 +164,8 @@ class OperationCreateView(CanCreateMixin, CreateView): ...@@ -164,7 +164,8 @@ class OperationCreateView(CanCreateMixin, CreateView):
""" """
model = Operation model = Operation
form_class = modelform_factory(Operation, form_class = modelform_factory(Operation,
fields=['amount', 'label', 'remark', 'journal', 'date', 'mode', 'cheque_number', 'accounting_type', 'done'], fields=['amount', 'label', 'remark', 'journal', 'target_type', 'target_id', 'target_label', 'date', 'mode',
'cheque_number', 'invoice', 'accounting_type', 'done'],
widgets={'journal': HiddenInput}) widgets={'journal': HiddenInput})
template_name = 'core/create.jinja' template_name = 'core/create.jinja'
...@@ -182,6 +183,26 @@ class OperationEditView(CanEditMixin, UpdateView): ...@@ -182,6 +183,26 @@ class OperationEditView(CanEditMixin, UpdateView):
""" """
model = Operation model = Operation
pk_url_kwarg = "op_id" pk_url_kwarg = "op_id"
fields = ['amount', 'label', 'remark', 'date', 'mode', 'cheque_number', 'accounting_type', 'done'] fields = ['amount', 'label', 'remark', 'target_type', 'target_id', 'target_label', 'date', 'mode', 'cheque_number',
'invoice', 'accounting_type', 'done']
template_name = 'core/edit.jinja'
# Company views
class CompanyCreateView(CanCreateMixin, CreateView):
"""
Create a company
"""
model = Company
fields = ['name']
template_name = 'core/create.jinja'
class CompanyEditView(CanCreateMixin, UpdateView):
"""
Edit a company
"""
model = Company
pk_url_kwarg = "co_id"
fields = ['name']
template_name = 'core/edit.jinja' template_name = 'core/edit.jinja'
...@@ -61,6 +61,9 @@ class Club(models.Model): ...@@ -61,6 +61,9 @@ class Club(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('club:club_view', kwargs={'club_id': self.id}) return reverse('club:club_view', kwargs={'club_id': self.id})
def get_display_name(self):
return self.name
def is_owned_by(self, user): def is_owned_by(self, user):
""" """
Method to see if that object can be super edited by the given user Method to see if that object can be super edited by the given user
......
import os import os
from datetime import date from datetime import date, datetime
from io import StringIO from io import StringIO
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
...@@ -9,7 +9,7 @@ from django.db import connection ...@@ -9,7 +9,7 @@ from django.db import connection
from core.models import Group, User, Page, PageRev from core.models import Group, User, Page, PageRev
from accounting.models import GeneralJournal, BankAccount, ClubAccount, Operation, AccountingType from accounting.models import GeneralJournal, BankAccount, ClubAccount, Operation, AccountingType, Company
from club.models import Club, Membership from club.models import Club, Membership
from subscription.models import Subscription, Subscriber from subscription.models import Subscription, Subscriber
from counter.models import Customer, ProductType, Product, Counter from counter.models import Customer, ProductType, Product, Counter
...@@ -239,6 +239,18 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site. ...@@ -239,6 +239,18 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
ba.save() ba.save()
ca = ClubAccount(name="Troll Penché", bank_account=ba, club=troll) ca = ClubAccount(name="Troll Penché", bank_account=ba, club=troll)
ca.save() ca.save()
AccountingType(code=756, label="Someone gave us money", movement_type='credit').save() gj = GeneralJournal(name="A16", start_date=date.today(), club_account=ca)
AccountingType(code=8570, label="Had to pay for food", movement_type='debit').save() gj.save()
credit = AccountingType(code=74, label="Someone gave us money", movement_type='credit')
credit.save()
debit = AccountingType(code=607, label="Had to pay a beer", movement_type='debit')
debit.save()
Operation(journal=gj, date=date.today(), amount=666.42, label="Satanic answer",
remark="An answer to life...", mode="CASH", done=True, accounting_type=credit, target_type="USER",
target_id=skia.id).save()
Operation(journal=gj, date=date.today(), amount=42, label="Answer",
remark="An answer to life...", mode="CASH", done=False, accounting_type=debit, target_type="CLUB",
target_id=bar_club.id).save()
woenzco = Company(name="Woenzel & co")
woenzco.save()
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-08-06 15:08+0200\n" "POT-Creation-Date: 2016-08-07 20:00+0200\n"
"PO-Revision-Date: 2016-07-18\n" "PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Skia <skia@libskia.so>\n" "Last-Translator: Skia <skia@libskia.so>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n" "Language-Team: AE info <ae.info@utbm.fr>\n"
...@@ -16,74 +16,124 @@ msgstr "" ...@@ -16,74 +16,124 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: accounting/models.py:32 accounting/models.py:55 accounting/models.py:94 #: accounting/models.py:33 accounting/models.py:45 accounting/models.py:68
#: club/models.py:18 counter/models.py:52 counter/models.py:77 #: accounting/models.py:111 club/models.py:18 counter/models.py:52
#: counter/models.py:105 launderette/models.py:14 launderette/models.py:42 #: counter/models.py:77 counter/models.py:105 launderette/models.py:14
#: launderette/models.py:65 #: launderette/models.py:42 launderette/models.py:65
msgid "name" msgid "name"
msgstr "nom" msgstr "nom"
#: accounting/models.py:33 #: accounting/models.py:36
msgid "company"
msgstr "entreprise"
#: accounting/models.py:46
msgid "iban" msgid "iban"
msgstr "IBAN" msgstr "IBAN"
#: accounting/models.py:34 #: accounting/models.py:47
msgid "account number" msgid "account number"
msgstr "numero de compte" msgstr "numero de compte"
#: accounting/models.py:92 club/models.py:109 counter/models.py:259 #: accounting/models.py:102
#, python-format
msgid "%(club_account)s on %(bank_account)s"
msgstr "%(club_account)s sur %(bank_account)s"
#: accounting/models.py:109 club/models.py:112 counter/models.py:259
#: launderette/models.py:94 #: launderette/models.py:94
msgid "start date" msgid "start date"
msgstr "date de début" msgstr "date de début"
#: accounting/models.py:93 club/models.py:110 counter/models.py:260 #: accounting/models.py:110 club/models.py:113 counter/models.py:260
msgid "end date" msgid "end date"
msgstr "date de fin" msgstr "date de fin"
#: accounting/models.py:95 #: accounting/models.py:112
msgid "is closed" msgid "is closed"
msgstr "est fermé" msgstr "est fermé"
#: accounting/models.py:97 accounting/models.py:136 counter/models.py:25 #: accounting/models.py:114 accounting/models.py:153 counter/models.py:25
#: counter/models.py:197 #: counter/models.py:197
msgid "amount" msgid "amount"
msgstr "montant" msgstr "montant"
#: accounting/models.py:98 #: accounting/models.py:115
msgid "effective_amount" msgid "effective_amount"
msgstr "montant effectif" msgstr "montant effectif"
#: accounting/models.py:134 #: accounting/models.py:151
msgid "number" msgid "number"
msgstr "numéro" msgstr "numéro"
#: accounting/models.py:137 core/models.py:466 counter/models.py:200 #: accounting/models.py:154 core/models.py:466 counter/models.py:200
#: counter/models.py:235 eboutic/models.py:13 eboutic/models.py:46 #: counter/models.py:235 eboutic/models.py:13 eboutic/models.py:46
msgid "date" msgid "date"
msgstr "date" msgstr "date"
#: accounting/models.py:138 accounting/models.py:198 counter/models.py:228 #: accounting/models.py:155 accounting/models.py:241 counter/models.py:228
msgid "label" msgid "label"
msgstr "intitulé" msgstr "intitulé"
#: accounting/models.py:139 #: accounting/models.py:156
msgid "remark" msgid "remark"
msgstr "remarque" msgstr "remarque"
#: accounting/models.py:140 counter/models.py:201 eboutic/models.py:48 #: accounting/models.py:157 counter/models.py:201 eboutic/models.py:48
#: subscription/models.py:34 #: subscription/models.py:34
msgid "payment method" msgid "payment method"
msgstr "méthode de paiement" msgstr "méthode de paiement"
#: accounting/models.py:141 #: accounting/models.py:158
msgid "cheque number" msgid "cheque number"
msgstr "numéro de chèque" msgstr "numéro de chèque"
#: accounting/models.py:143 #: accounting/models.py:159 eboutic/models.py:92
msgid "invoice"
msgstr "facture"
#: accounting/models.py:160
msgid "is done" msgid "is done"
msgstr "est fait" msgstr "est fait"
#: accounting/models.py:153 #: accounting/models.py:161 accounting/models.py:245
msgid "accounting type"
msgstr "type comptable"
#: accounting/models.py:162
msgid "target type"
msgstr "type de cible"
#: accounting/models.py:163
#: launderette/templates/launderette/launderette_admin.jinja:34
msgid "User"
msgstr "Utilisateur"
#: accounting/models.py:163 club/templates/club/club_detail.jinja:4
msgid "Club"
msgstr "Club"
#: accounting/models.py:163 core/templates/core/user_base.jinja:16
msgid "Account"
msgstr "Compte"
#: accounting/models.py:163
msgid "Company"
msgstr "Entreprise"
#: accounting/models.py:163 sith/settings.py:259 sith/settings_sample.py:259
msgid "Other"
msgstr "Autre"
#: accounting/models.py:164
msgid "target id"
msgstr "id de la cible"
#: accounting/models.py:165
msgid "target label"
msgstr "nom de la cible"
#: accounting/models.py:180
#, python-format #, python-format
msgid "" msgid ""
"The date can not be before the start date of the journal, which is\n" "The date can not be before the start date of the journal, which is\n"
...@@ -92,11 +142,20 @@ msgstr "" ...@@ -92,11 +142,20 @@ msgstr ""
"La date ne peut pas être avant la date de début du journal, qui est\n" "La date ne peut pas être avant la date de début du journal, qui est\n"
"%(start_date)s." "%(start_date)s."
#: accounting/models.py:197 counter/models.py:80 #: accounting/models.py:183
msgid "Target does not exists"
msgstr "La cible n'existe pas."
#: accounting/models.py:185
msgid "Please add a target label if you set no existing target"
msgstr ""
"Merci d'ajouter un nom de cible si vous ne spécifiez pas de cible existante"
#: accounting/models.py:240 counter/models.py:80
msgid "code" msgid "code"
msgstr "code" msgstr "code"
#: accounting/models.py:199 #: accounting/models.py:242
msgid "movement type" msgid "movement type"
msgstr "type de mouvement" msgstr "type de mouvement"