diff --git a/lingo/invoicing/forms.py b/lingo/invoicing/forms.py
index 91516e6..b2f385a 100644
--- a/lingo/invoicing/forms.py
+++ b/lingo/invoicing/forms.py
@@ -25,7 +25,7 @@ from lingo.invoicing.models import Campaign, DraftInvoiceLine, InvoiceLine, Regi
class CampaignForm(forms.ModelForm):
class Meta:
model = Campaign
- fields = ['date_start', 'date_end', 'date_issue']
+ fields = ['date_start', 'date_end', 'date_issue', 'injected_lines']
widgets = {
'date_start': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'date_end': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
@@ -92,6 +92,7 @@ class AbstractLineFilterSet(django_filters.FilterSet):
status_choices = [
('success', _('Success')),
+ ('success_injected', '%s (%s)' % (_('Success'), _('Injected'))),
('warning', _('Warning')),
('error', _('Error')),
]
@@ -106,6 +107,8 @@ class AbstractLineFilterSet(django_filters.FilterSet):
def filter_status(self, queryset, name, value):
if not value:
return queryset
+ if value == 'success_injected':
+ return queryset.filter(status='success', from_injected_line__isnull=False)
if value == 'error_todo':
return queryset.filter(status='error', error_status='')
if value == 'error_ignored':
diff --git a/lingo/invoicing/migrations/0015_injected_lines.py b/lingo/invoicing/migrations/0015_injected_lines.py
new file mode 100644
index 0000000..cd1e42a
--- /dev/null
+++ b/lingo/invoicing/migrations/0015_injected_lines.py
@@ -0,0 +1,25 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('invoicing', '0014_error_status'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='campaign',
+ name='injected_lines',
+ field=models.CharField(
+ choices=[
+ ('no', 'No'),
+ ('period', 'Yes, only for the period'),
+ ('all', 'Yes, all injected lines before the end of the period'),
+ ],
+ default='no',
+ max_length=10,
+ verbose_name='Integrate injected lines',
+ ),
+ ),
+ ]
diff --git a/lingo/invoicing/models.py b/lingo/invoicing/models.py
index ab848cf..d66b2ae 100644
--- a/lingo/invoicing/models.py
+++ b/lingo/invoicing/models.py
@@ -134,6 +134,16 @@ class Campaign(models.Model):
date_start = models.DateField(_('Start date'))
date_end = models.DateField(_('End date'))
date_issue = models.DateField(_('Issue date'))
+ injected_lines = models.CharField(
+ _('Integrate injected lines'),
+ choices=[
+ ('no', _('No')),
+ ('period', _('Yes, only for the period')),
+ ('all', _('Yes, all injected lines before the end of the period')),
+ ],
+ default='no',
+ max_length=10,
+ )
def __str__(self):
return _('From %(start)s to %(end)s') % {
diff --git a/lingo/invoicing/templates/lingo/invoicing/manager_campaign_detail.html b/lingo/invoicing/templates/lingo/invoicing/manager_campaign_detail.html
index bbe2c02..bd5262f 100644
--- a/lingo/invoicing/templates/lingo/invoicing/manager_campaign_detail.html
+++ b/lingo/invoicing/templates/lingo/invoicing/manager_campaign_detail.html
@@ -30,6 +30,7 @@
{% trans "Start date:" %} {{ object.date_start|date:'d/m/Y' }}
{% trans "End date:" %} {{ object.date_end|date:'d/m/Y' }}
{% trans "Issue date:" %} {{ object.date_issue|date:'d/m/Y' }}
+ {% trans "Integrate injected lines:" %} {{ object.get_injected_lines_display }}
diff --git a/lingo/invoicing/utils.py b/lingo/invoicing/utils.py
index 862bbcd..4b7a1cc 100644
--- a/lingo/invoicing/utils.py
+++ b/lingo/invoicing/utils.py
@@ -143,42 +143,47 @@ def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, user
)
)
- # fetch injected lines
- injected_lines = (
- InjectedLine.objects.filter(
- event_date__gte=pool.campaign.date_start,
- event_date__lt=pool.campaign.date_end,
- user_external_id=user_external_id,
- )
- .exclude(
- # exclude already invoiced lines
- invoiceline__isnull=False
- )
- .exclude(
- # exclude lines used in another campaign
- pk__in=DraftInvoiceLine.objects.filter(from_injected_line__isnull=False)
- .exclude(pool__campaign=pool.campaign)
- .values('from_injected_line')
- )
- )
-
- for injected_line in injected_lines:
- lines.append(
- DraftInvoiceLine(
- event_date=injected_line.event_date,
- slug=injected_line.slug,
- label=injected_line.label,
- quantity=injected_line.quantity,
- unit_amount=injected_line.unit_amount,
- total_amount=injected_line.total_amount,
+ if pool.campaign.injected_lines != 'no':
+ # fetch injected lines
+ injected_lines = (
+ InjectedLine.objects.filter(
+ event_date__lt=pool.campaign.date_end,
user_external_id=user_external_id,
- user_name=user_name,
- payer_external_id=injected_line.user_external_id,
- status='success',
- pool=pool,
- from_injected_line=injected_line,
)
+ .exclude(
+ # exclude already invoiced lines
+ invoiceline__isnull=False
+ )
+ .exclude(
+ # exclude lines used in another campaign
+ pk__in=DraftInvoiceLine.objects.filter(from_injected_line__isnull=False)
+ .exclude(pool__campaign=pool.campaign)
+ .values('from_injected_line')
+ )
+ .order_by('event_date')
)
+ if pool.campaign.injected_lines == 'period':
+ injected_lines = injected_lines.filter(
+ event_date__gte=pool.campaign.date_start,
+ )
+
+ for injected_line in injected_lines:
+ lines.append(
+ DraftInvoiceLine(
+ event_date=injected_line.event_date,
+ slug=injected_line.slug,
+ label=injected_line.label,
+ quantity=injected_line.quantity,
+ unit_amount=injected_line.unit_amount,
+ total_amount=injected_line.total_amount,
+ user_external_id=user_external_id,
+ user_name=user_name,
+ payer_external_id=injected_line.user_external_id,
+ status='success',
+ pool=pool,
+ from_injected_line=injected_line,
+ )
+ )
DraftInvoiceLine.objects.bulk_create(lines)
diff --git a/lingo/invoicing/views.py b/lingo/invoicing/views.py
index 3472e61..ca5e256 100644
--- a/lingo/invoicing/views.py
+++ b/lingo/invoicing/views.py
@@ -198,6 +198,7 @@ regies_import = RegiesImportView.as_view()
class CampaignListView(ListView):
template_name = 'lingo/invoicing/manager_campaign_list.html'
model = Campaign
+ ordering = '-date_start'
campaign_list = CampaignListView.as_view()
diff --git a/tests/invoicing/manager/test_campaign.py b/tests/invoicing/manager/test_campaign.py
index 17bb2ed..3fb2f7b 100644
--- a/tests/invoicing/manager/test_campaign.py
+++ b/tests/invoicing/manager/test_campaign.py
@@ -784,6 +784,7 @@ def test_journal_pool_lines(app, admin_user, draft):
invoice_model = DraftInvoice if draft else Invoice
line_model = DraftInvoiceLine if draft else InvoiceLine
+ regie = Regie.objects.create(label='Foo')
campaign = Campaign.objects.create(
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
@@ -871,6 +872,15 @@ def test_journal_pool_lines(app, admin_user, draft):
lines[-2].error_status = 'fixed'
lines[-2].save()
+ injected_line = InjectedLine.objects.create(
+ event_date=datetime.date(2022, 9, 1),
+ quantity=1,
+ unit_amount=1,
+ total_amount=1,
+ user_external_id='user:2',
+ payer_external_id='payer:2',
+ regie=regie,
+ )
lines.append(
line_model.objects.create(
event_date=datetime.date(2022, 9, 1),
@@ -884,6 +894,7 @@ def test_journal_pool_lines(app, admin_user, draft):
event={'event': 'foobar2'},
user_external_id='user:2',
payer_external_id='payer:2',
+ from_injected_line=injected_line,
),
)
@@ -1005,7 +1016,10 @@ def test_journal_pool_lines(app, admin_user, draft):
"{'error': 'PricingBookingCheckTypeError', 'error_details': {'check_type': 'foo-reason', "
"'check_type_group': 'foo-bar', 'reason': 'wrong-kind'}} {'event': 'foobar'}"
)
- assert format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[13].pk).text()) == 'Success'
+ assert (
+ format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[13].pk).text())
+ == 'Success (Injected)'
+ )
assert (
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[13].pk).text().strip()
== "{'foo': 'bar'} {'event': 'foobar2'}"
@@ -1064,6 +1078,11 @@ def test_journal_pool_lines(app, admin_user, draft):
params={'status': 'success'},
)
assert len(resp.pyquery('tr td.status')) == 2
+ resp = app.get(
+ '/manage/invoicing/campaign/%s/pool/%s/journal/' % (campaign.pk, pool.pk),
+ params={'status': 'success_injected'},
+ )
+ assert len(resp.pyquery('tr td.status')) == 1
resp = app.get(
'/manage/invoicing/campaign/%s/pool/%s/journal/' % (campaign.pk, pool.pk),
params={'status': 'warning'},
diff --git a/tests/invoicing/test_invoice_generation.py b/tests/invoicing/test_invoice_generation.py
index 74aa159..e9c6944 100644
--- a/tests/invoicing/test_invoice_generation.py
+++ b/tests/invoicing/test_invoice_generation.py
@@ -222,9 +222,10 @@ def test_get_invoice_lines_for_user_check_status_error(mock_status):
)
+@pytest.mark.parametrize('injected_lines', ['no', 'period', 'all'])
@mock.patch('lingo.invoicing.utils.get_check_status')
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data_for_event')
-def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_status):
+def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_status, injected_lines):
regie = Regie.objects.create(label='Regie')
agenda1 = Agenda.objects.create(label='Agenda 1')
agenda2 = Agenda.objects.create(label='Agenda 2')
@@ -239,6 +240,7 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_issue=datetime.date(2022, 10, 31),
+ injected_lines=injected_lines,
)
old_pool = Pool.objects.create(
campaign=campaign,
@@ -259,8 +261,8 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
)
# create some injected lines
- InjectedLine.objects.create(
- event_date=datetime.date(2022, 8, 31), # too soon
+ injected_line1 = InjectedLine.objects.create(
+ event_date=datetime.date(2022, 8, 31), # before the period
slug='event-2022-08-31',
label='Event 2022-08-31',
quantity=2,
@@ -387,7 +389,12 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
user_name='User1 Name1',
pool=pool,
)
- assert len(lines) == 2 # injected lines
+ if injected_lines == 'no':
+ assert len(lines) == 0
+ elif injected_lines == 'period':
+ assert len(lines) == 2 # injected lines
+ else:
+ assert len(lines) == 3 # injected lines
assert mock_status.call_args_list == [
mock.call(
agenda_slugs=['agenda-1', 'agenda-2'],
@@ -511,8 +518,15 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
adult_external_id='user:1',
),
]
- assert len(lines) == 6
- line1, line2, line3, line4, line5, line6 = lines
+ if injected_lines == 'no':
+ assert len(lines) == 4
+ line1, line2, line3, line4 = lines
+ elif injected_lines == 'period':
+ assert len(lines) == 6
+ line1, line2, line3, line4, line6, line7 = lines
+ else:
+ assert len(lines) == 7
+ line1, line2, line3, line4, line5, line6, line7 = lines
assert isinstance(line1, DraftInvoiceLine)
assert line1.invoice is None
assert line1.event_date == datetime.date(2022, 9, 1)
@@ -597,38 +611,56 @@ def test_get_invoice_lines_for_user_check_status(mock_pricing_data_event, mock_s
assert line4.status == 'success'
assert line4.pool == pool
assert line4.from_injected_line is None
- assert isinstance(line5, DraftInvoiceLine)
- assert line5.invoice is None
- assert line5.event_date == injected_line2.event_date
- assert line5.slug == 'event-2022-09-01'
- assert line5.label == 'Event 2022-09-01'
- assert line5.quantity == 2
- assert line5.unit_amount == 1.5
- assert line5.total_amount == 3
- assert line5.user_external_id == 'user:1'
- assert line5.user_name == 'User1 Name1'
- assert line5.payer_external_id == 'user:1'
- assert line5.event == {}
- assert line5.pricing_data == {}
- assert line5.status == 'success'
- assert line5.pool == pool
- assert line5.from_injected_line == injected_line2
- assert isinstance(line6, DraftInvoiceLine)
- assert line6.invoice is None
- assert line6.event_date == injected_line4.event_date
- assert line6.slug == 'event-2022-09-30'
- assert line6.label == 'Event 2022-09-30'
- assert line6.quantity == 3
- assert line6.unit_amount == 1.5
- assert line6.total_amount == 4.5
- assert line6.user_external_id == 'user:1'
- assert line6.user_name == 'User1 Name1'
- assert line6.payer_external_id == 'user:1'
- assert line6.event == {}
- assert line6.pricing_data == {}
- assert line6.status == 'success'
- assert line6.pool == pool
- assert line6.from_injected_line == injected_line4
+ if injected_lines != 'no':
+ if injected_lines == 'all':
+ assert isinstance(line5, DraftInvoiceLine)
+ assert line5.invoice is None
+ assert line5.event_date == injected_line1.event_date
+ assert line5.slug == 'event-2022-08-31'
+ assert line5.label == 'Event 2022-08-31'
+ assert line5.quantity == 2
+ assert line5.unit_amount == 1.5
+ assert line5.total_amount == 3
+ assert line5.user_external_id == 'user:1'
+ assert line5.user_name == 'User1 Name1'
+ assert line5.payer_external_id == 'user:1'
+ assert line5.event == {}
+ assert line5.pricing_data == {}
+ assert line5.status == 'success'
+ assert line5.pool == pool
+ assert line5.from_injected_line == injected_line1
+ assert isinstance(line6, DraftInvoiceLine)
+ assert line6.invoice is None
+ assert line6.event_date == injected_line2.event_date
+ assert line6.slug == 'event-2022-09-01'
+ assert line6.label == 'Event 2022-09-01'
+ assert line6.quantity == 2
+ assert line6.unit_amount == 1.5
+ assert line6.total_amount == 3
+ assert line6.user_external_id == 'user:1'
+ assert line6.user_name == 'User1 Name1'
+ assert line6.payer_external_id == 'user:1'
+ assert line6.event == {}
+ assert line6.pricing_data == {}
+ assert line6.status == 'success'
+ assert line6.pool == pool
+ assert line6.from_injected_line == injected_line2
+ assert isinstance(line7, DraftInvoiceLine)
+ assert line7.invoice is None
+ assert line7.event_date == injected_line4.event_date
+ assert line7.slug == 'event-2022-09-30'
+ assert line7.label == 'Event 2022-09-30'
+ assert line7.quantity == 3
+ assert line7.unit_amount == 1.5
+ assert line7.total_amount == 4.5
+ assert line7.user_external_id == 'user:1'
+ assert line7.user_name == 'User1 Name1'
+ assert line7.payer_external_id == 'user:1'
+ assert line7.event == {}
+ assert line7.pricing_data == {}
+ assert line7.status == 'success'
+ assert line7.pool == pool
+ assert line7.from_injected_line == injected_line4
@mock.patch('lingo.invoicing.utils.get_check_status')
@@ -1016,6 +1048,7 @@ def test_get_all_invoice_lines_queryset(mock_status):
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 11, 1),
date_issue=datetime.date(2022, 11, 30),
+ injected_lines='all',
)
pool = Pool.objects.create(
campaign=campaign,