wip/71910-invoicing-train-models (#71910) #2

Closed
lguerin wants to merge 1 commits from wip/71910-invoicing-train-models into wip/71528-generate-draft-invoices
6 changed files with 395 additions and 176 deletions

View File

@ -18,6 +18,7 @@ import datetime
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from lingo.invoicing.models import Campaign
from lingo.invoicing.utils import generate_invoices from lingo.invoicing.utils import generate_invoices
@ -27,6 +28,8 @@ class Command(BaseCommand):
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('date_start') parser.add_argument('date_start')
parser.add_argument('date_end') parser.add_argument('date_end')
parser.add_argument('--date-issue')
parser.add_argument('--draft', action='store_true')
def handle(self, *args, **options): def handle(self, *args, **options):
try: try:
@ -37,8 +40,26 @@ class Command(BaseCommand):
date_end = datetime.datetime.fromisoformat(options['date_end']).date() date_end = datetime.datetime.fromisoformat(options['date_end']).date()
except ValueError: except ValueError:
raise CommandError('Bad value "%s" for date_end' % options['date_end']) raise CommandError('Bad value "%s" for date_end' % options['date_end'])
if options.get('date_issue'):
try:
date_issue = datetime.datetime.fromisoformat(options['date_issue']).date()
except ValueError:
raise CommandError('Bad value "%s" for date_issue' % options['date_issue'])
else:
date_issue = date_end
generate_invoices(date_start=date_start, date_end=date_end) try:
campaign = Campaign.objects.get(date_start=date_start, date_end=date_end)
except Campaign.DoesNotExist:
campaigns = Campaign.objects.extra(
where=["(date_start, date_end) OVERLAPS (%s, %s)"], params=[date_start, date_end]
)
if campaigns.exists():
raise CommandError('Overlapping campaigns already exist')
campaign = Campaign.objects.create(
date_start=date_start, date_end=date_end, date_issue=date_issue
)
generate_invoices(campaign=campaign, draft=options['draft'])
self.stdout.write( self.stdout.write(
self.style.SUCCESS('Invoicing generation OK (start: %s, end: %s)' % (date_start, date_end)) self.style.SUCCESS('Invoicing generation OK (start: %s, end: %s)' % (date_start, date_end))

View File

@ -0,0 +1,65 @@
# Generated by Django 2.2.26 on 2022-12-01 10:38
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('invoicing', '0003_invoice'),
]
operations = [
migrations.CreateModel(
name='Campaign',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('date_start', models.DateField(verbose_name='Start date')),
('date_end', models.DateField(verbose_name='End date')),
('date_issue', models.DateField(verbose_name='Issue date')),
],
),
migrations.CreateModel(
name='Pool',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('draft', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
(
'campaign',
models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='invoicing.Campaign'),
),
],
),
migrations.AddField(
model_name='draftinvoice',
name='pool',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='invoicing.Pool'),
preserve_default=False,
),
migrations.AddField(
model_name='draftinvoiceline',
name='pool',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='invoicing.Pool'),
preserve_default=False,
),
migrations.AddField(
model_name='invoice',
name='pool',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='invoicing.Pool'),
preserve_default=False,
),
migrations.AddField(
model_name='invoiceline',
name='pool',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='invoicing.Pool'),
preserve_default=False,
),
]

View File

@ -87,6 +87,18 @@ class Regie(models.Model):
return created, regie return created, regie
class Campaign(models.Model):
date_start = models.DateField(_('Start date'))
date_end = models.DateField(_('End date'))
date_issue = models.DateField(_('Issue date'))
class Pool(models.Model):
campaign = models.ForeignKey(Campaign, on_delete=models.PROTECT)
draft = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
class AbstractInvoice(models.Model): class AbstractInvoice(models.Model):
label = models.CharField(_('Label'), max_length=300) label = models.CharField(_('Label'), max_length=300)
total_amount = models.DecimalField(max_digits=9, decimal_places=2, default=0) total_amount = models.DecimalField(max_digits=9, decimal_places=2, default=0)
@ -94,6 +106,8 @@ class AbstractInvoice(models.Model):
regie = models.ForeignKey(Regie, on_delete=models.PROTECT) regie = models.ForeignKey(Regie, on_delete=models.PROTECT)
payer = models.CharField(_('Payer'), max_length=300) payer = models.CharField(_('Payer'), max_length=300)
pool = models.ForeignKey(Pool, on_delete=models.PROTECT)
class Meta: class Meta:
abstract = True abstract = True
@ -125,6 +139,8 @@ class AbstractInvoiceLine(models.Model):
], ],
) )
pool = models.ForeignKey(Pool, on_delete=models.PROTECT)
class Meta: class Meta:
abstract = True abstract = True

View File

