invoicing: add number on invoice (#73608)
gitea-wip/lingo/pipeline/pr-main This commit looks good Details

This commit is contained in:
Lauréline Guérin 2023-01-20 17:56:06 +01:00
parent a20d01203d
commit 0a48dcc92e
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 204 additions and 35 deletions

View File

@ -0,0 +1,48 @@
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('invoicing', '0010_event_date'),
]
operations = [
migrations.AddField(
model_name='draftinvoice',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='invoice',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='invoice',
name='number',
field=models.PositiveIntegerField(default=0),
),
migrations.CreateModel(
name='Counter',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('name', models.CharField(max_length=128)),
('value', models.PositiveIntegerField(default=0)),
(
'regie',
models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='invoicing.Regie'),
),
],
options={
'unique_together': {('regie', 'name')},
},
),
]

View File

@ -222,6 +222,9 @@ class Pool(models.Model):
final_invoice.__class__ = Invoice
final_invoice.pk = None
final_invoice.pool = self
final_invoice.number = Counter.get_count(
regie=final_invoice.regie, name=final_invoice.created_at.strftime('%y-%m')
)
final_invoice.save()
for line in invoice.lines.all().order_by('pk'):
@ -256,6 +259,7 @@ class AbstractInvoice(models.Model):
date_issue = models.DateField(_('Issue date'))
regie = models.ForeignKey(Regie, on_delete=models.PROTECT)
payer = models.CharField(_('Payer'), max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
pool = models.ForeignKey(Pool, on_delete=models.PROTECT)
@ -267,8 +271,33 @@ class DraftInvoice(AbstractInvoice):
pass
class Counter(models.Model):
regie = models.ForeignKey(Regie, on_delete=models.PROTECT)
name = models.CharField(max_length=128)
value = models.PositiveIntegerField(default=0)
class Meta:
unique_together = (('regie', 'name'),)
@classmethod
def get_count(cls, regie, name):
queryset = cls.objects.select_for_update()
with transaction.atomic():
counter, dummy = queryset.get_or_create(regie=regie, name=name)
counter.value += 1
counter.save()
return counter.value
class Invoice(AbstractInvoice):
pass
number = models.PositiveIntegerField(default=0)
def format_number(self):
return 'F-%s-%s-%06d' % (
self.regie.slug.upper(),
self.created_at.strftime('%y-%m'),
int(self.number),
)
class InjectedLine(models.Model):

View File

@ -30,7 +30,11 @@
{% ifchanged line.invoice_id %}
{% if not forloop.first %}</ul>{% endif %}
<h3 data-invoice-id="{{ line.invoice_id }}">
{% blocktrans with number=line.invoice_id payer=line.invoice.payer amount=line.invoice.total_amount %}Invoice #{{ number }} addressed to {{ payer }}, amount {{ amount }}€{% endblocktrans %}
{% if pool.draft %}
{% blocktrans with number=line.invoice_id payer=line.invoice.payer amount=line.invoice.total_amount %}Invoice TMP-{{ number }} addressed to {{ payer }}, amount {{ amount }}€{% endblocktrans %}
{% else %}
{% blocktrans with number=line.invoice.format_number payer=line.invoice.payer amount=line.invoice.total_amount %}Invoice {{ number }} addressed to {{ payer }}, amount {{ amount }}€{% endblocktrans %}
{% endif %}
</h3>
<ul class="objects-list" data-invoice-id="{{ line.invoice_id }}">
{% endifchanged %}

View File

@ -3,7 +3,7 @@ from unittest import mock
import pytest
from lingo.invoicing.models import Campaign, DraftInvoice, DraftInvoiceLine, InvoiceLine, Pool, Regie
from lingo.invoicing.models import Campaign, DraftInvoice, DraftInvoiceLine, Invoice, InvoiceLine, Pool, Regie
from tests.utils import login
pytestmark = pytest.mark.django_db
@ -503,7 +503,11 @@ def test_detail_pool(app, admin_user):
assert 'tag-error' not in resp
def test_detail_pool_invoices(app, admin_user):
@pytest.mark.parametrize('draft', [True, False])
def test_detail_pool_invoices(app, admin_user, draft):
invoice_model = DraftInvoice if draft else Invoice
line_model = DraftInvoiceLine if draft else InvoiceLine
campaign = Campaign.objects.create(
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
@ -511,18 +515,23 @@ def test_detail_pool_invoices(app, admin_user):
)
pool = Pool.objects.create(
campaign=campaign,
draft=True,
draft=draft,
status='completed',
)
regie = Regie.objects.create(label='Foo')
invoice1 = DraftInvoice.objects.create(
invoice1 = invoice_model.objects.create(
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:1'
)
invoice2 = DraftInvoice.objects.create(
invoice2 = invoice_model.objects.create(
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:2'
)
if not draft:
invoice1.number = 42
invoice1.save()
invoice2.number = 43
invoice2.save()
line11 = DraftInvoiceLine.objects.create(
line11 = line_model.objects.create(
event_date=datetime.date(2022, 9, 1),
invoice=invoice1,
quantity=1,
@ -534,7 +543,7 @@ def test_detail_pool_invoices(app, admin_user):
user_external_id='user:1',
user_name='User1 Name1',
)
line12 = DraftInvoiceLine.objects.create(
line12 = line_model.objects.create(
event_date=datetime.date(2022, 9, 2),
invoice=invoice1,
quantity=1,
@ -546,7 +555,7 @@ def test_detail_pool_invoices(app, admin_user):
user_external_id='user:2',
user_name='User2 Name2',
)
line13 = DraftInvoiceLine.objects.create(
line13 = line_model.objects.create(
event_date=datetime.date(2022, 9, 3),
invoice=invoice1,
quantity=1,
@ -559,7 +568,7 @@ def test_detail_pool_invoices(app, admin_user):
user_name='User1 Name1',
)
orphan_line = DraftInvoiceLine.objects.create(
orphan_line = line_model.objects.create(
event_date=datetime.date(2022, 9, 1),
quantity=1,
unit_amount=42,
@ -571,7 +580,7 @@ def test_detail_pool_invoices(app, admin_user):
user_name='User1 Name1',
)
line21 = DraftInvoiceLine.objects.create(
line21 = line_model.objects.create(
event_date=datetime.date(2022, 9, 1),
invoice=invoice2,
quantity=1,
@ -587,10 +596,17 @@ def test_detail_pool_invoices(app, admin_user):
app = login(app)
resp = app.get('/manage/invoicing/campaign/%s/pool/%s/' % (campaign.pk, pool.pk))
assert '#%s' % orphan_line.pk not in resp
assert (
resp.pyquery('h3[data-invoice-id="%s"]' % invoice1.pk).text()
== 'Invoice #%s addressed to payer:1, amount 6.00€' % invoice1.pk
)
if draft:
assert (
resp.pyquery('h3[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
).text() == 'Invoice F-FOO-%s-000042 addressed to payer:1, amount 6.00€' % invoice1.created_at.strftime(
'%y-%m'
)
assert len(resp.pyquery('ul[data-invoice-id="%s"] li' % invoice1.pk)) == 3
assert (
resp.pyquery('ul[data-invoice-id="%s"] li:nth-child(1)' % invoice1.pk).text()
@ -604,10 +620,17 @@ def test_detail_pool_invoices(app, admin_user):
resp.pyquery('ul[data-invoice-id="%s"] li:nth-child(3)' % invoice1.pk).text()
== '#%s User2 Name2 - 02/09/2022 - Label 12 (2.00)' % line12.pk
)
assert (
resp.pyquery('h3[data-invoice-id="%s"]' % invoice2.pk).text()
== 'Invoice #%s addressed to payer:2, amount 1.00€' % invoice2.pk
)
if draft:
assert (
resp.pyquery('h3[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
).text() == 'Invoice F-FOO-%s-000043 addressed to payer:2, amount 1.00€' % invoice2.created_at.strftime(
'%y-%m'
)
assert len(resp.pyquery('ul[data-invoice-id="%s"] li' % invoice2.pk)) == 1
assert (
resp.pyquery('ul[data-invoice-id="%s"] li:nth-child(1)' % invoice2.pk).text()

View File

@ -13,6 +13,7 @@ from lingo.agendas.models import Agenda
from lingo.invoicing import utils
from lingo.invoicing.models import (
Campaign,
Counter,
DraftInvoice,
DraftInvoiceLine,
InjectedLine,
@ -1400,7 +1401,9 @@ def test_generate_invoices_cmd():
def test_promote_pool():
regie = Regie.objects.create(label='Regie')
today = datetime.date.today()
regie1 = Regie.objects.create(label='Regie1')
regie2 = Regie.objects.create(label='Regie2')
campaign = Campaign.objects.create(
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
@ -1427,7 +1430,7 @@ def test_promote_pool():
)
invoice1 = DraftInvoice.objects.create(
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:1'
date_issue=datetime.date.today(), regie=regie1, pool=pool, payer='payer:1'
)
line11 = DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1454,7 +1457,7 @@ def test_promote_pool():
total_amount=2,
user_external_id='user:2',
payer_external_id='payer:1',
regie=regie,
regie=regie1,
)
line12 = DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1472,7 +1475,7 @@ def test_promote_pool():
pool=pool,
)
invoice2 = DraftInvoice.objects.create(
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:2'
date_issue=datetime.date.today(), regie=regie1, pool=pool, payer='payer:2'
)
injected_line21 = InjectedLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1483,7 +1486,7 @@ def test_promote_pool():
total_amount=1,
user_external_id='user:2',
payer_external_id='payer:2',
regie=regie,
regie=regie1,
)
line21 = DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1541,8 +1544,12 @@ def test_promote_pool():
pool=pool,
)
invoice3 = DraftInvoice.objects.create(
date_issue=datetime.date.today(), regie=regie2, pool=pool, payer='payer:1'
)
old_invoice = DraftInvoice.objects.create(
date_issue=datetime.date.today(), regie=regie, pool=old_pool, payer='payer:1'
date_issue=datetime.date.today(), regie=regie1, pool=old_pool, payer='payer:1'
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1567,7 +1574,7 @@ def test_promote_pool():
total_amount=2,
user_external_id='user:2',
payer_external_id='payer:1',
regie=regie,
regie=regie1,
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1586,7 +1593,7 @@ def test_promote_pool():
)
other_invoice = DraftInvoice.objects.create(
date_issue=datetime.date.today(), regie=regie, pool=other_pool, payer='payer:1'
date_issue=datetime.date.today(), regie=regie1, pool=other_pool, payer='payer:1'
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1611,7 +1618,7 @@ def test_promote_pool():
total_amount=2,
user_external_id='user:2',
payer_external_id='payer:1',
regie=regie,
regie=regie1,
)
DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
@ -1635,7 +1642,7 @@ def test_promote_pool():
assert Campaign.objects.count() == 2
assert Pool.objects.count() == 3
assert Pool.objects.filter(draft=False).count() == 0
assert DraftInvoice.objects.count() == 4
assert DraftInvoice.objects.count() == 5
assert DraftInvoiceLine.objects.count() == 10
assert Invoice.objects.count() == 0
assert InvoiceLine.objects.count() == 0
@ -1647,11 +1654,13 @@ def test_promote_pool():
assert Campaign.objects.count() == 2
assert Pool.objects.count() == 4
assert Pool.objects.filter(draft=False).count() == 1
assert DraftInvoice.objects.count() == 4
assert DraftInvoice.objects.count() == 5
assert DraftInvoiceLine.objects.count() == 10
assert Invoice.objects.count() == 2
assert Invoice.objects.count() == 3
assert InvoiceLine.objects.count() == 6
assert InjectedLine.objects.count() == 4
assert Counter.objects.get(regie=regie1, name=today.strftime('%y-%m')).value == 2
assert Counter.objects.get(regie=regie2, name=today.strftime('%y-%m')).value == 1
test_counts()
@ -1664,10 +1673,12 @@ def test_promote_pool():
final_invoice1 = Invoice.objects.order_by('pk')[0]
assert final_invoice1.date_issue == invoice1.date_issue
assert final_invoice1.regie == regie
assert final_invoice1.regie == regie1
assert final_invoice1.pool == final_pool
assert final_invoice1.payer == invoice1.payer
assert final_invoice1.total_amount == invoice1.total_amount == 3
assert final_invoice1.number == 1
assert final_invoice1.format_number() == 'F-REGIE1-%s-000001' % today.strftime('%y-%m')
final_line11 = InvoiceLine.objects.order_by('pk')[0]
assert final_line11.event_date == line11.event_date
@ -1705,10 +1716,12 @@ def test_promote_pool():
final_invoice2 = Invoice.objects.order_by('pk')[1]
assert final_invoice2.date_issue == invoice2.date_issue
assert final_invoice2.regie == regie
assert final_invoice2.regie == regie1
assert final_invoice2.pool == final_pool
assert final_invoice2.payer == invoice2.payer
assert final_invoice2.total_amount == invoice2.total_amount == 3
assert final_invoice2.number == 2
assert final_invoice2.format_number() == 'F-REGIE1-%s-000002' % today.strftime('%y-%m')
final_line21 = InvoiceLine.objects.order_by('pk')[2]
assert final_line21.event_date == line21.event_date
@ -1776,6 +1789,15 @@ def test_promote_pool():
assert final_orphan_line2.pool == final_pool
assert final_orphan_line2.from_injected_line is None
final_invoice3 = Invoice.objects.order_by('pk')[2]
assert final_invoice3.date_issue == invoice3.date_issue
assert final_invoice3.regie == regie2
assert final_invoice3.pool == final_pool
assert final_invoice3.payer == invoice3.payer
assert final_invoice3.total_amount == invoice3.total_amount == 0
assert final_invoice3.number == 1
assert final_invoice3.format_number() == 'F-REGIE2-%s-000001' % today.strftime('%y-%m')
with pytest.raises(PoolPromotionError) as excinfo:
old_pool.promote()
assert '%s' % excinfo.value == 'Pool too old'

View File

@ -2,7 +2,16 @@ import datetime
import pytest
from lingo.invoicing.models import Campaign, DraftInvoice, DraftInvoiceLine, Invoice, InvoiceLine, Pool, Regie
from lingo.invoicing.models import (
Campaign,
Counter,
DraftInvoice,
DraftInvoiceLine,
Invoice,
InvoiceLine,
Pool,
Regie,
)
pytestmark = pytest.mark.django_db
@ -151,3 +160,37 @@ def test_invoice_total_amount(draft):
assert invoice.total_amount == 12
invoice2.refresh_from_db()
assert invoice2.total_amount == 0
def test_counter():
regie1 = Regie.objects.create()
regie2 = Regie.objects.create()
assert Counter.get_count(regie=regie1, name='foo') == 1
assert Counter.objects.count() == 1
counter1 = Counter.objects.get(regie=regie1, name='foo')
assert counter1.value == 1
assert Counter.get_count(regie=regie1, name='foo') == 2
counter1.refresh_from_db()
assert counter1.value == 2
assert Counter.get_count(regie=regie1, name='foo') == 3
counter1.refresh_from_db()
assert counter1.value == 3
assert Counter.get_count(regie=regie2, name='foo') == 1
assert Counter.objects.count() == 2
counter1.refresh_from_db()
assert counter1.value == 3
counter2 = Counter.objects.get(regie=regie2, name='foo')
assert counter2.value == 1
assert Counter.get_count(regie=regie2, name='bar') == 1
assert Counter.objects.count() == 3
counter1.refresh_from_db()
assert counter1.value == 3
counter2.refresh_from_db()
assert counter2.value == 1
counter3 = Counter.objects.get(regie=regie2, name='bar')
assert counter3.value == 1