Facturation: ajouter des liens vers les objets depuis la page des lignes non facturées (#75100) #34

Merged
lguerin merged 3 commits from wip/75100-invoicing-non-invoiced-lines-links into main 2023-03-23 16:47:20 +01:00
8 changed files with 565 additions and 504 deletions

View File

@ -18,6 +18,7 @@ import copy
import sys
import traceback
from django.conf import settings
from django.contrib.auth.models import Group
from django.core.serializers.json import DjangoJSONEncoder
from django.db import connection, models, transaction
@ -479,3 +480,14 @@ class InvoiceLine(AbstractInvoiceLine):
],
blank=True,
)
def get_chrono_event_url(self):
if not settings.KNOWN_SERVICES.get('chrono'):
return
chrono = list(settings.KNOWN_SERVICES['chrono'].values())[0]
chrono_url = chrono.get('url')
lguerin marked this conversation as resolved
Review

Pas compris pourquoi on cherche quand même à retrouver l’URL de l’événement chrono lorsque chrono.get('url') est évalué à False et qu’on se retrouve avec une chaîne vide à la place. C’est quoi le cas qui justifierait cela et dans lequel on ne se retrouverait pas avec une URL erronée au final ?

Pas compris pourquoi on cherche quand même à retrouver l’URL de l’événement chrono lorsque `chrono.get('url')` est évalué à `False` et qu’on se retrouve avec une chaîne vide à la place. C’est quoi le cas qui justifierait cela et dans lequel on ne se retrouverait pas avec une URL erronée au final ?
Review

En effet, ça doit etre un copier/coller d'ailleurs, je vais changer ça

En effet, ça doit etre un copier/coller d'ailleurs, je vais changer ça
if not chrono_url:
return
if not self.event.get('agenda') or not self.event.get('slug'):
return
return '%smanage/agendas/%s/events/%s/' % (chrono_url, self.event['agenda'], self.event['slug'])

View File

@ -46,4 +46,6 @@
{% endif %}
{% if line.from_injected_line_id %}({% trans "Injected" %}){% endif %}
</td>
<td><a class="details-toggle" href="#">{% trans "see details" %}</a></td>
<td>
<span class="togglable"></span>
Review

Ok, pas très à l’aise avec le bout de JS qui vient remplir ces <span class="toggable"/> d’abord vides dans les gabarits, alors je vais tenter de recopier les données du test écrit dans un shell django, et constater le résultat.

Ok, pas très à l’aise avec le bout de JS qui vient remplir ces `<span class="toggable"/>` d’abord vides dans les gabarits, alors je vais tenter de recopier les données du test écrit dans un shell django, et constater le résultat.
Review

Ok, testé en local, c’est bon pour moi.

Ok, testé en local, c’est bon pour moi.
</td>

View File

@ -27,12 +27,17 @@
</thead>
<tbody>
{% for line in object_list %}
<tr data-line-id="{% if line.status == 'injected' %}injected{% else %}line{% endif %}-{{ line.pk }}">
<tr data-line-id="{% if line.status == 'injected' %}injected{% else %}line{% endif %}-{{ line.pk }}" class="untoggled">
<td>
{{ line.event_date|date:"d/m/Y" }} - {{ line.label }}
<br />
{% if line.status == 'error'%}
<a href="{% url 'lingo-manager-invoicing-pool-journal' regie_pk=regie.pk pk=line.campaign_id pool_pk=line.pool_id %}?pk={{ line.pk }}">({{ line.slug }})</a>
{% if line.event.agenda %}
<br />
<a href="{% url 'lingo-manager-agenda-detail-redirect' line.event.agenda %}">{% trans "see agenda" %}</a>
{% if line.chrono_event_url %}- <a href="{{ line.chrono_event_url }}">{% trans "see event" %}</a>{% endif %}
Review

