wip/73742-invoicing-configure-injected-lines-in-campaign (#73742) #23

Merged
lguerin merged 2 commits from wip/73742-invoicing-configure-injected-lines-in-campaign into main 2023-02-04 15:47:00 +01:00
8 changed files with 170 additions and 73 deletions

View File

@ -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':

View File

@ -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',
),
),
]

View File

@ -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',
Outdated
Review

J'aurais bien mis "all" par défaut, mais on ne va pas chipoter, et je ne sais pas bien l'usage qui sera fait en vrai... on verra bien à ce moment là.

J'aurais bien mis "all" par défaut, mais on ne va pas chipoter, et je ne sais pas bien l'usage qui sera fait en vrai... on verra bien à ce moment là.
max_length=10,
)
def __str__(self):
return _('From %(start)s to %(end)s') % {

View File

@ -30,6 +30,7 @@
<li>{% trans "Start date:" %} {{ object.date_start|date:'d/m/Y' }}</li>
<li>{% trans "End date:" %} {{ object.date_end|date:'d/m/Y' }}</li>
<li>{% trans "Issue date:" %} {{ object.date_issue|date:'d/m/Y' }}</li>
<li>{% trans "Integrate injected lines:" %} {{ object.get_injected_lines_display }}</li>
</ul>
</div>

View File

@ -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)

View File

@ -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()

View File

@ -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'},

View File

@ -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,