Sith/accounting/views.py

725 lines
27 KiB
Python
Raw Normal View History

# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
2016-04-20 01:01:14 +00:00
from django.views.generic import ListView, DetailView, RedirectView
2016-11-30 01:41:25 +00:00
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
2016-01-28 15:53:37 +00:00
from django.shortcuts import render
2016-11-30 01:41:25 +00:00
from django.core.urlresolvers import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from django.forms.models import modelform_factory
2016-11-30 01:41:25 +00:00
from django.core.exceptions import PermissionDenied
from django.forms import HiddenInput, TextInput
2016-11-30 01:41:25 +00:00
from django.db import transaction
2016-12-21 03:58:52 +00:00
from django.db.models import Sum
2016-11-30 01:41:25 +00:00
from django.conf import settings
2016-08-24 17:50:22 +00:00
from django import forms
2016-11-07 00:51:02 +00:00
from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext as _
from django.conf import settings
2016-12-21 03:58:52 +00:00
import collections
2016-08-24 17:50:22 +00:00
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
2016-04-20 01:01:14 +00:00
2016-12-21 02:52:33 +00:00
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
from core.views.forms import SelectFile, SelectDate
2016-10-05 13:54:00 +00:00
from accounting.models import BankAccount, ClubAccount, GeneralJournal, Operation, AccountingType, Company, SimplifiedAccountingType, Label
from counter.models import Counter, Selling, Product
2016-08-24 17:50:22 +00:00
# Main accounting view
class BankAccountListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = BankAccount
template_name = 'accounting/bank_account_list.jinja'
ordering = ['name']
# Simplified accounting types
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = SimplifiedAccountingType
template_name = 'accounting/simplifiedaccountingtype_list.jinja'
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = SimplifiedAccountingType
pk_url_kwarg = "type_id"
fields = ['label', 'accounting_type']
template_name = 'core/edit.jinja'
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
"""
Create an accounting type (for the admins)
"""
model = SimplifiedAccountingType
fields = ['label', 'accounting_type']
template_name = 'core/create.jinja'
2016-05-03 06:50:54 +00:00
# Accounting types
class AccountingTypeListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = AccountingType
template_name = 'accounting/accountingtype_list.jinja'
class AccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = AccountingType
pk_url_kwarg = "type_id"
fields = ['code', 'label', 'movement_type']
2016-06-24 17:43:11 +00:00
template_name = 'core/edit.jinja'
2016-05-03 06:50:54 +00:00
class AccountingTypeCreateView(CanCreateMixin, CreateView):
2016-05-03 06:50:54 +00:00
"""
Create an accounting type (for the admins)
"""
model = AccountingType
fields = ['code', 'label', 'movement_type']
2016-06-24 17:43:11 +00:00
template_name = 'core/create.jinja'
2016-04-20 01:01:14 +00:00
# BankAccount views
class BankAccountEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
2016-08-24 17:50:22 +00:00
fields = ['name', 'iban', 'number', 'club']
2016-06-24 17:43:11 +00:00
template_name = 'core/edit.jinja'
2016-04-20 01:01:14 +00:00
class BankAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every club account
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
template_name = 'accounting/bank_account_details.jinja'
class BankAccountCreateView(CanCreateMixin, CreateView):
2016-04-20 01:01:14 +00:00
"""
Create a bank account (for the admins)
"""
model = BankAccount
2016-07-21 10:09:57 +00:00
fields = ['name', 'club', 'iban', 'number']
2016-06-24 17:43:11 +00:00
template_name = 'core/create.jinja'
2016-04-20 01:01:14 +00:00
2016-05-09 09:49:01 +00:00
class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
2016-04-20 01:01:14 +00:00
"""
Delete a bank account (for the admins)
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:bank_list')
# ClubAccount views
class ClubAccountEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
fields = ['name', 'club', 'bank_account']
2016-06-24 17:43:11 +00:00
template_name = 'core/edit.jinja'
2016-04-20 01:01:14 +00:00
class ClubAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every journal
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
template_name = 'accounting/club_account_details.jinja'
class ClubAccountCreateView(CanCreateMixin, CreateView):
2016-04-20 01:01:14 +00:00
"""
Create a club account (for the admins)
"""
model = ClubAccount
fields = ['name', 'club', 'bank_account']
2016-06-24 17:43:11 +00:00
template_name = 'core/create.jinja'
def get_initial(self):
ret = super(ClubAccountCreateView, self).get_initial()
if 'parent' in self.request.GET.keys():
obj = BankAccount.objects.filter(id=int(self.request.GET['parent'])).first()
if obj is not None:
ret['bank_account'] = obj.id
return ret
2016-04-20 01:01:14 +00:00
2016-05-09 09:49:01 +00:00
class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
2016-04-20 01:01:14 +00:00
"""
Delete a club account (for the admins)
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:bank_list')
# Journal views
2016-12-21 02:52:33 +00:00
class JournalTabsMixin(TabedViewMixin):
def get_tabs_title(self):
return _("Journal")
def get_list_of_tabs(self):
tab_list = []
tab_list.append({
'url': reverse('accounting:journal_details', kwargs={'j_id': self.object.id}),
'slug': 'journal',
'name': _("Journal"),
})
tab_list.append({
2016-12-21 04:18:02 +00:00
'url': reverse('accounting:journal_nature_statement', kwargs={'j_id': self.object.id}),
'slug': 'nature_statement',
'name': _("Statement by nature"),
2016-12-21 02:52:33 +00:00
})
tab_list.append({
2016-12-21 04:18:02 +00:00
'url': reverse('accounting:journal_person_statement', kwargs={'j_id': self.object.id}),
'slug': 'person_statement',
'name': _("Statement by person"),
2016-12-21 02:52:33 +00:00
})
tab_list.append({
2016-12-21 04:18:02 +00:00
'url': reverse('accounting:journal_accounting_statement', kwargs={'j_id': self.object.id}),
'slug': 'accounting_statement',
'name': _("Accounting statement"),
2016-12-21 02:52:33 +00:00
})
return tab_list
2016-06-22 11:40:30 +00:00
class JournalCreateView(CanCreateMixin, CreateView):
2016-04-20 01:01:14 +00:00
"""
Create a general journal
"""
model = GeneralJournal
2016-08-24 17:50:22 +00:00
form_class = modelform_factory(GeneralJournal, fields=['name', 'start_date', 'club_account'],
widgets={ 'start_date': SelectDate, })
2016-06-24 17:43:11 +00:00
template_name = 'core/create.jinja'
def get_initial(self):
ret = super(JournalCreateView, self).get_initial()
if 'parent' in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET['parent'])).first()
if obj is not None:
ret['club_account'] = obj.id
return ret
2016-05-03 06:50:54 +00:00
2016-12-21 02:52:33 +00:00
class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
2016-05-03 06:50:54 +00:00
"""
A detail view, listing every operation
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = 'accounting/journal_details.jinja'
2016-12-21 02:52:33 +00:00
current_tab = 'journal'
2016-05-03 06:50:54 +00:00
class JournalEditView(CanEditMixin, UpdateView):
"""
Update a general journal
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
2016-06-24 19:55:52 +00:00
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
2016-06-24 17:43:11 +00:00
template_name = 'core/edit.jinja'
2016-05-03 06:50:54 +00:00
2017-02-05 16:26:04 +00:00
class JournalDeleteView(CanEditPropMixin, DeleteView):
"""
Delete a club account (for the admins)
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:club_details')
2017-03-12 19:33:17 +00:00
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.operations.count() == 0:
return super(JournalDeleteView, self).dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
2016-11-07 00:51:02 +00:00
2016-05-03 06:50:54 +00:00
# Operation views
2016-08-24 17:50:22 +00:00
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' ]
2016-08-24 17:50:22 +00:00
widgets = {
'journal': HiddenInput,
'target_id': HiddenInput,
'date': SelectDate,
'invoice': SelectFile,
}
user = AutoCompleteSelectField('users', help_text=None, required=False)
club_account = AutoCompleteSelectField('club_accounts', help_text=None, required=False)
club = AutoCompleteSelectField('clubs', help_text=None, required=False)
company = AutoCompleteSelectField('companies', help_text=None, required=False)
def __init__(self, *args, **kwargs):
2016-10-05 18:17:37 +00:00
club_account = kwargs.pop('club_account', None)
2016-08-24 17:50:22 +00:00
super(OperationForm, self).__init__(*args, **kwargs)
2016-10-05 18:17:37 +00:00
if club_account:
self.fields['label'].queryset = club_account.labels.order_by('name').all()
2016-08-24 17:50:22 +00:00
if self.instance.target_type == "USER":
self.fields['user'].initial = self.instance.target_id
elif self.instance.target_type == "ACCOUNT":
self.fields['club_account'].initial = self.instance.target_id
elif self.instance.target_type == "CLUB":
self.fields['club'].initial = self.instance.target_id
elif self.instance.target_type == "COMPANY":
self.fields['company'].initial = self.instance.target_id
def clean(self):
self.cleaned_data = super(OperationForm, self).clean()
if 'target_type' in self.cleaned_data.keys():
if self.cleaned_data['target_type'] == "USER":
self.cleaned_data['target_id'] = self.cleaned_data['user'].id
elif self.cleaned_data['target_type'] == "ACCOUNT":
self.cleaned_data['target_id'] = self.cleaned_data['club_account'].id
elif self.cleaned_data['target_type'] == "CLUB":
self.cleaned_data['target_id'] = self.cleaned_data['club'].id
elif self.cleaned_data['target_type'] == "COMPANY":
self.cleaned_data['target_id'] = self.cleaned_data['company'].id
2016-08-24 17:50:22 +00:00
return self.cleaned_data
def save(self):
ret = super(OperationForm, self).save()
if self.instance.target_type == "ACCOUNT" and not self.instance.linked_operation and self.instance.target.has_open_journal():
inst = self.instance
club_account = inst.target
acc_type = AccountingType.objects.exclude(movement_type="NEUTRAL").exclude(
movement_type=inst.accounting_type.movement_type).order_by('code').first() # Select a random opposite accounting type
2016-08-24 17:50:22 +00:00
op = Operation(
journal=club_account.get_open_journal(),
amount=inst.amount,
date=inst.date,
remark=inst.remark,
mode=inst.mode,
cheque_number=inst.cheque_number,
invoice=inst.invoice,
done=False, # Has to be checked by hand
simpleaccounting_type=None,
accounting_type=acc_type,
target_type="ACCOUNT",
target_id=inst.journal.club_account.id,
target_label="",
linked_operation=inst,
)
op.save()
self.instance.linked_operation = op
self.save()
return ret
class OperationCreateView(CanCreateMixin, CreateView):
2016-05-03 06:50:54 +00:00
"""
Create an operation
"""
model = Operation
2016-08-24 17:50:22 +00:00
form_class = OperationForm
template_name = 'accounting/operation_edit.jinja'
2016-06-24 17:43:11 +00:00
2016-10-05 18:17:37 +00:00
def get_form(self, form_class=None):
self.journal = GeneralJournal.objects.filter(id=self.kwargs['j_id']).first()
ca = self.journal.club_account if self.journal else None
return self.form_class(club_account=ca, **self.get_form_kwargs())
2016-06-24 17:43:11 +00:00
def get_initial(self):
ret = super(OperationCreateView, self).get_initial()
2016-10-05 18:17:37 +00:00
if self.journal is not None:
ret['journal'] = self.journal.id
2016-06-24 17:43:11 +00:00
return ret
2016-05-03 06:50:54 +00:00
2016-08-24 19:49:46 +00:00
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(OperationCreateView, self).get_context_data(**kwargs)
if self.journal:
kwargs['object'] = self.journal
return kwargs
2016-06-24 19:55:52 +00:00
class OperationEditView(CanEditMixin, UpdateView):
2016-05-03 06:50:54 +00:00
"""
An edit view, working as detail for the moment
"""
model = Operation
pk_url_kwarg = "op_id"
2016-08-24 17:50:22 +00:00
form_class = OperationForm
template_name = 'accounting/operation_edit.jinja'
2016-08-07 18:10:50 +00:00
2016-08-24 19:49:46 +00:00
def get_context_data(self, **kwargs):
""" Add journal to the context """
2016-08-29 18:13:53 +00:00
kwargs = super(OperationEditView, self).get_context_data(**kwargs)
2016-08-24 19:49:46 +00:00
kwargs['object'] = self.object.journal
return kwargs
2016-11-07 00:51:02 +00:00
class OperationPDFView(CanViewMixin, DetailView):
"""
Display the PDF of a given operation
"""
model = Operation
pk_url_kwarg = "op_id"
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.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
pdfmetrics.registerFont(TTFont('DejaVu', 'DejaVuSerif.ttf'))
self.object = self.get_object()
amount = self.object.amount
remark = self.object.remark
nature = self.object.accounting_type.movement_type
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
club_address = self.object.journal.club_account.club.address
id_op = self.object.id
if self.object.target_type == "OTHER":
target = self.object.target_label
else:
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)
2016-11-07 00:51:02 +00:00
p = canvas.Canvas(response)
p.setFont('DejaVu', 12)
p.setTitle("%s %d" % (_("Operation"), num))
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)
2016-12-21 03:58:52 +00:00
2016-11-07 00:51:02 +00:00
labelStr = [["%s %s - %s %s" % (_("Journal"), ti, _("Operation"), num)]]
label = Table(labelStr, colWidths=[150], rowHeights=[20])
2016-12-21 03:58:52 +00:00
2016-11-07 00:51:02 +00:00
label.setStyle(TableStyle([
('ALIGN',(0,0),(-1,-1),'RIGHT'),
2016-11-07 00:51:02 +00:00
]))
w, h = label.wrapOn(label, 0, 0)
label.drawOn(p, width-180, height)
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 ""})
2016-12-18 22:11:23 +00:00
p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date})
2016-11-07 00:51:02 +00:00
data = []
2016-12-21 03:58:52 +00:00
2016-11-07 00:51:02 +00:00
data += [["%s" % (_("Credit").upper() if nature == 'CREDIT' else _("Debit").upper())]]
data += [[_("Amount: %(amount).2f") % {"amount": amount}]]
2016-12-21 03:58:52 +00:00
2016-11-07 00:51:02 +00:00
payment_mode = ""
for m in settings.SITH_ACCOUNTING_PAYMENT_METHOD:
if m[0] == mode:
payment_mode += "[\u00D7]"
else:
payment_mode += "[ ]"
payment_mode += " %s\n" %(m[1])
data += [[payment_mode]]
2016-12-21 03:58:52 +00:00
2016-11-07 00:51:02 +00:00
data += [["%s : %s" % (_("Debtor") if nature == 'CREDIT' else _("Creditor"), target), ""]]
data += [["%s \n%s" % (_("Comment:"), remark)]]
2016-12-21 03:58:52 +00:00
2016-11-07 00:51:02 +00:00
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),
('SPAN', (0, 0), (1, 0)), # line DEBIT/CREDIT
('SPAN', (0, 1), (1, 1)), # line amount
('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', (-2, -1), (-1, -1), 'LEFT'),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
]))
2016-12-18 22:11:23 +00:00
signature = []
signature += [[_("Signature:")]]
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)]))
w, h = tSig.wrapOn(p, 0, 0)
tSig.drawOn(p, 90, 200)
2016-11-07 00:51:02 +00:00
w, h = t.wrapOn(p, 0, 0)
t.drawOn(p, 90, 350)
p.drawCentredString(10.5 * cm, 2 * cm, club_name)
p.drawCentredString(10.5 * cm, 1 * cm, club_address)
p.showPage()
p.save()
return response
2016-12-21 04:18:02 +00:00
class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
2016-12-21 03:58:52 +00:00
Display a statement sorted by labels
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
2016-12-21 04:18:02 +00:00
template_name='accounting/journal_statement_nature.jinja'
current_tab='nature_statement'
2016-12-21 03:58:52 +00:00
def statement(self, queryset, movement_type):
ret = collections.OrderedDict()
statement = collections.OrderedDict()
total_sum = 0
2016-12-21 12:09:40 +00:00
for sat in [None] + list(SimplifiedAccountingType.objects.order_by('label').all()):
2016-12-21 03:58:52 +00:00
sum = queryset.filter(accounting_type__movement_type=movement_type,
2016-12-21 12:09:40 +00:00
simpleaccounting_type=sat).aggregate(amount_sum=Sum('amount'))['amount_sum']
if sat: sat = sat.label
else: sat = ""
2016-12-21 03:58:52 +00:00
if sum:
total_sum += sum
2016-12-21 12:09:40 +00:00
statement[sat] = sum
2016-12-21 03:58:52 +00:00
ret[movement_type] = statement
ret[movement_type+"_sum"] = total_sum
return ret
2016-12-14 17:04:57 +00:00
2016-12-21 03:58:52 +00:00
def big_statement(self):
label_list = self.object.operations.order_by('label').values_list('label').distinct()
labels = Label.objects.filter(id__in=label_list).all()
statement = collections.OrderedDict()
gen_statement = collections.OrderedDict()
no_label_statement = collections.OrderedDict()
gen_statement.update(self.statement(self.object.operations.all(), "CREDIT"))
gen_statement.update(self.statement(self.object.operations.all(), "DEBIT"))
statement[_("General statement")] = gen_statement
no_label_statement.update(self.statement(self.object.operations.filter(label=None).all(), "CREDIT"))
no_label_statement.update(self.statement(self.object.operations.filter(label=None).all(), "DEBIT"))
statement[_("No label operations")] = no_label_statement
for l in labels:
l_stmt = collections.OrderedDict()
l_stmt.update(self.statement(self.object.operations.filter(label=l).all(), "CREDIT"))
l_stmt.update(self.statement(self.object.operations.filter(label=l).all(), "DEBIT"))
statement[l] = l_stmt
return statement
2016-12-14 17:04:57 +00:00
def get_context_data(self, **kwargs):
2016-12-21 03:58:52 +00:00
""" Add infos to the context """
2016-12-21 04:18:02 +00:00
kwargs = super(JournalNatureStatementView, self).get_context_data(**kwargs)
2016-12-21 03:58:52 +00:00
kwargs['statement'] = self.big_statement()
return kwargs
2016-12-21 04:18:02 +00:00
class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
2016-12-14 17:04:57 +00:00
"""
Calculate a dictionary with operation target and sum of operations
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
2016-12-21 04:18:02 +00:00
template_name='accounting/journal_statement_person.jinja'
current_tab='person_statement'
2016-12-14 17:04:57 +00:00
def sum_by_target(self, target_id, target_type, movement_type):
return self.object.operations.filter(accounting_type__movement_type=movement_type,
target_id=target_id, target_type=target_type).aggregate(amount_sum=Sum('amount'))['amount_sum']
2016-12-14 17:04:57 +00:00
def statement(self, movement_type):
2016-12-21 12:09:40 +00:00
statement = collections.OrderedDict()
for op in self.object.operations.filter(accounting_type__movement_type=movement_type).order_by('target_type',
'target_id').distinct():
statement[op.target] = self.sum_by_target(op.target_id, op.target_type, movement_type)
2016-12-21 03:58:52 +00:00
return statement
2016-12-14 17:04:57 +00:00
def total(self, movement_type):
return sum(self.statement(movement_type).values())
2016-12-14 17:04:57 +00:00
def get_context_data(self, **kwargs):
""" Add journal to the context """
2016-12-21 04:18:02 +00:00
kwargs = super(JournalPersonStatementView, self).get_context_data(**kwargs)
kwargs['credit_statement'] = self.statement("CREDIT")
kwargs['debit_statement'] = self.statement("DEBIT")
kwargs['total_credit'] = self.total("CREDIT")
kwargs['total_debit'] = self.total("DEBIT")
2016-12-14 17:04:57 +00:00
return kwargs
2016-12-21 04:18:02 +00:00
class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView):
2016-12-14 17:04:57 +00:00
"""
Calculate a dictionary with operation type and sum of operations
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
2016-12-21 04:18:02 +00:00
template_name='accounting/journal_statement_accounting.jinja'
current_tab = "accounting_statement"
2016-12-14 17:04:57 +00:00
2016-12-21 03:58:52 +00:00
def statement(self):
statement = collections.OrderedDict()
2016-12-20 23:43:54 +00:00
for at in AccountingType.objects.order_by('code').all():
2016-12-21 03:58:52 +00:00
sum_by_type = self.object.operations.filter(
accounting_type__code__startswith=at.code).aggregate(amount_sum=Sum('amount'))['amount_sum']
2016-12-21 12:09:40 +00:00
if sum_by_type:
2016-12-21 03:58:52 +00:00
statement[at] = sum_by_type
return statement
2016-12-14 17:04:57 +00:00
def get_context_data(self, **kwargs):
""" Add journal to the context """
2016-12-21 04:18:02 +00:00
kwargs = super(JournalAccountingStatementView, self).get_context_data(**kwargs)
2016-12-21 03:58:52 +00:00
kwargs['statement'] = self.statement()
2016-12-14 17:04:57 +00:00
return kwargs
2016-08-07 18:10:50 +00:00
# Company views
2016-12-19 20:58:40 +00:00
class CompanyListView(CanViewMixin, ListView):
model = Company
template_name = 'accounting/co_list.jinja'
2016-08-07 18:10:50 +00:00
class CompanyCreateView(CanCreateMixin, CreateView):
"""
Create a company
"""
model = Company
fields = ['name']
template_name = 'core/create.jinja'
2016-12-19 20:58:40 +00:00
success_url = reverse_lazy('accounting:co_list')
2016-08-07 18:10:50 +00:00
class CompanyEditView(CanCreateMixin, UpdateView):
"""
Edit a company
"""
model = Company
pk_url_kwarg = "co_id"
fields = ['name']
2016-06-24 17:43:11 +00:00
template_name = 'core/edit.jinja'
2016-12-19 20:58:40 +00:00
success_url = reverse_lazy('accounting:co_list')
2016-01-28 15:53:37 +00:00
2016-10-05 13:54:00 +00:00
# Label views
class LabelListView(CanViewMixin, DetailView):
model = ClubAccount
pk_url_kwarg = "clubaccount_id"
template_name = 'accounting/label_list.jinja'
2016-12-21 03:58:52 +00:00
class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the rights before creating the object
2016-10-05 13:54:00 +00:00
model = Label
form_class = modelform_factory(Label, fields=['name', 'club_account'], widgets={
'club_account': HiddenInput,
})
template_name = 'core/create.jinja'
def get_initial(self):
ret = super(LabelCreateView, self).get_initial()
if 'parent' in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET['parent'])).first()
if obj is not None:
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"
template_name = 'core/delete_confirm.jinja'
def get_success_url(self):
return self.object.get_absolute_url()
2016-11-30 01:41:25 +00:00
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
"""
template_name = "accounting/refound_account.jinja"
form_class = CloseCustomerAccountForm
def permission(self, user):
2016-12-10 00:29:56 +00:00
if user.is_root or user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
2016-11-30 01:41:25 +00:00
return True
else:
raise PermissionDenied
def dispatch(self, request, *arg, **kwargs):
res = super(RefoundAccountView, self).dispatch(request, *arg, **kwargs)
if self.permission(request.user):
return res
def post(self, request, *arg, **kwargs):
self.operator = request.user
if self.permission(request.user):
return super(RefoundAccountView, self).post(self, request, *arg, **kwargs)
def form_valid(self, form):
self.customer = form.cleaned_data['user']
self.create_selling()
return super(RefoundAccountView, self).form_valid(form)
def get_success_url(self):
return reverse('accounting:refound_account')
def create_selling(self):
with transaction.atomic():
uprice = self.customer.customer.amount
2016-12-15 11:17:19 +00:00
refound_club_counter = Counter.objects.get(id=settings.SITH_COUNTER_REFOUND_ID)
refound_club = refound_club_counter.club
2016-11-30 01:41:25 +00:00
s = Selling(label=_('Refound account'), unit_price=uprice,
quantity=1, seller=self.operator,
customer=self.customer.customer,
2016-12-15 11:17:19 +00:00
club=refound_club, counter=refound_club_counter,
product=Product.objects.get(id=settings.SITH_PRODUCT_REFOUND_ID))
2016-11-30 01:41:25 +00:00
s.save()