Juste par curiosité, c’est dans quel cas qu’on est en mesure de rediriger vers lingo-manager-agenda-detail mais pas en mesure de retrouver une URL d’événement correspondant côté chrono ?
C’est pour tâcher d’avoir un fonctionnement un minimum standalone dans lingo, ou bien il y a vraiment des cas où ça survient ?

Juste par curiosité, c’est dans quel cas qu’on est en mesure de rediriger vers `lingo-manager-agenda-detail` mais pas en mesure de retrouver une URL d’événement correspondant côté chrono ? C’est pour tâcher d’avoir un fonctionnement un minimum standalone dans lingo, ou bien il y a vraiment des cas où ça survient ?
Review

Non en effet ça ne devrait pas arriver, si on a pu lancer une campagne de facturation, c'est qu'on a pu synchroniser les agendas chronos; aussi, dans les lignes de facturation, on doit retrouver dans le json event le slug de l'event, et on doit être en mesure d'avoir une url.
Ce ne sont que des précautions :)

Non en effet ça ne devrait pas arriver, si on a pu lancer une campagne de facturation, c'est qu'on a pu synchroniser les agendas chronos; aussi, dans les lignes de facturation, on doit retrouver dans le json `event` le slug de l'event, et on doit être en mesure d'avoir une url. Ce ne sont que des précautions :)
Review

Ok, c’est ce qu’il me semblait, merci.

Ok, c’est ce qu’il me semblait, merci.
{% endif %}
{% else %}
({{ line.slug }})
{% endif %}
@ -54,7 +59,11 @@
({{ line.error_display }})
{% endif %}
</td>
<td>{% if line.status != 'injected' %}<a class="details-toggle" href="#">{% trans "see details" %}</a>{% endif %}</td>
<td>
{% if line.status != 'injected' %}
<span class="togglable"></span>
{% endif %}
</td>
</tr>
{% if line.status != 'injected' %}
<tr data-details-for-line-id="line-{{ line.pk }}" style="display: none">
@ -71,20 +80,4 @@
</table>
{% include "gadjo/pagination.html" %}
</div>
<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')) {
$(this).text('{% trans "see details" %}');
$details.hide();
} else {
$(this).text('{% trans "hide details" %}');
$details.show();
}
});
});
</script>
{% endblock %}

View File