@ -26,14 +26,15 @@ from lingo.invoicing.models import DraftInvoice, DraftInvoiceLine
from lingo.pricing.models import AgendaPricing, AgendaPricingNotFound, PricingError from lingo.pricing.models import AgendaPricing, AgendaPricingNotFound, PricingError
def get_agendas(date_start, date_end): def get_agendas(pool):
agendas_pricings = AgendaPricing.objects.filter(flat_fee_schedule=False).extra( agendas_pricings = AgendaPricing.objects.filter(flat_fee_schedule=False).extra(
where=["(date_start, date_end) OVERLAPS (%s, %s)"], params=[date_start, date_end] where=["(date_start, date_end) OVERLAPS (%s, %s)"],
params=[pool.campaign.date_start, pool.campaign.date_end],
) )
return Agenda.objects.filter(pk__in=agendas_pricings.values('agendas')).order_by('pk') return Agenda.objects.filter(pk__in=agendas_pricings.values('agendas')).order_by('pk')
def get_all_subscriptions(agendas, date_start, date_end): def get_all_subscriptions(agendas, pool):
# result: # result:
# { # {
# 'user_id': { # 'user_id': {
@ -44,7 +45,11 @@ def get_all_subscriptions(agendas, date_start, date_end):
# } # }
all_subscriptions = {} all_subscriptions = {}
for agenda in agendas: for agenda in agendas:
subscriptions = get_subscriptions(agenda_slug=agenda.slug, date_start=date_start, date_end=date_end) subscriptions = get_subscriptions(
agenda_slug=agenda.slug,
date_start=pool.campaign.date_start,
date_end=pool.campaign.date_end,
)
for subscription in subscriptions: for subscription in subscriptions:
if subscription['user_external_id'] not in all_subscriptions: if subscription['user_external_id'] not in all_subscriptions:
all_subscriptions[subscription['user_external_id']] = {} all_subscriptions[subscription['user_external_id']] = {}
@ -55,9 +60,7 @@ def get_all_subscriptions(agendas, date_start, date_end):
return all_subscriptions return all_subscriptions
def get_invoice_lines_for_user( def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, subscriptions, pool):
agendas, agendas_pricings, user_external_id, subscriptions, date_start, date_end
):
def get_agenda_pricing(agendas_pricings_for_agenda, date_event): def get_agenda_pricing(agendas_pricings_for_agenda, date_event):
# same logic as AgendaPricing.get_agenda_pricing # same logic as AgendaPricing.get_agenda_pricing
for agenda_pricing in agendas_pricings_for_agenda: for agenda_pricing in agendas_pricings_for_agenda:
@ -93,8 +96,8 @@ def get_invoice_lines_for_user(
check_status_list = get_check_status( check_status_list = get_check_status(
agenda_slugs=list(subscriptions.keys()), agenda_slugs=list(subscriptions.keys()),
user_external_id=user_external_id, user_external_id=user_external_id,
date_start=date_start, date_start=pool.campaign.date_start,
date_end=date_end, date_end=pool.campaign.date_end,
) )
request = RequestFactory().get('/') # XXX request = RequestFactory().get('/') # XXX
agendas_by_slug = {a.slug: a for a in agendas} agendas_by_slug = {a.slug: a for a in agendas}
@ -150,6 +153,7 @@ def get_invoice_lines_for_user(
event=serialized_event, event=serialized_event,
pricing_data=pricing_error, pricing_data=pricing_error,
status='error', status='error',
pool=pool,
) )
) )
else: else:
@ -166,16 +170,20 @@ def get_invoice_lines_for_user(
event=serialized_event, event=serialized_event,
pricing_data=pricing_data, pricing_data=pricing_data,
status='success', status='success',
pool=pool,
) )
) )
DraftInvoiceLine.objects.bulk_create(lines) DraftInvoiceLine.objects.bulk_create(lines)
return lines return lines
def get_all_invoice_lines(agendas, subscriptions, date_start, date_end): def get_all_invoice_lines(agendas, subscriptions, pool):
agendas_pricings = ( agendas_pricings = (
AgendaPricing.objects.filter(flat_fee_schedule=False) AgendaPricing.objects.filter(flat_fee_schedule=False)
.extra(where=["(date_start, date_end) OVERLAPS (%s, %s)"], params=[date_start, date_end]) .extra(
where=["(date_start, date_end) OVERLAPS (%s, %s)"],
params=[pool.campaign.date_start, pool.campaign.date_end],
)
.prefetch_related('agendas', 'pricing__criterias', 'pricing__categories') .prefetch_related('agendas', 'pricing__criterias', 'pricing__categories')
) )
@ -187,13 +195,12 @@ def get_all_invoice_lines(agendas, subscriptions, date_start, date_end):
agendas_pricings=agendas_pricings, agendas_pricings=agendas_pricings,
user_external_id=user_external_id, user_external_id=user_external_id,
subscriptions=user_subs, subscriptions=user_subs,
date_start=date_start, pool=pool,
date_end=date_end,
) )
return lines return lines
def generate_invoices_from_lines(agendas, all_lines, date_start, date_end): def generate_invoices_from_lines(agendas, all_lines, pool):
agendas_by_slug = {a.slug: a for a in agendas} agendas_by_slug = {a.slug: a for a in agendas}
# regroup lines by regie, and by payer_external_id (payer) # regroup lines by regie, and by payer_external_id (payer)
@ -222,10 +229,15 @@ def generate_invoices_from_lines(agendas, all_lines, date_start, date_end):
for regie_id, regie_subs in lines_by_regie.items(): for regie_id, regie_subs in lines_by_regie.items():
for payer_external_id, adult_lines in regie_subs.items(): for payer_external_id, adult_lines in regie_subs.items():
invoice = DraftInvoice.objects.create( invoice = DraftInvoice.objects.create(
label=_('Invoice from %s to %s') % (date_start, date_end - datetime.timedelta(days=1)), label=_('Invoice from %s to %s')
date_issue=date_end, # XXX % (
pool.campaign.date_start,
pool.campaign.date_end - datetime.timedelta(days=1),
),
date_issue=pool.campaign.date_issue,
regie_id=regie_id, regie_id=regie_id,
payer=payer_external_id, payer=payer_external_id,
pool=pool,
) )
DraftInvoiceLine.objects.filter(pk__in=[line.pk for line in adult_lines]).update(invoice=invoice) DraftInvoiceLine.objects.filter(pk__in=[line.pk for line in adult_lines]).update(invoice=invoice)
invoices.append(invoice) invoices.append(invoice)
@ -233,14 +245,13 @@ def generate_invoices_from_lines(agendas, all_lines, date_start, date_end):
return invoices return invoices
def generate_invoices(date_start, date_end): def generate_invoices(campaign, draft=True):
pool = campaign.pool_set.create(draft=draft)
# get agendas with pricing corresponding to the period # get agendas with pricing corresponding to the period
agendas = get_agendas(date_start=date_start, date_end=date_end) agendas = get_agendas(pool=pool)
# get subscriptions for each agenda, for the period # get subscriptions for each agenda, for the period
subscriptions = get_all_subscriptions(agendas=agendas, date_start=date_start, date_end=date_end) subscriptions = get_all_subscriptions(agendas=agendas, pool=pool)
# get invoice lines for all subscribed users, for each agenda in the corresponding period # get invoice lines for all subscribed users, for each agenda in the corresponding period
lines = get_all_invoice_lines( lines = get_all_invoice_lines(agendas=agendas, subscriptions=subscriptions, pool=pool)
agendas=agendas, subscriptions=subscriptions, date_start=date_start, date_end=date_end
)
# and generate invoices # and generate invoices
generate_invoices_from_lines(agendas=agendas, all_lines=lines, date_start=date_start, date_end=date_end) generate_invoices_from_lines(agendas=agendas, all_lines=lines, pool=pool)

