Commit a0f7150c authored by Skia's avatar Skia

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):
except AttributeError:
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):
name = models.CharField(_('name'), max_length=30)
......@@ -85,6 +98,10 @@ class ClubAccount(models.Model):
def __str__(self):
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 storing all the operations for a period of time
......@@ -139,19 +156,45 @@ class Operation(models.Model):
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'), 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)
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:
unique_together = ('number', 'journal')
ordering = ['-number']
def __getattribute__(self, attr):
if attr == "target":
return self.get_target()
else:
return object.__getattribute__(self, attr)
def clean(self):
super(Operation, self).clean()
if self.date < self.journal.start_date:
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)})
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):
if self.number is None:
......@@ -198,6 +241,9 @@ class AccountingType(models.Model):
label = models.CharField(_('label'), max_length=60)
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):
"""
Method to see if that object can be edited by the given user
......@@ -211,4 +257,3 @@ class AccountingType(models.Model):
def __str__(self):
return self.movement_type+" - "+self.code+" - "+self.label
......@@ -22,7 +22,7 @@
<td>{% trans %}Label{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Payment mode{% endtrans %}</td>
<!-- TODO: <td>Target</td> -->
<td>{% trans %}Target{% endtrans %}</td>
<td>{% trans %}Code{% endtrans %}</td>
<td>{% trans %}Nature{% endtrans %}</td>
<td>{% trans %}Done{% endtrans %}</td>
......@@ -37,7 +37,12 @@
<td>{{ o.date }}</td>
<td>{{ o.label }}</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.label }}</td>
{% if o.done %}
......
......@@ -25,6 +25,9 @@ urlpatterns = [
# Operations
url(r'^operation/create$', OperationCreateView.as_view(), name='op_new'),
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
from django.forms import HiddenInput
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
......@@ -164,7 +164,8 @@ class OperationCreateView(CanCreateMixin, CreateView):
"""
model = 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})
template_name = 'core/create.jinja'
......@@ -182,6 +183,26 @@ class OperationEditView(CanEditMixin, UpdateView):
"""
model = Operation
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'
......@@ -61,6 +61,9 @@ class Club(models.Model):
def get_absolute_url(self):
return reverse('club:club_view', kwargs={'club_id': self.id})
def get_display_name(self):
return self.name
def is_owned_by(self, user):
"""
Method to see if that object can be super edited by the given user
......
import os
from datetime import date
from datetime import date, datetime
from io import StringIO
from django.core.management.base import BaseCommand, CommandError
......@@ -9,7 +9,7 @@ from django.db import connection
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 subscription.models import Subscription, Subscriber
from counter.models import Customer, ProductType, Product, Counter
......@@ -239,6 +239,18 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
ba.save()
ca = ClubAccount(name="Troll Penché", bank_account=ba, club=troll)
ca.save()
AccountingType(code=756, label="Someone gave us money", movement_type='credit').save()
AccountingType(code=8570, label="Had to pay for food", movement_type='debit').save()
gj = GeneralJournal(name="A16", start_date=date.today(), club_account=ca)
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 @@
msgid ""
msgstr ""
"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"
"Last-Translator: Skia <skia@libskia.so>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
......@@ -16,74 +16,124 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: accounting/models.py:32 accounting/models.py:55 accounting/models.py:94
#: club/models.py:18 counter/models.py:52 counter/models.py:77
#: counter/models.py:105 launderette/models.py:14 launderette/models.py:42
#: launderette/models.py:65
#: accounting/models.py:33 accounting/models.py:45 accounting/models.py:68
#: accounting/models.py:111 club/models.py:18 counter/models.py:52
#: counter/models.py:77 counter/models.py:105 launderette/models.py:14
#: launderette/models.py:42 launderette/models.py:65
msgid "name"
msgstr "nom"
#: accounting/models.py:33
#: accounting/models.py:36
msgid "company"
msgstr "entreprise"
#: accounting/models.py:46
msgid "iban"
msgstr "IBAN"
#: accounting/models.py:34
#: accounting/models.py:47
msgid "account number"
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
msgid "start date"
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"
msgstr "date de fin"
#: accounting/models.py:95
#: accounting/models.py:112
msgid "is closed"
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
msgid "amount"
msgstr "montant"
#: accounting/models.py:98
#: accounting/models.py:115
msgid "effective_amount"
msgstr "montant effectif"
#: accounting/models.py:134
#: accounting/models.py:151
msgid "number"
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
msgid "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"
msgstr "intitulé"
#: accounting/models.py:139
#: accounting/models.py:156
msgid "remark"
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
msgid "payment method"
msgstr "méthode de paiement"
#: accounting/models.py:141
#: accounting/models.py:158
msgid "cheque number"
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"
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
msgid ""
"The date can not be before the start date of the journal, which is\n"
......@@ -92,11 +142,20 @@ msgstr ""
"La date ne peut pas être avant la date de début du journal, qui est\n"
"%(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"
msgstr "code"
#: accounting/models.py:199
#: accounting/models.py:242
msgid "movement type"
msgstr "type de mouvement"
......@@ -145,7 +204,7 @@ msgstr "Nouveau compte club"
#: accounting/templates/accounting/bank_account_details.jinja:18
#: accounting/templates/accounting/bank_account_list.jinja:15
#: accounting/templates/accounting/club_account_details.jinja:44
#: accounting/templates/accounting/journal_details.jinja:51
#: accounting/templates/accounting/journal_details.jinja:56
#: club/templates/club/club_detail.jinja:7 core/templates/core/page.jinja:31
#: core/templates/core/user_base.jinja:8
#: core/templates/core/user_tools.jinja:37
......@@ -159,7 +218,7 @@ msgstr "Éditer"
#: accounting/templates/accounting/bank_account_list.jinja:16
#: core/templates/core/group_list.jinja:13
#: launderette/templates/launderette/launderette_admin.jinja:16
#: launderette/views.py:147
#: launderette/views.py:146
msgid "Delete"
msgstr "Supprimer"
......@@ -226,12 +285,12 @@ msgid "Actions"
msgstr "Actions"
#: accounting/templates/accounting/club_account_details.jinja:39
#: accounting/templates/accounting/journal_details.jinja:44
#: accounting/templates/accounting/journal_details.jinja:49
msgid "Yes"
msgstr "Oui"
#: accounting/templates/accounting/club_account_details.jinja:41
#: accounting/templates/accounting/journal_details.jinja:46
#: accounting/templates/accounting/journal_details.jinja:51
msgid "No"
msgstr "Non"
......@@ -277,6 +336,10 @@ msgstr "Intitulé"
msgid "Payment mode"
msgstr "Méthode de paiement"
#: accounting/templates/accounting/journal_details.jinja:25
msgid "Target"
msgstr "Cible"
#: accounting/templates/accounting/journal_details.jinja:26
msgid "Code"
msgstr "Code"
......@@ -317,40 +380,36 @@ msgstr "Adresse"
msgid "You can not make loops in clubs"
msgstr "Vous ne pouvez pas faire de boucles dans les clubs"
#: club/models.py:107 eboutic/models.py:12 eboutic/models.py:45
#: club/models.py:110 eboutic/models.py:12 eboutic/models.py:45
#: launderette/models.py:69 launderette/models.py:98
msgid "user"
msgstr "nom d'utilisateur"
#: club/models.py:108
#: club/models.py:111
msgid "club"
msgstr "club"
#: club/models.py:111
#: club/models.py:114
msgid "role"
msgstr "rôle"
#: club/models.py:113 core/models.py:27 counter/models.py:53
#: club/models.py:116 core/models.py:27 counter/models.py:53
#: counter/models.py:78
msgid "description"
msgstr "description"
#: club/models.py:118
#: club/models.py:121
msgid "User must be subscriber to take part to a club"
msgstr "L'utilisateur doit être cotisant pour faire partie d'un club"
#: club/models.py:120
#: club/models.py:123
msgid "User is already member of that club"
msgstr "L'utilisateur est déjà membre de ce club"
#: club/models.py:124
#: club/models.py:127
msgid "past member"
msgstr "Anciens membres"
#: club/templates/club/club_detail.jinja:4
msgid "Club"
msgstr "Club"
#: club/templates/club/club_detail.jinja:5
#: core/templates/core/group_edit.jinja:4
msgid "Back to list"
......@@ -398,7 +457,7 @@ msgstr "Il n'y a pas de club dans ce site web."
msgid "Club members"
msgstr "Membres du club"
#: club/templates/club/club_members.jinja:13 launderette/views.py:147
#: club/templates/club/club_members.jinja:13 launderette/views.py:146
msgid "Add"
msgstr "Ajouter"
......@@ -857,10 +916,6 @@ msgstr "L'utilisateur n'a pas de compte"
msgid "Groups"
msgstr "Groupes"
#: core/templates/core/user_base.jinja:16
msgid "Account"
msgstr "Compte"
#: core/templates/core/user_detail.jinja:4
#, python-format
msgid "%(user_name)s's profile"
......@@ -1195,7 +1250,7 @@ msgstr "ANN"
msgid "You have not enough money to buy all the basket"
msgstr "Vous n'avez pas assez d'argent pour acheter le panier"
#: eboutic/models.py:47 sith/settings.py:252 sith/settings_sample.py:251
#: eboutic/models.py:47 sith/settings.py:253 sith/settings_sample.py:253
msgid "Credit card"
msgstr "Carte banquaire"
......@@ -1223,10 +1278,6 @@ msgstr "nom du produit"
msgid "basket"
msgstr "panier"
#: eboutic/models.py:92
msgid "invoice"
msgstr "facture"
#: eboutic/templates/eboutic/eboutic_main.jinja:33
msgid "Proceed to command"
msgstr "Procéder à la commande"
......@@ -1323,14 +1374,10 @@ msgid "Tokens"
msgstr "Jetons"
#: launderette/templates/launderette/launderette_admin.jinja:32
#: launderette/views.py:148
#: launderette/views.py:147
msgid "Type"
msgstr "Type"
#: launderette/templates/launderette/launderette_admin.jinja:34
msgid "User"
msgstr "Utilisateur"
#: launderette/templates/launderette/launderette_admin.jinja:35
msgid "Since"
msgstr "Depuis"
......@@ -1344,12 +1391,12 @@ msgid "Washing and drying"
msgstr "Lavage et séchage"
#: launderette/templates/launderette/launderette_book.jinja:26
#: sith/settings.py:340 sith/settings_sample.py:339
#: sith/settings.py:341 sith/settings_sample.py:341
msgid "Washing"
msgstr "Lavage"
#: launderette/templates/launderette/launderette_book.jinja:30
#: sith/settings.py:340 sith/settings_sample.py:339
#: sith/settings.py:341 sith/settings_sample.py:341
msgid "Drying"
msgstr "Séchage"
......@@ -1374,115 +1421,113 @@ msgstr "Éditer la page de présentation"
msgid "Book launderette slot"
msgstr "Réserver un créneau de laverie"
#: launderette/views.py:147