@ -65,7 +65,7 @@
</thead>
<tbody>
{% for line in object_list %}
<tr data-line-id="{{ line.pk }}">
<tr data-line-id="{{ line.pk }}" class="untoggled">
{% include 'lingo/invoicing/manager_line_detail_fragment.html' %}
</tr>
<tr data-details-for-line-id="{{ line.pk }}" style="display: none">
@ -83,18 +83,6 @@
</div>
<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')) {
$(this).text('{% trans "see details" %}');
$details.hide();
} else {
$(this).text('{% trans "hide details" %}');
$details.show();
}
});
$(document).on('click', 'a.error-status', function(event) {
// avoid reloading the whole page. There is no refresh of counters, it is not so important.
event.preventDefault();

View File

@ -678,6 +678,7 @@ class NonInvoicedLineListView(ListView):
status=line['status'], pricing_data=line['pricing_data']
).get_error_display()
line['campaign_id'] = pools[line['pool_id']].campaign_id
line['chrono_event_url'] = InvoiceLine(event=line['event']).get_chrono_event_url()
return context

View File

@ -51,16 +51,21 @@ $(function() {
$(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();
}
$li.toggleClass('toggled').toggleClass('untoggled');
$('li.line[data-invoice-id="' + invoice_id + '"]').toggle();
}
});
$(document).on('click', '.lines .togglable', function(event) {
event.preventDefault();
var $toggle = $(this);
var $tr = $toggle.parents('tr');
var line_id = $tr.data('line-id');
var $details = $('tr[data-details-for-line-id=' + line_id + ']');
$tr.toggleClass('toggled').toggleClass('untoggled');
Review

Typiquement le genre de ligne où je comprends rien :)
Même en lisant la doc JQuery c’est pas plus clair.
Je vais me contenter d’injecter des données dans mon devinst et tâcher de reproduire le comportement de repliabilité/dépliabilité, ça vaudra pour relecture du code…

Typiquement le genre de ligne où je comprends rien :) Même en lisant la doc JQuery c’est pas plus clair. Je vais me contenter d’injecter des données dans mon devinst et tâcher de reproduire le comportement de repliabilité/dépliabilité, ça vaudra pour relecture du code…
$details.toggle();
});
/* focus tab from #open:<tab slug> anchor, to point to open panel */
if (document.location.hash && document.location.hash.indexOf('#open:') == 0) {
const $tab_button = $('#tab-' + document.location.hash.substring(6) + '[role=tab]');

View File

@ -1800,467 +1800,3 @@ def test_set_error_status_line(app, admin_user):
% (regie.pk, campaign.pk, pool.pk, 0),
status=404,
)
def test_non_invoiced_line_list(app, admin_user):
regie = Regie.objects.create(label='Regie')
other_regie = Regie.objects.create(label='Other Regie')
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool = Pool.objects.create(
campaign=campaign,
draft=True,
)
campaign2 = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool2 = Pool.objects.create(
campaign=campaign2,
draft=False,
)
other_campaign = Campaign.objects.create(
regie=other_regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
other_pool = Pool.objects.create(
campaign=other_campaign,
draft=True,
)
other_campaign2 = Campaign.objects.create(
regie=other_regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
other_pool2 = Pool.objects.create(
campaign=other_campaign2,
draft=False,
)
# not invoiced
InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 1),
slug='event-2022-09-01',
label='Event 2022-09-01',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=regie,
)
# not invoiced but in another regie
InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 1),
slug='other-event-2022-09-01',
label='Other Event 2022-09-01',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=other_regie,
)
# not invoiced, but linked in a DraftInvoiceLine
injected_line2 = InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='event-2022-09-02',
label='Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=regie,
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='event-2022-09-02',
label='Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
pool=pool,
from_injected_line=injected_line2,
)
# not invoiced, but linked in a DraftInvoiceLine, but in another regie
other_injected_line2 = InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='other-event-2022-09-02',
label='Other Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=other_regie,
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='other-event-2022-09-02',
label='Other Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
pool=other_pool,
from_injected_line=other_injected_line2,
)
# invoiced, as linked in a non draft pool
injected_line3 = InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 3),
slug='event-2022-09-03',
label='Event 2022-09-03',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=regie,
)
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 3),
slug='event-2022-09-03',
label='Event 2022-09-03',
quantity=2,
unit_amount=1.5,
total_amount=3,
pool=pool2,
from_injected_line=injected_line3,
)
# non fixed error
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 4),
slug='event-2022-09-04',
label='Event 2022-09-04',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='error',
)
# non fixed error, but in another regie
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 4),
slug='other-event-2022-09-04',
label='Other Event 2022-09-04',
quantity=0,
unit_amount=0,
total_amount=0,
pool=other_pool2,
status='error',
)
# fixed or ignored errors
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 5),
slug='event-2022-09-05',
label='Event 2022-09-05',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='error',
error_status='fixed',
)
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 6),
slug='event-2022-09-06',
label='Event 2022-09-06',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='error',
error_status='ignored',
)
# not errors
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 7),
slug='event-2022-09-07',
label='Event 2022-09-07',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='success',
)
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 8),
slug='event-2022-09-08',
label='Event 2022-09-08',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='warning',
)
app = login(app)
resp = app.get('/manage/invoicing/regie/%s/non-invoiced-lines/' % regie.pk)
assert 'event-2022-09-01' in resp
assert 'other-event-2022-09-01' not in resp
assert 'event-2022-09-02' in resp
assert 'other-event-2022-09-02' not in resp
assert 'event-2022-09-03' not in resp
assert 'event-2022-09-04' in resp
assert 'other-event-2022-09-04' not in resp
assert 'event-2022-09-05' not in resp
assert 'event-2022-09-06' not in resp
assert 'event-2022-09-07' not in resp
assert 'event-2022-09-08' not in resp
def test_regie_invoices(app, admin_user):
regie = Regie.objects.create(label='Foo')
campaign1 = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool1 = Pool.objects.create(
campaign=campaign1,
draft=False,
status='completed',
)
campaign2 = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool2 = Pool.objects.create(
campaign=campaign2,
draft=False,
status='completed',
)
invoice1 = Invoice.objects.create(
date_issue=datetime.date.today(),
regie=regie,
pool=pool1,
payer_external_id='payer:1',
payer_first_name='First1',
payer_last_name='Name1',
payer_demat=True,
payer_direct_debit=False,
)
invoice1.set_number()
invoice1.save()
invoice2 = Invoice.objects.create(
date_issue=datetime.date.today(),
regie=regie,
pool=pool2,
payer_external_id='payer:2',
payer_first_name='First2',
payer_last_name='Name2',
payer_demat=False,
payer_direct_debit=True,
)
invoice2.set_number()
invoice2.save()
line11 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
invoice=invoice1,
quantity=1,
unit_amount=1,
total_amount=1,
status='success',
pool=pool1,
label='Label 11',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
line12 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 2),
invoice=invoice1,
quantity=1,
unit_amount=2,
total_amount=2,
status='success',
pool=pool1,
label='Label 12',
user_external_id='user:2',
user_first_name='User2',
user_last_name='Name2',
)
line13 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 3),
invoice=invoice1,
quantity=1,
unit_amount=3,
total_amount=3,
status='success',
pool=pool1,
label='Label 13',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
orphan_line = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
quantity=1,
unit_amount=42,
total_amount=42,
status='failed',
pool=pool1,
label='Label 14',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
line21 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
invoice=invoice2,
quantity=1,
unit_amount=1,
total_amount=1,
status='success',
pool=pool2,
label='Label 21',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
app = login(app)
resp = app.get('/manage/invoicing/regie/%s/invoices/' % regie.pk)
assert '#%s' % orphan_line.pk not in resp
assert resp.pyquery(
'li[data-invoice-id="%s"]' % invoice1.pk
).text() == 'Invoice F%02s-%s-0000001 addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no' % (
regie.pk,
invoice1.created_at.strftime('%y-%m'),
)
lines_url = resp.pyquery('li[data-invoice-id="%s"]' % invoice1.pk).attr('data-invoice-lines-url')
assert lines_url == '/manage/invoicing/ajax/regie/%s/invoice/%s/lines/' % (
regie.pk,
invoice1.pk,
)
lines_resp = app.get(lines_url)
assert len(lines_resp.pyquery('li')) == 3
assert (
lines_resp.pyquery('li:nth-child(1)').text()
== '#%s User1 Name1 (user:1) - 01/09/2022 - Label 11 (1.00)' % line11.pk
)
assert (
lines_resp.pyquery('li:nth-child(2)').text()
== '#%s User1 Name1 (user:1) - 03/09/2022 - Label 13 (3.00)' % line13.pk
)
assert (
lines_resp.pyquery('li:nth-child(3)').text()
== '#%s User2 Name2 (user:2) - 02/09/2022 - Label 12 (2.00)' % line12.pk
)
assert resp.pyquery(
'li[data-invoice-id="%s"]' % invoice2.pk
).text() == 'Invoice F%02d-%s-0000002 addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes' % (
regie.pk,
invoice2.created_at.strftime('%y-%m'),
)
lines_url = resp.pyquery('li[data-invoice-id="%s"]' % invoice2.pk).attr('data-invoice-lines-url')
assert lines_url == '/manage/invoicing/ajax/regie/%s/invoice/%s/lines/' % (
regie.pk,
invoice2.pk,
)
lines_resp = app.get(lines_url)
assert len(lines_resp.pyquery('li')) == 1
assert (
lines_resp.pyquery('li:nth-child(1)').text()
== '#%s User1 Name1 (user:1) - 01/09/2022 - Label 21 (1.00)' % line21.pk
)
# test filters
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'number': invoice1.formatted_number},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'number': invoice1.created_at.strftime('%y-%m')},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_external_id': 'payer:1'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_external_id': 'payer:2'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_first_name': 'first'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_first_name': 'first1'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_last_name': 'name'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_last_name': 'name1'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_demat': True},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_direct_debit': True},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_external_id': 'user:1'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_external_id': 'user:2'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_first_name': 'user'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_first_name': 'user2'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_last_name': 'name'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_last_name': 'name1'},
)
assert len(resp.pyquery('li.invoice')) == 2