View File

@ -9,7 +9,7 @@ from django.test.utils import CaptureQueriesContext
from lingo.agendas.chrono import ChronoError from lingo.agendas.chrono import ChronoError
from lingo.agendas.models import Agenda from lingo.agendas.models import Agenda
from lingo.invoicing import utils from lingo.invoicing import utils
from lingo.invoicing.models import DraftInvoice, DraftInvoiceLine, Regie from lingo.invoicing.models import Campaign, DraftInvoice, DraftInvoiceLine, Pool, Regie
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing, PricingError from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing, PricingError
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -21,12 +21,18 @@ def test_get_agendas():
agenda3 = Agenda.objects.create(label='Agenda 3') agenda3 = Agenda.objects.create(label='Agenda 3')
Agenda.objects.create(label='Agenda 4') Agenda.objects.create(label='Agenda 4')
pricing = Pricing.objects.create(label='Foo bar 1') pricing = Pricing.objects.create(label='Foo bar 1')
campaign = Campaign.objects.create(
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,
)
# no agenda pricing defined # no agenda pricing defined
assert ( assert list(utils.get_agendas(pool=pool)) == []
list(utils.get_agendas(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)))
== []
)
# agenda pricing, but for flat_fee_schedule # agenda pricing, but for flat_fee_schedule
agenda_pricing = AgendaPricing.objects.create( agenda_pricing = AgendaPricing.objects.create(
@ -36,10 +42,7 @@ def test_get_agendas():
flat_fee_schedule=True, # wrong config flat_fee_schedule=True, # wrong config
) )
agenda_pricing.agendas.add(agenda1) agenda_pricing.agendas.add(agenda1)
assert ( assert list(utils.get_agendas(pool=pool)) == []
list(utils.get_agendas(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)))
== []
)
# create some agenda pricing # create some agenda pricing
agenda_pricing1 = AgendaPricing.objects.create( agenda_pricing1 = AgendaPricing.objects.create(
@ -52,74 +55,77 @@ def test_get_agendas():
date_start=datetime.date(year=2022, month=9, day=1), date_start=datetime.date(year=2022, month=9, day=1),
date_end=datetime.date(year=2022, month=10, day=1), date_end=datetime.date(year=2022, month=10, day=1),
) )
assert ( assert list(utils.get_agendas(pool=pool)) == []
list(utils.get_agendas(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)))
== []
)
# link agendas to agenda pricing # link agendas to agenda pricing
agenda_pricing1.agendas.add(agenda1, agenda2) agenda_pricing1.agendas.add(agenda1, agenda2)
agenda_pricing2.agendas.add(agenda3) agenda_pricing2.agendas.add(agenda3)
assert list( assert list(utils.get_agendas(pool=pool)) == [agenda3]
utils.get_agendas(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)) campaign.date_start = datetime.date(2021, 9, 1)
) == [agenda3] campaign.date_end = datetime.date(2021, 10, 1)
assert list( campaign.save()
utils.get_agendas(date_start=datetime.date(2021, 9, 1), date_end=datetime.date(2021, 10, 1)) assert list(utils.get_agendas(pool=pool)) == [agenda1, agenda2]
) == [agenda1, agenda2] campaign.date_start = datetime.date(2022, 8, 31)
assert ( campaign.date_end = datetime.date(2022, 9, 1)
list(utils.get_agendas(date_start=datetime.date(2022, 8, 31), date_end=datetime.date(2022, 9, 1))) campaign.save()
== [] assert list(utils.get_agendas(pool=pool)) == []
) campaign.date_start = datetime.date(2022, 9, 1)
assert list( campaign.date_end = datetime.date(2022, 9, 2)
utils.get_agendas(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 9, 2)) campaign.save()
) == [agenda3] assert list(utils.get_agendas(pool=pool)) == [agenda3]
assert list( campaign.date_start = datetime.date(2022, 9, 30)
utils.get_agendas(date_start=datetime.date(2022, 9, 30), date_end=datetime.date(2022, 10, 1)) campaign.date_end = datetime.date(2022, 10, 1)
) == [agenda3] campaign.save()
assert ( assert list(utils.get_agendas(pool=pool)) == [agenda3]
list(utils.get_agendas(date_start=datetime.date(2022, 10, 1), date_end=datetime.date(2022, 10, 2))) campaign.date_start = datetime.date(2022, 10, 1)
== [] campaign.date_end = datetime.date(2022, 10, 2)
) campaign.save()
assert list( assert list(utils.get_agendas(pool=pool)) == []
utils.get_agendas(date_start=datetime.date(2021, 9, 15), date_end=datetime.date(2022, 9, 15)) campaign.date_start = datetime.date(2021, 9, 15)
) == [agenda1, agenda2, agenda3] campaign.date_end = datetime.date(2022, 9, 15)
campaign.save()
assert list(utils.get_agendas(pool=pool)) == [agenda1, agenda2, agenda3]
@mock.patch('lingo.invoicing.utils.get_subscriptions') @mock.patch('lingo.invoicing.utils.get_subscriptions')
def test_get_all_subscriptions_error(mock_subscriptions): def test_get_all_subscriptions_error(mock_subscriptions):
agenda = Agenda.objects.create(label='Agenda') agenda = Agenda.objects.create(label='Agenda')
campaign = Campaign.objects.create(
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,
)
mock_subscriptions.side_effect = ChronoError('foo baz') mock_subscriptions.side_effect = ChronoError('foo baz')
with pytest.raises(ChronoError): with pytest.raises(ChronoError):
utils.get_all_subscriptions( utils.get_all_subscriptions(agendas=[agenda], pool=pool)
agendas=[agenda], date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)
)
@mock.patch('lingo.invoicing.utils.get_subscriptions') @mock.patch('lingo.invoicing.utils.get_subscriptions')
def test_get_all_subscriptions(mock_subscriptions): def test_get_all_subscriptions(mock_subscriptions):
agenda1 = Agenda.objects.create(label='Agenda 1') agenda1 = Agenda.objects.create(label='Agenda 1')
agenda2 = Agenda.objects.create(label='Agenda 2') agenda2 = Agenda.objects.create(label='Agenda 2')
campaign = Campaign.objects.create(
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,
)
# no agendas # no agendas
assert ( assert utils.get_all_subscriptions(agendas=[], pool=pool) == {}
utils.get_all_subscriptions(
agendas=[], date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)
)
== {}
)
assert mock_subscriptions.call_args_list == [] assert mock_subscriptions.call_args_list == []
# no subscriptions # no subscriptions
mock_subscriptions.return_value = [] mock_subscriptions.return_value = []
assert ( assert utils.get_all_subscriptions(agendas=[agenda1, agenda2], pool=pool) == {}
utils.get_all_subscriptions(
agendas=[agenda1, agenda2],
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
)
== {}
)
assert mock_subscriptions.call_args_list == [ assert mock_subscriptions.call_args_list == [
mock.call( mock.call(
agenda_slug='agenda-1', date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1) agenda_slug='agenda-1', date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)
@ -157,9 +163,7 @@ def test_get_all_subscriptions(mock_subscriptions):
}, },
], ],
] ]
assert utils.get_all_subscriptions( assert utils.get_all_subscriptions(agendas=[agenda1, agenda2], pool=pool) == {
agendas=[agenda1, agenda2], date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)
) == {
'user:1': { 'user:1': {
'agenda-1': [ 'agenda-1': [
{ {
@ -203,6 +207,15 @@ def test_get_all_subscriptions(mock_subscriptions):
@mock.patch('lingo.invoicing.utils.get_check_status') @mock.patch('lingo.invoicing.utils.get_check_status')
def test_get_invoice_lines_for_user_check_status_error(mock_status): def test_get_invoice_lines_for_user_check_status_error(mock_status):
campaign = Campaign.objects.create(
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,
)
mock_status.side_effect = ChronoError('foo baz') mock_status.side_effect = ChronoError('foo baz')
with pytest.raises(ChronoError): with pytest.raises(ChronoError):
utils.get_invoice_lines_for_user( utils.get_invoice_lines_for_user(
@ -210,8 +223,7 @@ def test_get_invoice_lines_for_user_check_status_error(mock_status):
agendas_pricings=[], agendas_pricings=[],
user_external_id='user:1', user_external_id='user:1',
subscriptions={'foo': 'bar'}, subscriptions={'foo': 'bar'},
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
@ -227,6 +239,15 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
date_end=datetime.date(year=2022, month=10, day=1), date_end=datetime.date(year=2022, month=10, day=1),
) )
agenda_pricing.agendas.add(agenda1, agenda2) agenda_pricing.agendas.add(agenda1, agenda2)
campaign = Campaign.objects.create(
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,
)
# no agendas # no agendas
assert ( assert (
@ -243,8 +264,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
== [] == []
) )
@ -258,8 +278,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
agendas_pricings=[agenda_pricing], agendas_pricings=[agenda_pricing],
user_external_id='user:1', user_external_id='user:1',
subscriptions={}, subscriptions={},
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
== [] == []
) )
@ -282,8 +301,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
== [] == []
) )
@ -323,8 +341,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
== [] == []
) )
@ -411,8 +428,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
assert mock_pricing_data_event.call_args_list == [ assert mock_pricing_data_event.call_args_list == [
mock.call( mock.call(
@ -495,6 +511,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
} }
assert line1.pricing_data == {'foo1': 'bar1', 'pricing': 1} assert line1.pricing_data == {'foo1': 'bar1', 'pricing': 1}
assert line1.status == 'success' assert line1.status == 'success'
assert line1.pool == pool
assert isinstance(line2, DraftInvoiceLine) assert isinstance(line2, DraftInvoiceLine)
assert line2.invoice is None assert line2.invoice is None
assert line2.slug == 'agenda-1@event-2' assert line2.slug == 'agenda-1@event-2'
@ -512,6 +529,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
} }
assert line2.pricing_data == {'foo2': 'bar2', 'pricing': 2} assert line2.pricing_data == {'foo2': 'bar2', 'pricing': 2}
assert line2.status == 'success' assert line2.status == 'success'
assert line2.pool == pool
assert isinstance(line3, DraftInvoiceLine) assert isinstance(line3, DraftInvoiceLine)
assert line3.invoice is None assert line3.invoice is None
assert line3.slug == 'agenda-2@eveeent-1' assert line3.slug == 'agenda-2@eveeent-1'
@ -529,6 +547,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
} }
assert line3.pricing_data == {'foo3': 'bar3', 'pricing': 3} assert line3.pricing_data == {'foo3': 'bar3', 'pricing': 3}
assert line3.status == 'success' assert line3.status == 'success'
assert line3.pool == pool
assert isinstance(line4, DraftInvoiceLine) assert isinstance(line4, DraftInvoiceLine)
assert line4.invoice is None assert line4.invoice is None
assert line4.slug == 'agenda-2@eveeent-2' assert line4.slug == 'agenda-2@eveeent-2'
@ -546,6 +565,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
} }
assert line4.pricing_data == {'foo4': 'bar4', 'pricing': 4} assert line4.pricing_data == {'foo4': 'bar4', 'pricing': 4}
assert line4.status == 'success' assert line4.status == 'success'
assert line4.pool == pool
@mock.patch('lingo.invoicing.utils.get_check_status') @mock.patch('lingo.invoicing.utils.get_check_status')
@ -559,6 +579,15 @@ def test_get_invoice_lines_for_user_check_status_subscriptions_dates(mock_pricin
date_end=datetime.date(year=2022, month=10, day=1), date_end=datetime.date(year=2022, month=10, day=1),
) )
agenda_pricing.agendas.add(agenda) agenda_pricing.agendas.add(agenda)
campaign = Campaign.objects.create(
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,
)
mock_pricing_data_event.return_value = {'foo': 'bar', 'pricing': 42} mock_pricing_data_event.return_value = {'foo': 'bar', 'pricing': 42}
mock_status.return_value = [ mock_status.return_value = [
@ -589,8 +618,7 @@ def test_get_invoice_lines_for_user_check_status_subscriptions_dates(mock_pricin
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
== [] == []
) )
@ -611,8 +639,7 @@ def test_get_invoice_lines_for_user_check_status_subscriptions_dates(mock_pricin
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
== [] == []
) )
@ -644,8 +671,7 @@ def test_get_invoice_lines_for_user_check_status_subscriptions_dates(mock_pricin
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
assert len(lines) == 1 assert len(lines) == 1
assert mock_pricing_data_event.call_args_list == [ assert mock_pricing_data_event.call_args_list == [
@ -690,6 +716,15 @@ def test_get_invoice_lines_for_user_check_status_agenda_pricing_dates(mock_statu
date_end=datetime.date(year=2022, month=11, day=1), date_end=datetime.date(year=2022, month=11, day=1),
) )
agenda_pricing3.agendas.add(agenda) agenda_pricing3.agendas.add(agenda)
campaign = Campaign.objects.create(
date_start=datetime.date(2022, 1, 1),
date_end=datetime.date(2023, 1, 1),
date_issue=datetime.date(2023, 1, 31),
)
pool = Pool.objects.create(
campaign=campaign,
draft=True,
)
pricing_data_event_patch = mock.patch.object(AgendaPricing, 'get_pricing_data_for_event', autospec=True) pricing_data_event_patch = mock.patch.object(AgendaPricing, 'get_pricing_data_for_event', autospec=True)
@ -722,8 +757,7 @@ def test_get_invoice_lines_for_user_check_status_agenda_pricing_dates(mock_statu
}, },
], ],
}, },
date_start=datetime.date(2022, 1, 1), pool=pool,
date_end=datetime.date(2023, 1, 1),
) )
assert len(lines) == 1 assert len(lines) == 1
assert mock_pricing_data_event.call_args_list[0][0][0] == agenda_pricing1 assert mock_pricing_data_event.call_args_list[0][0][0] == agenda_pricing1
@ -757,8 +791,7 @@ def test_get_invoice_lines_for_user_check_status_agenda_pricing_dates(mock_statu
}, },
], ],
}, },
date_start=datetime.date(2022, 1, 1), pool=pool,
date_end=datetime.date(2023, 1, 1),
) )
assert len(lines) == 1 assert len(lines) == 1
assert mock_pricing_data_event.call_args_list[0][0][0] == agenda_pricing3 assert mock_pricing_data_event.call_args_list[0][0][0] == agenda_pricing3
@ -791,8 +824,7 @@ def test_get_invoice_lines_for_user_check_status_agenda_pricing_dates(mock_statu
}, },
], ],
}, },
date_start=datetime.date(2022, 1, 1), pool=pool,
date_end=datetime.date(2023, 1, 1),
) )
assert len(lines) == 0 assert len(lines) == 0
assert mock_pricing_data_event.call_args_list == [] assert mock_pricing_data_event.call_args_list == []
@ -809,6 +841,15 @@ def test_get_invoice_lines_for_user_check_status_pricing_error(mock_pricing_data
date_end=datetime.date(year=2022, month=10, day=1), date_end=datetime.date(year=2022, month=10, day=1),
) )
agenda_pricing.agendas.add(agenda) agenda_pricing.agendas.add(agenda)
campaign = Campaign.objects.create(
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,
)
mock_pricing_data_event.side_effect = [ mock_pricing_data_event.side_effect = [
{'foo1': 'bar1', 'pricing': 1}, {'foo1': 'bar1', 'pricing': 1},
@ -860,8 +901,7 @@ def test_get_invoice_lines_for_user_check_status_pricing_error(mock_pricing_data
}, },
], ],
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
assert len(lines) == 3 assert len(lines) == 3
line1, line2, line3 = lines line1, line2, line3 = lines
@ -882,6 +922,7 @@ def test_get_invoice_lines_for_user_check_status_pricing_error(mock_pricing_data
} }
assert line1.pricing_data == {'foo1': 'bar1', 'pricing': 1} assert line1.pricing_data == {'foo1': 'bar1', 'pricing': 1}
assert line1.status == 'success' assert line1.status == 'success'
assert line1.pool == pool
assert isinstance(line2, DraftInvoiceLine) assert isinstance(line2, DraftInvoiceLine)
assert line2.invoice is None assert line2.invoice is None
assert line2.slug == 'agenda@event-2' assert line2.slug == 'agenda@event-2'
@ -899,6 +940,7 @@ def test_get_invoice_lines_for_user_check_status_pricing_error(mock_pricing_data
} }
assert line2.pricing_data == {'error': {}} assert line2.pricing_data == {'error': {}}
assert line2.status == 'error' assert line2.status == 'error'
assert line2.pool == pool
assert isinstance(line3, DraftInvoiceLine) assert isinstance(line3, DraftInvoiceLine)
assert line3.invoice is None assert line3.invoice is None
assert line3.slug == 'agenda@event-3' assert line3.slug == 'agenda@event-3'
@ -916,6 +958,7 @@ def test_get_invoice_lines_for_user_check_status_pricing_error(mock_pricing_data
} }
assert line3.pricing_data == {'foo3': 'bar3', 'pricing': 3} assert line3.pricing_data == {'foo3': 'bar3', 'pricing': 3}
assert line3.status == 'success' assert line3.status == 'success'
assert line3.pool == pool
@mock.patch('lingo.invoicing.utils.get_invoice_lines_for_user') @mock.patch('lingo.invoicing.utils.get_invoice_lines_for_user')
@ -923,21 +966,33 @@ def test_get_all_invoice_lines(mock_user_lines):
agenda1 = Agenda.objects.create(label='Agenda 1') agenda1 = Agenda.objects.create(label='Agenda 1')
agenda2 = Agenda.objects.create(label='Agenda 2') agenda2 = Agenda.objects.create(label='Agenda 2')
Agenda.objects.create(label='Agenda 3') Agenda.objects.create(label='Agenda 3')
campaign = Campaign.objects.create(
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,
)
line1 = DraftInvoiceLine.objects.create( line1 = DraftInvoiceLine.objects.create(
quantity=0, quantity=0,
unit_amount=0, unit_amount=0,
total_amount=0, total_amount=0,
pool=pool,
) )
line2 = DraftInvoiceLine.objects.create( line2 = DraftInvoiceLine.objects.create(
quantity=0, quantity=0,
unit_amount=0, unit_amount=0,
total_amount=0, total_amount=0,
pool=pool,
) )
line3 = DraftInvoiceLine.objects.create( line3 = DraftInvoiceLine.objects.create(
quantity=0, quantity=0,
unit_amount=0, unit_amount=0,
total_amount=0, total_amount=0,
pool=pool,
) )
mock_user_lines.side_effect = [[line1, line2], [line3]] mock_user_lines.side_effect = [[line1, line2], [line3]]
@ -947,8 +1002,7 @@ def test_get_all_invoice_lines(mock_user_lines):
utils.get_all_invoice_lines( utils.get_all_invoice_lines(
agendas=[agenda1, agenda2], agendas=[agenda1, agenda2],
subscriptions={}, subscriptions={},
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
== [] == []
) )
@ -961,8 +1015,7 @@ def test_get_all_invoice_lines(mock_user_lines):
'user:1': {'foo': 'bar1'}, 'user:1': {'foo': 'bar1'},
'user:2': {'foo': 'bar2'}, 'user:2': {'foo': 'bar2'},
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) == [line1, line2, line3] ) == [line1, line2, line3]
assert mock_user_lines.call_args_list == [ assert mock_user_lines.call_args_list == [
mock.call( mock.call(
@ -970,16 +1023,14 @@ def test_get_all_invoice_lines(mock_user_lines):
agendas_pricings=mock.ANY, agendas_pricings=mock.ANY,
user_external_id='user:1', user_external_id='user:1',
subscriptions={'foo': 'bar1'}, subscriptions={'foo': 'bar1'},
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
), ),
mock.call( mock.call(
agendas=[agenda1, agenda2], agendas=[agenda1, agenda2],
agendas_pricings=mock.ANY, agendas_pricings=mock.ANY,
user_external_id='user:2', user_external_id='user:2',
subscriptions={'foo': 'bar2'}, subscriptions={'foo': 'bar2'},
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
), ),
] ]
@ -1029,6 +1080,16 @@ def test_get_all_invoice_lines_queryset(mock_status):
) )
agenda_pricing22.agendas.add(agenda2) agenda_pricing22.agendas.add(agenda2)
campaign = Campaign.objects.create(
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 11, 1),
date_issue=datetime.date(2022, 11, 30),
)
pool = Pool.objects.create(
campaign=campaign,
draft=True,
)
mock_status.return_value = [ mock_status.return_value = [
{ {
'event': { 'event': {
@ -1109,8 +1170,7 @@ def test_get_all_invoice_lines_queryset(mock_status):
], ],
}, },
}, },
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 11, 1),
) )
assert lines assert lines
assert len(ctx.captured_queries) == 7 assert len(ctx.captured_queries) == 7
@ -1124,6 +1184,16 @@ def test_generate_invoices_from_lines():
agenda3 = Agenda.objects.create(label='Agenda 3', regie=regie1) agenda3 = Agenda.objects.create(label='Agenda 3', regie=regie1)
agenda4 = Agenda.objects.create(label='Agenda 4') # regie not configured agenda4 = Agenda.objects.create(label='Agenda 4') # regie not configured
campaign = Campaign.objects.create(
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,
)
line_error = DraftInvoiceLine.objects.create( line_error = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-1'}, event={'agenda': 'agenda-1'},
quantity=0, quantity=0,
@ -1132,6 +1202,7 @@ def test_generate_invoices_from_lines():
user_external_id='user:1', user_external_id='user:1',
payer_external_id='user:1', payer_external_id='user:1',
status='error', status='error',
pool=pool,
) )
line1 = DraftInvoiceLine.objects.create( line1 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-1'}, event={'agenda': 'agenda-1'},
@ -1141,6 +1212,7 @@ def test_generate_invoices_from_lines():
user_external_id='user:1', user_external_id='user:1',
payer_external_id='user:1', payer_external_id='user:1',
status='success', status='success',
pool=pool,
) )
line2 = DraftInvoiceLine.objects.create( line2 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-1'}, event={'agenda': 'agenda-1'},
@ -1150,6 +1222,7 @@ def test_generate_invoices_from_lines():
user_external_id='user:1', user_external_id='user:1',
payer_external_id='user:1', payer_external_id='user:1',
status='success', status='success',
pool=pool,
) )
line3 = DraftInvoiceLine.objects.create( line3 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-2'}, event={'agenda': 'agenda-2'},
@ -1159,6 +1232,7 @@ def test_generate_invoices_from_lines():
user_external_id='user:1', user_external_id='user:1',
payer_external_id='user:1', payer_external_id='user:1',
status='success', status='success',
pool=pool,
) )
line4 = DraftInvoiceLine.objects.create( line4 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-2'}, event={'agenda': 'agenda-2'},
@ -1168,6 +1242,7 @@ def test_generate_invoices_from_lines():
user_external_id='user:1', user_external_id='user:1',
payer_external_id='user:2', payer_external_id='user:2',
status='success', status='success',
pool=pool,
) )
line5 = DraftInvoiceLine.objects.create( line5 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-3'}, event={'agenda': 'agenda-3'},
@ -1177,6 +1252,7 @@ def test_generate_invoices_from_lines():
user_external_id='user:2', user_external_id='user:2',
payer_external_id='user:1', payer_external_id='user:1',
status='success', status='success',
pool=pool,
) )
DraftInvoiceLine.objects.create( # not used for generation DraftInvoiceLine.objects.create( # not used for generation
event={'agenda': 'agenda-3'}, event={'agenda': 'agenda-3'},
@ -1186,6 +1262,7 @@ def test_generate_invoices_from_lines():
user_external_id='user:2', user_external_id='user:2',
payer_external_id='user:1', payer_external_id='user:1',
status='success', status='success',
pool=pool,
) )
line6 = DraftInvoiceLine.objects.create( line6 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-4'}, # regie not configured event={'agenda': 'agenda-4'}, # regie not configured
@ -1195,21 +1272,20 @@ def test_generate_invoices_from_lines():
user_external_id='user:1', user_external_id='user:1',
payer_external_id='user:1', payer_external_id='user:1',
status='success', status='success',
pool=pool,
) )
invoices = utils.generate_invoices_from_lines( invoices = utils.generate_invoices_from_lines(
agendas=[], agendas=[],
all_lines=[line_error, line1, line2, line3, line4, line5, line6], all_lines=[line_error, line1, line2, line3, line4, line5, line6],
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
assert len(invoices) == 0 assert len(invoices) == 0
invoices = utils.generate_invoices_from_lines( invoices = utils.generate_invoices_from_lines(
agendas=[agenda1, agenda2, agenda3, agenda4], agendas=[agenda1, agenda2, agenda3, agenda4],
all_lines=[line_error, line1, line2, line3, line4, line5, line6], all_lines=[line_error, line1, line2, line3, line4, line5, line6],
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
assert len(invoices) == 3 assert len(invoices) == 3
invoice1, invoice2, invoice3 = invoices invoice1, invoice2, invoice3 = invoices
@ -1220,23 +1296,26 @@ def test_generate_invoices_from_lines():
assert isinstance(invoice1, DraftInvoice) assert isinstance(invoice1, DraftInvoice)
assert invoice1.label == 'Invoice from 2022-09-01 to 2022-09-30' assert invoice1.label == 'Invoice from 2022-09-01 to 2022-09-30'
assert invoice1.total_amount == 8 assert invoice1.total_amount == 8
assert invoice1.date_issue == datetime.date(2022, 10, 1) assert invoice1.date_issue == datetime.date(2022, 10, 31)
assert invoice1.regie == regie1 assert invoice1.regie == regie1
assert invoice1.payer == 'user:1' assert invoice1.payer == 'user:1'
assert invoice1.pool == pool
assert list(invoice1.lines.order_by('pk')) == [line1, line2, line5] assert list(invoice1.lines.order_by('pk')) == [line1, line2, line5]
assert isinstance(invoice2, DraftInvoice) assert isinstance(invoice2, DraftInvoice)
assert invoice2.label == 'Invoice from 2022-09-01 to 2022-09-30' assert invoice2.label == 'Invoice from 2022-09-01 to 2022-09-30'
assert invoice2.total_amount == 3 assert invoice2.total_amount == 3
assert invoice2.date_issue == datetime.date(2022, 10, 1) assert invoice2.date_issue == datetime.date(2022, 10, 31)
assert invoice2.regie == regie2 assert invoice2.regie == regie2
assert invoice2.payer == 'user:1' assert invoice2.payer == 'user:1'
assert invoice2.pool == pool
assert list(invoice2.lines.order_by('pk')) == [line3] assert list(invoice2.lines.order_by('pk')) == [line3]
assert isinstance(invoice3, DraftInvoice) assert isinstance(invoice3, DraftInvoice)
assert invoice3.label == 'Invoice from 2022-09-01 to 2022-09-30' assert invoice3.label == 'Invoice from 2022-09-01 to 2022-09-30'
assert invoice3.total_amount == 4 assert invoice3.total_amount == 4
assert invoice3.date_issue == datetime.date(2022, 10, 1) assert invoice3.date_issue == datetime.date(2022, 10, 31)
assert invoice3.regie == regie2 assert invoice3.regie == regie2
assert invoice3.payer == 'user:2' assert invoice3.payer == 'user:2'
assert invoice3.pool == pool
assert list(invoice3.lines.order_by('pk')) == [line4] assert list(invoice3.lines.order_by('pk')) == [line4]
@ -1248,73 +1327,40 @@ def test_generate_invoices(mock_generate, mock_lines, mock_subscriptions, mock_a
agenda1 = Agenda.objects.create(label='Agenda 1') agenda1 = Agenda.objects.create(label='Agenda 1')
agenda2 = Agenda.objects.create(label='Agenda 2') agenda2 = Agenda.objects.create(label='Agenda 2')
Agenda.objects.create(label='Agenda 3') Agenda.objects.create(label='Agenda 3')
line1 = DraftInvoiceLine.objects.create( campaign = Campaign.objects.create(
event={'agenda': 'agenda-1'}, date_start=datetime.date(2022, 9, 1),
quantity=1, date_end=datetime.date(2022, 10, 1),
unit_amount=1, date_issue=datetime.date(2022, 10, 31),
total_amount=1,
user_external_id='user:1',
payer_external_id='user:1',
status='success',
)
line2 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-1'},
quantity=1,
unit_amount=2,
total_amount=2,
user_external_id='user:1',
payer_external_id='user:1',
status='success',
)
line3 = DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-2'},
quantity=1,
unit_amount=3,
total_amount=3,
user_external_id='user:1',
payer_external_id='user:1',
status='success',
)
DraftInvoiceLine.objects.create(
event={'agenda': 'agenda-1'},
quantity=1,
unit_amount=5,
total_amount=5,
user_external_id='user:2',
payer_external_id='user:1',
status='success',
) )
mock_agendas.return_value = [agenda1, agenda2] mock_agendas.return_value = [agenda1, agenda2]
mock_subscriptions.return_value = {'foo': 'bar'} mock_subscriptions.return_value = {'foo': 'bar'}
mock_lines.return_value = [line1, line2, line3] mock_lines.return_value = ['foo', 'bar']
# check only calls between functions # check only calls between functions
utils.generate_invoices(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)) utils.generate_invoices(campaign=campaign, draft=True)
assert mock_agendas.call_args_list == [ pool = Pool.objects.latest('pk')
mock.call(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)) assert pool.campaign == campaign
] assert pool.draft is True
assert mock_agendas.call_args_list == [mock.call(pool=pool)]
assert mock_subscriptions.call_args_list == [ assert mock_subscriptions.call_args_list == [
mock.call( mock.call(
agendas=[agenda1, agenda2], agendas=[agenda1, agenda2],
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
] ]
assert mock_lines.call_args_list == [ assert mock_lines.call_args_list == [
mock.call( mock.call(
agendas=[agenda1, agenda2], agendas=[agenda1, agenda2],
subscriptions={'foo': 'bar'}, subscriptions={'foo': 'bar'},
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
] ]
assert mock_generate.call_args_list == [ assert mock_generate.call_args_list == [
mock.call( mock.call(
agendas=[agenda1, agenda2], agendas=[agenda1, agenda2],
all_lines=[line1, line2, line3], all_lines=['foo', 'bar'],
date_start=datetime.date(2022, 9, 1), pool=pool,
date_end=datetime.date(2022, 10, 1),
) )
] ]
@ -1338,7 +1384,54 @@ def test_generate_invoices_cmd():
assert '%s' % excinfo.value == 'Bad value "bad-value" for date_end' assert '%s' % excinfo.value == 'Bad value "bad-value" for date_end'
assert mock_generate.call_args_list == [] assert mock_generate.call_args_list == []
with pytest.raises(CommandError) as excinfo:
call_command('generate_invoices', '2022-09-01', '2022-10-01', '--date-issue=bad-value')
assert '%s' % excinfo.value == 'Bad value "bad-value" for date_issue'
assert mock_generate.call_args_list == []
assert Campaign.objects.count() == 0
call_command('generate_invoices', '2022-09-01', '2022-10-01') call_command('generate_invoices', '2022-09-01', '2022-10-01')
assert mock_generate.call_args_list == [ assert Campaign.objects.count() == 1
mock.call(date_start=datetime.date(2022, 9, 1), date_end=datetime.date(2022, 10, 1)) campaign = Campaign.objects.latest('pk')
] assert campaign.date_start == datetime.date(2022, 9, 1)
assert campaign.date_end == datetime.date(2022, 10, 1)
assert campaign.date_issue == campaign.date_end
assert mock_generate.call_args_list == [mock.call(campaign=campaign, draft=False)]
mock_generate.reset_mock()
# again
call_command('generate_invoices', '2022-09-01', '2022-10-01')
assert Campaign.objects.count() == 1
assert mock_generate.call_args_list == [mock.call(campaign=campaign, draft=False)]
mock_generate.reset_mock()
call_command('generate_invoices', '2022-09-01', '2022-10-01', '--draft')
assert Campaign.objects.count() == 1
assert mock_generate.call_args_list == [mock.call(campaign=campaign, draft=True)]
mock_generate.reset_mock()
# with overlapping
with pytest.raises(CommandError) as excinfo:
call_command('generate_invoices', '2022-08-31', '2022-09-02')
assert '%s' % excinfo.value == 'Overlapping campaigns already exist'
assert mock_generate.call_args_list == []
with pytest.raises(CommandError) as excinfo:
call_command('generate_invoices', '2022-09-30', '2022-10-02')
assert '%s' % excinfo.value == 'Overlapping campaigns already exist'
assert mock_generate.call_args_list == []
# no overlapping
call_command('generate_invoices', '2022-08-01', '2022-09-01', '--date-issue=2022-10-31')
assert Campaign.objects.count() == 2
campaign = Campaign.objects.latest('pk')
assert mock_generate.call_args_list == [mock.call(campaign=campaign, draft=False)]
assert campaign.date_start == datetime.date(2022, 8, 1)
assert campaign.date_end == datetime.date(2022, 9, 1)
assert campaign.date_issue == datetime.date(2022, 10, 31)
mock_generate.reset_mock()
call_command('generate_invoices', '2022-10-01', '2022-11-01')
assert Campaign.objects.count() == 3
campaign = Campaign.objects.latest('pk')
assert mock_generate.call_args_list == [mock.call(campaign=campaign, draft=False)]
assert campaign.date_start == datetime.date(2022, 10, 1)
assert campaign.date_end == datetime.date(2022, 11, 1)
assert campaign.date_issue == campaign.date_end

View File

@ -2,7 +2,7 @@ import datetime
import pytest import pytest
from lingo.invoicing.models import DraftInvoice, DraftInvoiceLine, Invoice, InvoiceLine, Regie from lingo.invoicing.models import Campaign, DraftInvoice, DraftInvoiceLine, Invoice, InvoiceLine, Pool, Regie
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -13,9 +13,19 @@ def test_invoice_total_amount(draft):
invoice_model = DraftInvoice if draft else Invoice invoice_model = DraftInvoice if draft else Invoice
line_model = DraftInvoiceLine if draft else InvoiceLine line_model = DraftInvoiceLine if draft else InvoiceLine
invoice = invoice_model.objects.create(date_issue=datetime.date.today(), regie=regie) campaign = Campaign.objects.create(
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=draft,
)
invoice = invoice_model.objects.create(date_issue=datetime.date.today(), regie=regie, pool=pool)
assert invoice.total_amount == 0 assert invoice.total_amount == 0
invoice2 = invoice_model.objects.create(date_issue=datetime.date.today(), regie=regie) invoice2 = invoice_model.objects.create(date_issue=datetime.date.today(), regie=regie, pool=pool)
assert invoice2.total_amount == 0 assert invoice2.total_amount == 0
# line with error status, ignored # line with error status, ignored
@ -25,6 +35,7 @@ def test_invoice_total_amount(draft):
unit_amount=0, unit_amount=0,
total_amount=0, total_amount=0,
status='error', status='error',
pool=pool,
) )
invoice.refresh_from_db() invoice.refresh_from_db()
assert invoice.total_amount == 0 assert invoice.total_amount == 0
@ -73,6 +84,7 @@ def test_invoice_total_amount(draft):
unit_amount=20, unit_amount=20,
total_amount=20, total_amount=20,
status='success', status='success',
pool=pool,
) )
invoice.refresh_from_db() invoice.refresh_from_db()
assert invoice.total_amount == 32 assert invoice.total_amount == 32
@ -100,6 +112,7 @@ def test_invoice_total_amount(draft):
unit_amount=20, unit_amount=20,
total_amount=20, total_amount=20,
status='success', status='success',
pool=pool,
) )
invoice.refresh_from_db() invoice.refresh_from_db()
assert invoice.total_amount == 12 assert invoice.total_amount == 12