pagination des factures et du journal (#73917) #24

Merged
lguerin merged 3 commits from wip/73917-paginate-invoices into main 2023-02-04 15:51:12 +01:00
8 changed files with 165 additions and 62 deletions

View File

@ -0,0 +1,7 @@
{% for line in object_list %}
<li class="line" data-invoice-id="{{ line.invoice_id }}">
<a href="{{ journal_url }}?pk={{ line.pk }}">#{{ line.pk }}</a>
<a href="{{ journal_url }}?user_external_id={{ line.user_external_id }}">{{ line.user_name }}</a>
- {{ line.event_date|date:"d/m/Y" }} - {{ line.label }} ({{ line.total_amount }})
</li>
{% endfor %}

View File

@ -74,6 +74,7 @@
<script>
$(function() {
$(document).on('click', 'a.details-toggle', function(event) {
event.preventDefault();
var line_id = $(this).parents('tr').data('line-id');
var $details = $('tr[data-details-for-line-id=' + line_id + ']');
if ($details.is(':visible')) {
@ -83,7 +84,6 @@
$(this).text('{% trans "hide details" %}');
$details.show();
}
event.preventDefault();
});
});
</script>

View File

@ -27,24 +27,44 @@
{% block content %}
{% url 'lingo-manager-invoicing-pool-journal' pk=object.pk pool_pk=pool.pk as journal_url %}
<div>
{% for line in lines %}
{% ifchanged line.invoice_id %}
{% if not forloop.first %}</ul>{% endif %}
<h3 data-invoice-id="{{ line.invoice_id }}">
<ul class="objects-list">
{% for invoice in object_list %}
<li class="invoice untoggled" data-invoice-id="{{ invoice.pk }}" data-invoice-lines-url="{% url 'lingo-manager-invoicing-invoice-line-list' pk=object.pk pool_pk=pool.pk invoice_pk=invoice.pk %}">
{% if pool.draft %}
{% blocktrans with number=line.invoice_id payer=line.invoice.payer amount=line.invoice.total_amount %}Invoice <a href="{{ journal_url }}?invoice_id={{ number }}">TMP-{{ number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
{% blocktrans with number=invoice.pk payer=invoice.payer amount=invoice.total_amount %}Invoice <a href="{{ journal_url }}?invoice_id={{ number }}">TMP-{{ number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
{% else %}
{% blocktrans with invoice_number=line.invoice.formatted_number payer=line.invoice.payer amount=line.invoice.total_amount number=line.invoice.number regie_id=line.invoice.regie_id %}Invoice <a href="{{ journal_url }}?invoice_number={{ number }}&regie={{ regie_id }}">{{ invoice_number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
{% blocktrans with invoice_number=invoice.formatted_number payer=invoice.payer amount=invoice.total_amount number=invoice.number regie_id=invoice.regie_id %}Invoice <a href="{{ journal_url }}?invoice_number={{ number }}&regie={{ regie_id }}">{{ invoice_number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
{% endif %}
</h3>
<ul class="objects-list" data-invoice-id="{{ line.invoice_id }}">
{% endifchanged %}
<li>
<a href="{{ journal_url }}?pk={{ line.pk }}">#{{ line.pk }}</a>
<a href="{{ journal_url }}?user_external_id={{ line.user_external_id }}">{{ line.user_name }}</a>
- {{ line.event_date|date:"d/m/Y" }} - {{ line.label }} ({{ line.total_amount }})
</li>
{% if forloop.last %}</ul>{% endif %}
{% endfor %}
<span class="togglable"></span>
</li>
{% endfor %}
</ul>
{% include "gadjo/pagination.html" %}
</div>
<script>
$(function() {
$(document).on('click', '.togglable', function(event) {
event.preventDefault();
var $toggle = $(this);
var $li = $toggle.parents('li');
var invoice_id = $li.data('invoice-id');
if ($('li.line[data-invoice-id="' + invoice_id + '"]').length == 0) {
$.ajax({
url: $li.data('invoice-lines-url')
}).done(function(html) {
$li.toggleClass('toggled').toggleClass('untoggled');
$(html).insertAfter($li);
});
} else {
if ($li.hasClass('toggled')) {
$li.toggleClass('toggled').toggleClass('untoggled');
$('li.line[data-invoice-id="' + invoice_id + '"]').hide();
} else {
$li.toggleClass('toggled').toggleClass('untoggled');
$('li.line[data-invoice-id="' + invoice_id + '"]').show();
}
}
});
});
</script>
{% endblock %}

View File

@ -28,17 +28,26 @@
{% if pool.exception %}<pre>{{ pool.exception }}</pre>{% endif %}
</div>
{% endif %}
<form class="journal-filters">
{{ filterset.form.as_p }}
<script>
$(function() {
$('form.journal-filters input,select').on('change',
function() {
$(this).parents('form').submit();
<div class="section">
<div>
<form class="journal-filters">
<fieldset class="gadjo-foldable gadjo-folded" id="filters">
<legend class="gadjo-foldable-widget">{% trans "Journal Filtering" %}</legend>
<div class="gadjo-folding">
{{ filterset.form.as_p }}
</div>
</fieldset>
<script>
$(function() {
$('form.journal-filters input,select').on('change',
function() {
$(this).parents('form').submit();
});
});
});
</script>
</form>
</script>
</form>
</div>
</div>
<table class="main lines">
<thead>
<tr>
@ -55,7 +64,7 @@
</tr>
</thead>
<tbody>
{% for line in lines %}
{% for line in object_list %}
<tr data-line-id="{{ line.pk }}">
{% include 'lingo/invoicing/manager_line_detail_fragment.html' %}
</tr>
@ -70,6 +79,7 @@
{% endfor %}
</tbody>
</table>
{% include "gadjo/pagination.html" %}
</div>
<script>
$(function() {

View File

@ -89,6 +89,11 @@ urlpatterns = [
views.pool_delete,
name='lingo-manager-invoicing-pool-delete',
),
path(
'ajax/campaign/<int:pk>/pool/<int:pool_pk>/invoice/<int:invoice_pk>/lines/',
views.invoice_line_list,
name='lingo-manager-invoicing-invoice-line-list',
),
path(
'ajax/campaign/<int:pk>/pool/<int:pool_pk>/line/<int:line_pk>/<slug:status>/',
views.line_set_error_status,

View File

@ -45,6 +45,7 @@ from lingo.invoicing.models import (
DraftInvoice,
DraftInvoiceLine,
InjectedLine,
Invoice,
InvoiceLine,
Pool,
Regie,
@ -301,56 +302,51 @@ class CampaignDeleteView(DeleteView):
campaign_delete = CampaignDeleteView.as_view()
class PoolDetailView(DetailView):
class PoolDetailView(ListView):
template_name = 'lingo/invoicing/manager_pool_detail.html'
model = Pool
pk_url_kwarg = 'pool_pk'
paginate_by = 100
def dispatch(self, request, *args, **kwargs):
self.campaign = get_object_or_404(Campaign, pk=kwargs['pk'])
self.object = get_object_or_404(Pool, pk=kwargs['pool_pk'], campaign=self.campaign)
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
return self.campaign.pool_set.all()
invoice_model = Invoice
if self.object.draft:
invoice_model = DraftInvoice
return invoice_model.objects.filter(pool=self.object)
def get_context_data(self, **kwargs):
kwargs['object'] = self.campaign
kwargs['pool'] = self.object
line_model = InvoiceLine
line_values = ['status', 'error_status']
if self.object.draft:
line_model = DraftInvoiceLine
all_lines = line_model.objects.filter(pool=self.object)
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 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'])
kwargs['lines'] = (
all_lines.filter(invoice__isnull=False)
.select_related('invoice')
.order_by('invoice__pk', 'user_external_id', 'pk')
[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'])
return super().get_context_data(**kwargs)
pool_detail = PoolDetailView.as_view()
class PoolJournalView(DetailView):
class PoolJournalView(ListView):
template_name = 'lingo/invoicing/manager_pool_journal.html'
model = Pool
pk_url_kwarg = 'pool_pk'
paginate_by = 100
def dispatch(self, request, *args, **kwargs):
self.campaign = get_object_or_404(Campaign, pk=kwargs['pk'])
self.object = get_object_or_404(Pool, pk=kwargs['pool_pk'], campaign=self.campaign)
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
return self.campaign.pool_set.all()
def get_context_data(self, **kwargs):
kwargs['object'] = self.campaign
kwargs['pool'] = self.object
line_model = InvoiceLine
filter_model = InvoiceLineFilterSet
if self.object.draft:
@ -363,9 +359,13 @@ class PoolJournalView(DetailView):
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
line_filterset = filter_model(data=data, queryset=all_lines, pool=self.object)
kwargs['lines'] = line_filterset.qs if data and [v for v in data.values() if v] else all_lines
kwargs['filterset'] = line_filterset
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['object'] = self.campaign
kwargs['pool'] = self.object
kwargs['filterset'] = self.filterset
return super().get_context_data(**kwargs)
@ -457,6 +457,24 @@ class PoolDeleteView(DeleteView):
pool_delete = PoolDeleteView.as_view()
class InvoiceLineListView(ListView):
template_name = 'lingo/invoicing/manager_invoice_lines.html'
def dispatch(self, request, *args, **kwargs):
pool = get_object_or_404(Pool, pk=kwargs['pool_pk'], campaign_id=kwargs['pk'])
invoice_model = Invoice
if pool.draft:
invoice_model = DraftInvoice
self.invoice = get_object_or_404(invoice_model, pk=kwargs['invoice_pk'], pool=pool)
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
return self.invoice.lines.all().order_by('user_external_id', 'event_date', 'pk')
invoice_line_list = InvoiceLineListView.as_view()
class LineSetErrorStatusView(DetailView):
model = InvoiceLine
pk_url_kwarg = 'line_pk'

View File

@ -128,3 +128,32 @@ form.journal-filters li {
display: inline;
margin-right: 10px;
}
span.togglable {
padding: 1em 2ex;
cursor: pointer;
}
.objects-list span.togglable {
position: absolute;
right: 1ex;
line-height: 2em;
height: 2em;
padding-top: 0;
top: 0.25em;
}
.untoggled span.togglable:after {
font-family: FontAwesome;
content: "\f107"; /* angle-down */
cursor: pointer;
}
.toggled span.togglable:after {
font-family: FontAwesome;
content: "\f106"; /* angle-up */
cursor: pointer;
}
.objects-list li.line {
padding-left: 2em;
}

View File

@ -620,44 +620,58 @@ def test_detail_pool_invoices(app, admin_user, draft):
assert '#%s' % orphan_line.pk not in resp
if draft:
assert (
resp.pyquery('h3[data-invoice-id="%s"]' % invoice1.pk).text()
resp.pyquery('li[data-invoice-id="%s"]' % invoice1.pk).text()
== 'Invoice TMP-%s addressed to payer:1, amount 6.00€' % invoice1.pk
)
else:
assert resp.pyquery(
'h3[data-invoice-id="%s"]' % invoice1.pk
'li[data-invoice-id="%s"]' % invoice1.pk
).text() == 'Invoice F%02s-%s-0000001 addressed to payer:1, amount 6.00€' % (
regie.pk,
invoice1.created_at.strftime('%y-%m'),
)
assert len(resp.pyquery('ul[data-invoice-id="%s"] li' % invoice1.pk)) == 3
lines_url = resp.pyquery('li[data-invoice-id="%s"]' % invoice1.pk).attr('data-invoice-lines-url')
assert lines_url == '/manage/invoicing/ajax/campaign/%s/pool/%s/invoice/%s/lines/' % (
campaign.pk,
pool.pk,
invoice1.pk,
)
lines_resp = app.get(lines_url)
assert len(lines_resp.pyquery('li')) == 3
assert (
resp.pyquery('ul[data-invoice-id="%s"] li:nth-child(1)' % invoice1.pk).text()
lines_resp.pyquery('li:nth-child(1)').text()
== '#%s User1 Name1 - 01/09/2022 - Label 11 (1.00)' % line11.pk
)
assert (
resp.pyquery('ul[data-invoice-id="%s"] li:nth-child(2)' % invoice1.pk).text()
lines_resp.pyquery('li:nth-child(2)').text()
== '#%s User1 Name1 - 03/09/2022 - Label 13 (3.00)' % line13.pk
)
assert (
resp.pyquery('ul[data-invoice-id="%s"] li:nth-child(3)' % invoice1.pk).text()
lines_resp.pyquery('li:nth-child(3)').text()
== '#%s User2 Name2 - 02/09/2022 - Label 12 (2.00)' % line12.pk
)
if draft:
assert (
resp.pyquery('h3[data-invoice-id="%s"]' % invoice2.pk).text()
resp.pyquery('li[data-invoice-id="%s"]' % invoice2.pk).text()
== 'Invoice TMP-%s addressed to payer:2, amount 1.00€' % invoice2.pk
)
else:
assert resp.pyquery(
'h3[data-invoice-id="%s"]' % invoice2.pk
'li[data-invoice-id="%s"]' % invoice2.pk
).text() == 'Invoice F%02d-%s-0000002 addressed to payer:2, amount 1.00€' % (
regie.pk,
invoice2.created_at.strftime('%y-%m'),
)
assert len(resp.pyquery('ul[data-invoice-id="%s"] li' % invoice2.pk)) == 1
lines_url = resp.pyquery('li[data-invoice-id="%s"]' % invoice2.pk).attr('data-invoice-lines-url')
assert lines_url == '/manage/invoicing/ajax/campaign/%s/pool/%s/invoice/%s/lines/' % (
campaign.pk,
pool.pk,
invoice2.pk,
)
lines_resp = app.get(lines_url)
assert len(lines_resp.pyquery('li')) == 1
assert (
resp.pyquery('ul[data-invoice-id="%s"] li:nth-child(1)' % invoice2.pk).text()
lines_resp.pyquery('li:nth-child(1)').text()
== '#%s User1 Name1 - 01/09/2022 - Label 21 (1.00)' % line21.pk
)