View File

@ -8,7 +8,16 @@ from django.urls import reverse
from webtest import Upload
from lingo.agendas.models import Agenda
from lingo.invoicing.models import Campaign, Counter, InjectedLine, Regie
from lingo.invoicing.models import (
Campaign,
Counter,
DraftInvoiceLine,
InjectedLine,
Invoice,
InvoiceLine,
Pool,
Regie,
)
from tests.utils import login
pytestmark = pytest.mark.django_db
@ -197,3 +206,518 @@ def test_manager_invoicing_regie_import_export(app, admin_user, freezer):
assert urlparse(response.request.url).path == reverse('lingo-manager-invoicing-regie-list')
assert 'A regie was created. A regie was updated.' in response.text
assert Regie.objects.count() == 2
def test_non_invoiced_line_list(app, admin_user):
regie = Regie.objects.create(label='Regie')
other_regie = Regie.objects.create(label='Other Regie')
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool = Pool.objects.create(
campaign=campaign,
draft=True,
)
campaign2 = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool2 = Pool.objects.create(
campaign=campaign2,
draft=False,
)
other_campaign = Campaign.objects.create(
regie=other_regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
other_pool = Pool.objects.create(
campaign=other_campaign,
draft=True,
)
other_campaign2 = Campaign.objects.create(
regie=other_regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
other_pool2 = Pool.objects.create(
campaign=other_campaign2,
draft=False,
)
# not invoiced
InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 1),
slug='event-2022-09-01',
label='Event 2022-09-01',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=regie,
)
# not invoiced but in another regie
InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 1),
slug='other-event-2022-09-01',
label='Other Event 2022-09-01',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=other_regie,
)
# not invoiced, but linked in a DraftInvoiceLine
injected_line2 = InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='event-2022-09-02',
label='Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=regie,
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='event-2022-09-02',
label='Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
pool=pool,
from_injected_line=injected_line2,
)
# not invoiced, but linked in a DraftInvoiceLine, but in another regie
other_injected_line2 = InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='other-event-2022-09-02',
label='Other Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=other_regie,
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 2),
slug='other-event-2022-09-02',
label='Other Event 2022-09-02',
quantity=2,
unit_amount=1.5,
total_amount=3,
pool=other_pool,
from_injected_line=other_injected_line2,
)
# invoiced, as linked in a non draft pool
injected_line3 = InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 3),
slug='event-2022-09-03',
label='Event 2022-09-03',
quantity=2,
unit_amount=1.5,
total_amount=3,
user_external_id='user:1',
payer_external_id='payer:1',
regie=regie,
)
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 3),
slug='event-2022-09-03',
label='Event 2022-09-03',
quantity=2,
unit_amount=1.5,
total_amount=3,
pool=pool2,
from_injected_line=injected_line3,
)
# non fixed error
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 4),
slug='event-2022-09-04',
label='Event 2022-09-04',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='error',
)
# non fixed error, but in another regie
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 4),
slug='other-event-2022-09-04',
label='Other Event 2022-09-04',
quantity=0,
unit_amount=0,
total_amount=0,
pool=other_pool2,
status='error',
)
# fixed or ignored errors
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 5),
slug='event-2022-09-05',
label='Event 2022-09-05',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='error',
error_status='fixed',
)
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 6),
slug='event-2022-09-06',
label='Event 2022-09-06',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='error',
error_status='ignored',
)
# not errors
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 7),
slug='event-2022-09-07',
label='Event 2022-09-07',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='success',
)
InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 8),
slug='event-2022-09-08',
label='Event 2022-09-08',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool2,
status='warning',
)
app = login(app)
resp = app.get('/manage/invoicing/regie/%s/non-invoiced-lines/' % regie.pk)
assert 'event-2022-09-01' in resp
assert 'other-event-2022-09-01' not in resp
assert 'event-2022-09-02' in resp
assert 'other-event-2022-09-02' not in resp
assert 'event-2022-09-03' not in resp
assert 'event-2022-09-04' in resp
assert 'other-event-2022-09-04' not in resp
assert 'event-2022-09-05' not in resp
assert 'event-2022-09-06' not in resp
assert 'event-2022-09-07' not in resp
assert 'event-2022-09-08' not in resp
def test_non_invoiced_line_link(app, admin_user, settings):
settings.KNOWN_SERVICES = {}
regie = Regie.objects.create(label='Regie')
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
line = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 4),
slug='event-2022-09-04',
label='Event 2022-09-04',
quantity=0,
unit_amount=0,
total_amount=0,
pool=pool,
status='error',
)
app = login(app)
resp = app.get('/manage/invoicing/regie/%s/non-invoiced-lines/' % regie.pk)
assert 'see agenda' not in resp
assert 'see event' not in resp
line.event = {
'agenda': 'foobar',
}
line.save()
resp = app.get('/manage/invoicing/regie/%s/non-invoiced-lines/' % regie.pk)
Review

