350 lines
12 KiB
Python
350 lines
12 KiB
Python
# lingo - payment and billing system
|
|
# Copyright (C) 2023 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as 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 Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from django.http import Http404
|
|
from django.shortcuts import get_object_or_404, redirect
|
|
from django.urls import reverse
|
|
from django.views.generic import DeleteView, DetailView, FormView, ListView
|
|
|
|
from lingo.invoicing.forms import (
|
|
DraftInvoiceFilterSet,
|
|
DraftInvoiceLineFilterSet,
|
|
InvoiceFilterSet,
|
|
InvoiceLineFilterSet,
|
|
)
|
|
from lingo.invoicing.models import (
|
|
Campaign,
|
|
DraftInvoice,
|
|
DraftInvoiceLine,
|
|
Invoice,
|
|
InvoiceLine,
|
|
InvoicePayment,
|
|
Pool,
|
|
Regie,
|
|
)
|
|
from lingo.invoicing.views.utils import PDFMixin
|
|
|
|
|
|
def is_ajax(request):
|
|
return request.headers.get('x-requested-with') == 'XMLHttpRequest'
|
|
|
|
|
|
class PoolDetailView(ListView):
|
|
template_name = 'lingo/invoicing/manager_pool_detail.html'
|
|
paginate_by = 100
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
self.campaign = get_object_or_404(Campaign, pk=kwargs['pk'], regie=self.regie)
|
|
self.object = get_object_or_404(Pool, pk=kwargs['pool_pk'], campaign=self.campaign)
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
invoice_model = Invoice
|
|
filter_model = InvoiceFilterSet
|
|
if self.object.draft:
|
|
invoice_model = DraftInvoice
|
|
filter_model = DraftInvoiceFilterSet
|
|
|
|
data = self.request.GET or None
|
|
self.filterset = filter_model(
|
|
data=data,
|
|
queryset=invoice_model.objects.filter(pool=self.object).order_by('created_at'),
|
|
pool=self.object,
|
|
)
|
|
return self.filterset.qs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs['regie'] = self.regie
|
|
kwargs['object'] = self.campaign
|
|
kwargs['pool'] = self.object
|
|
kwargs['filterset'] = self.filterset
|
|
line_model = InvoiceLine
|
|
line_values = ['status', 'error_status']
|
|
if self.object.draft:
|
|
line_model = DraftInvoiceLine
|
|
line_values = ['status']
|
|
all_lines = line_model.objects.filter(pool=self.object).values(*line_values)
|
|
self.object.error_count = len(
|
|
[line for line in all_lines if line['status'] == 'error' and not line.get('error_status')]
|
|
)
|
|
self.object.warning_count = len([line for line in all_lines if line['status'] == 'warning'])
|
|
self.object.success_count = len([line for line in all_lines if line['status'] == 'success'])
|
|
kwargs['has_running_pool'] = any(
|
|
p.status in ['registered', 'running'] for p in self.campaign.pool_set.all()
|
|
)
|
|
return super().get_context_data(**kwargs)
|
|
|
|
|
|
pool_detail = PoolDetailView.as_view()
|
|
|
|
|
|
class PoolJournalView(ListView):
|
|
template_name = 'lingo/invoicing/manager_pool_journal.html'
|
|
paginate_by = 100
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
self.campaign = get_object_or_404(Campaign, pk=kwargs['pk'], regie=self.regie)
|
|
self.object = get_object_or_404(Pool, pk=kwargs['pool_pk'], campaign=self.campaign)
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
line_model = InvoiceLine
|
|
filter_model = InvoiceLineFilterSet
|
|
if self.object.draft:
|
|
line_model = DraftInvoiceLine
|
|
filter_model = DraftInvoiceLineFilterSet
|
|
all_lines = line_model.objects.filter(pool=self.object).order_by('pk').select_related('invoice')
|
|
self.object.error_count = len(
|
|
[line for line in all_lines if line.status == 'error' and not getattr(line, 'error_status', '')]
|
|
)
|
|
self.object.warning_count = len([line for line in all_lines if line.status == 'warning'])
|
|
self.object.success_count = len([line for line in all_lines if line.status == 'success'])
|
|
data = self.request.GET or None
|
|
self.filterset = filter_model(data=data, queryset=all_lines, pool=self.object)
|
|
return self.filterset.qs if data and [v for v in data.values() if v] else all_lines
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs['regie'] = self.regie
|
|
kwargs['object'] = self.campaign
|
|
kwargs['pool'] = self.object
|
|
kwargs['filterset'] = self.filterset
|
|
return super().get_context_data(**kwargs)
|
|
|
|
|
|
pool_journal = PoolJournalView.as_view()
|
|
|
|
|
|
class PoolAddView(FormView):
|
|
template_name = 'lingo/invoicing/manager_pool_add.html'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
self.object = get_object_or_404(
|
|
Campaign.objects.filter(regie=self.regie, finalized=False)
|
|
.exclude(pool__draft=False)
|
|
.exclude(pool__status__in=['registered', 'running']),
|
|
pk=kwargs['pk'],
|
|
)
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs['form'] = None
|
|
kwargs['regie'] = self.regie
|
|
kwargs['object'] = self.object
|
|
return super().get_context_data(**kwargs)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
self.object.mark_as_valid()
|
|
self.object.generate()
|
|
return redirect(
|
|
'%s#open:pools'
|
|
% reverse('lingo-manager-invoicing-campaign-detail', args=[self.regie.pk, self.object.pk])
|
|
)
|
|
|
|
|
|
pool_add = PoolAddView.as_view()
|
|
|
|
|
|
class PoolPromoteView(FormView):
|
|
template_name = 'lingo/invoicing/manager_pool_promote.html'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
self.object = get_object_or_404(
|
|
Pool,
|
|
campaign__id=kwargs['pk'],
|
|
campaign__regie=self.regie,
|
|
campaign__invalid=False,
|
|
campaign__finalized=False,
|
|
pk=kwargs['pool_pk'],
|
|
draft=True,
|
|
status='completed',
|
|
)
|
|
if not self.object.is_last:
|
|
raise Http404
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs['form'] = None
|
|
kwargs['regie'] = self.regie
|
|
kwargs['object'] = self.object.campaign
|
|
kwargs['pool'] = self.object
|
|
return super().get_context_data(**kwargs)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
self.object.promote()
|
|
return redirect(
|
|
'%s#open:pools'
|
|
% reverse(
|
|
'lingo-manager-invoicing-campaign-detail', args=[self.regie.pk, self.object.campaign.pk]
|
|
)
|
|
)
|
|
|
|
|
|
pool_promote = PoolPromoteView.as_view()
|
|
|
|
|
|
class PoolDeleteView(DeleteView):
|
|
template_name = 'lingo/manager_confirm_delete.html'
|
|
model = Pool
|
|
pk_url_kwarg = 'pool_pk'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
self.campaign = get_object_or_404(
|
|
Campaign.objects.filter(regie=self.regie, finalized=False).exclude(
|
|
pool__status__in=['registered', 'running']
|
|
),
|
|
pk=kwargs['pk'],
|
|
)
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return self.campaign.pool_set.all()
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
if self.object.is_last and self.object.draft:
|
|
self.campaign.mark_as_invalid()
|
|
invoice_model = Invoice
|
|
line_model = InvoiceLine
|
|
if self.object.draft:
|
|
invoice_model = DraftInvoice
|
|
line_model = DraftInvoiceLine
|
|
line_model.objects.filter(pool=self.object).delete()
|
|
invoice_model.objects.filter(pool=self.object).delete()
|
|
return super().delete(request, *args, **kwargs)
|
|
|
|
def get_success_url(self):
|
|
return '%s#open:pools' % reverse(
|
|
'lingo-manager-invoicing-campaign-detail', args=[self.regie.pk, self.campaign.pk]
|
|
)
|
|
|
|
|
|
pool_delete = PoolDeleteView.as_view()
|
|
|
|
|
|
class InvoicePDFView(PDFMixin, DetailView):
|
|
pk_url_kwarg = 'invoice_pk'
|
|
model = Invoice
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
self.pool = get_object_or_404(
|
|
Pool, pk=kwargs['pool_pk'], campaign=kwargs['pk'], campaign__regie=self.regie
|
|
)
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
invoice_model = Invoice
|
|
if self.pool.draft:
|
|
invoice_model = DraftInvoice
|
|
return invoice_model.objects.filter(pool=self.pool)
|
|
|
|
|
|
invoice_pdf = InvoicePDFView.as_view()
|
|
|
|
|
|
class InvoiceLineListView(ListView):
|
|
template_name = 'lingo/invoicing/manager_invoice_lines.html'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
self.pool = get_object_or_404(
|
|
Pool, pk=kwargs['pool_pk'], campaign_id=kwargs['pk'], campaign__regie=self.regie
|
|
)
|
|
invoice_model = Invoice
|
|
if self.pool.draft:
|
|
invoice_model = DraftInvoice
|
|
self.invoice = get_object_or_404(invoice_model, pk=kwargs['invoice_pk'], pool=self.pool)
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return self.invoice.lines.all().order_by('user_external_id', 'event_date', 'pk')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs['regie'] = self.pool.campaign.regie
|
|
kwargs['object'] = self.pool.campaign
|
|
kwargs['pool'] = self.pool
|
|
if not self.pool.draft:
|
|
kwargs['invoice'] = self.invoice
|
|
kwargs['invoice_payments'] = (
|
|
InvoicePayment.objects.filter(invoice=self.invoice)
|
|
.select_related('payment')
|
|
.order_by('created_at')
|
|
)
|
|
return super().get_context_data(**kwargs)
|
|
|
|
|
|
invoice_line_list = InvoiceLineListView.as_view()
|
|
|
|
|
|
class LineSetErrorStatusView(DetailView):
|
|
model = InvoiceLine
|
|
pk_url_kwarg = 'line_pk'
|
|
template_name = 'lingo/invoicing/manager_line_detail_fragment.html'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return (
|
|
super()
|
|
.get_queryset()
|
|
.filter(
|
|
status='error',
|
|
pool=self.kwargs['pool_pk'],
|
|
pool__campaign=self.kwargs['pk'],
|
|
pool__campaign__regie=self.regie,
|
|
)
|
|
)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs['regie'] = self.regie
|
|
kwargs['object'] = self.object.pool.campaign
|
|
kwargs['pool'] = self.object.pool
|
|
kwargs['line'] = self.object
|
|
return super().get_context_data(**kwargs)
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
error_status = kwargs['status']
|
|
if error_status == 'reset':
|
|
self.object.error_status = ''
|
|
elif error_status == 'ignore':
|
|
self.object.error_status = 'ignored'
|
|
elif error_status == 'fix':
|
|
self.object.error_status = 'fixed'
|
|
else:
|
|
raise Http404
|
|
self.object.save()
|
|
|
|
if is_ajax(self.request):
|
|
context = self.get_context_data(object=self.object)
|
|
return self.render_to_response(context)
|
|
|
|
return redirect(
|
|
reverse(
|
|
'lingo-manager-invoicing-pool-journal', args=[self.regie.pk, kwargs['pk'], kwargs['pool_pk']]
|
|
)
|
|
)
|
|
|
|
|
|
line_set_error_status = LineSetErrorStatusView.as_view()
|