Ok, le test est clair. Nickel.
Juste comme dit plus haut, j’ai du mal à voir ”dans la vraie vie” quand surviendraient les cas où le lien “see agenda” est visible, mais où il manque le nom d’agenda et le slug d’événement nécessaires à la génération du lien correspondant coté chrono.

Ok, le test est clair. Nickel. Juste comme dit plus haut, j’ai du mal à voir ”dans la vraie vie” quand surviendraient les cas où le lien “see agenda” est visible, mais où il manque le nom d’agenda et le slug d’événement nécessaires à la génération du lien correspondant coté chrono.
assert '<a href="/manage/pricing/agenda/foobar/">see agenda</a>' in resp
assert 'see event' not in resp
line.event['slug'] = 'bazbaz'
line.save()
resp = app.get('/manage/invoicing/regie/%s/non-invoiced-lines/' % regie.pk)
assert '<a href="/manage/pricing/agenda/foobar/">see agenda</a>' in resp
assert 'see event' not in resp
settings.KNOWN_SERVICES['chrono'] = {'default': {'url': 'https://chrono.dev/'}}
resp = app.get('/manage/invoicing/regie/%s/non-invoiced-lines/' % regie.pk)
assert '<a href="/manage/pricing/agenda/foobar/">see agenda</a>' in resp
assert '<a href="https://chrono.dev/manage/agendas/foobar/events/bazbaz/">see event</a>' in resp
def test_regie_invoices(app, admin_user):
regie = Regie.objects.create(label='Foo')
campaign1 = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool1 = Pool.objects.create(
campaign=campaign1,
draft=False,
status='completed',
)
campaign2 = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
)
pool2 = Pool.objects.create(
campaign=campaign2,
draft=False,
status='completed',
)
invoice1 = Invoice.objects.create(
date_issue=datetime.date.today(),
regie=regie,
pool=pool1,
payer_external_id='payer:1',
payer_first_name='First1',
payer_last_name='Name1',
payer_demat=True,
payer_direct_debit=False,
)
invoice1.set_number()
invoice1.save()
invoice2 = Invoice.objects.create(
date_issue=datetime.date.today(),
regie=regie,
pool=pool2,
payer_external_id='payer:2',
payer_first_name='First2',
payer_last_name='Name2',
payer_demat=False,
payer_direct_debit=True,
)
invoice2.set_number()
invoice2.save()
line11 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
invoice=invoice1,
quantity=1,
unit_amount=1,
total_amount=1,
status='success',
pool=pool1,
label='Label 11',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
line12 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 2),
invoice=invoice1,
quantity=1,
unit_amount=2,
total_amount=2,
status='success',
pool=pool1,
label='Label 12',
user_external_id='user:2',
user_first_name='User2',
user_last_name='Name2',
)
line13 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 3),
invoice=invoice1,
quantity=1,
unit_amount=3,
total_amount=3,
status='success',
pool=pool1,
label='Label 13',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
orphan_line = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
quantity=1,
unit_amount=42,
total_amount=42,
status='failed',
pool=pool1,
label='Label 14',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
line21 = InvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
invoice=invoice2,
quantity=1,
unit_amount=1,
total_amount=1,
status='success',
pool=pool2,
label='Label 21',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
app = login(app)
resp = app.get('/manage/invoicing/regie/%s/invoices/' % regie.pk)
assert '#%s' % orphan_line.pk not in resp
assert resp.pyquery(
'li[data-invoice-id="%s"]' % invoice1.pk
).text() == 'Invoice F%02s-%s-0000001 addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no' % (
regie.pk,
invoice1.created_at.strftime('%y-%m'),
)
lines_url = resp.pyquery('li[data-invoice-id="%s"]' % invoice1.pk).attr('data-invoice-lines-url')
assert lines_url == '/manage/invoicing/ajax/regie/%s/invoice/%s/lines/' % (
regie.pk,
invoice1.pk,
)
lines_resp = app.get(lines_url)
assert len(lines_resp.pyquery('li')) == 3
assert (
lines_resp.pyquery('li:nth-child(1)').text()
== '#%s User1 Name1 (user:1) - 01/09/2022 - Label 11 (1.00)' % line11.pk
)
assert (
lines_resp.pyquery('li:nth-child(2)').text()
== '#%s User1 Name1 (user:1) - 03/09/2022 - Label 13 (3.00)' % line13.pk
)
assert (
lines_resp.pyquery('li:nth-child(3)').text()
== '#%s User2 Name2 (user:2) - 02/09/2022 - Label 12 (2.00)' % line12.pk
)
assert resp.pyquery(
'li[data-invoice-id="%s"]' % invoice2.pk
).text() == 'Invoice F%02d-%s-0000002 addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes' % (
regie.pk,
invoice2.created_at.strftime('%y-%m'),
)
lines_url = resp.pyquery('li[data-invoice-id="%s"]' % invoice2.pk).attr('data-invoice-lines-url')
assert lines_url == '/manage/invoicing/ajax/regie/%s/invoice/%s/lines/' % (
regie.pk,
invoice2.pk,
)
lines_resp = app.get(lines_url)
assert len(lines_resp.pyquery('li')) == 1
assert (
lines_resp.pyquery('li:nth-child(1)').text()
== '#%s User1 Name1 (user:1) - 01/09/2022 - Label 21 (1.00)' % line21.pk
)
# test filters
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'number': invoice1.formatted_number},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'number': invoice1.created_at.strftime('%y-%m')},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_external_id': 'payer:1'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_external_id': 'payer:2'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_first_name': 'first'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_first_name': 'first1'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_last_name': 'name'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_last_name': 'name1'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_demat': True},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'payer_direct_debit': True},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_external_id': 'user:1'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_external_id': 'user:2'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_first_name': 'user'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_first_name': 'user2'},
)
assert len(resp.pyquery('li.invoice')) == 1
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_last_name': 'name'},
)
assert len(resp.pyquery('li.invoice')) == 2
resp = app.get(
'/manage/invoicing/regie/%s/invoices/' % regie.pk,
params={'user_last_name': 'name1'},
)
assert len(resp.pyquery('li.invoice')) == 2