Compare commits

...

10 Commits

Author SHA1 Message Date
Lauréline Guérin 3b3dcf1c7f
invoicing: don't delete cancellation reason used on credit (#89810)
gitea/lingo/pipeline/head This commit looks good Details
2024-04-23 13:32:44 +02:00
Lauréline Guérin 904203c808
basket: don't assign cancelled credits (#89810) 2024-04-23 13:32:44 +02:00
Lauréline Guérin b48de48412
api: exclude cancelled credits (#89810) 2024-04-23 13:32:44 +02:00
Lauréline Guérin 9352f2d0c7
invoicing: display and filter cancelled credits (#89810) 2024-04-23 11:53:35 +02:00
Lauréline Guérin 73f2cafd40
invoicing: credit cancellation fields (#89810) 2024-04-23 11:53:35 +02:00
Lauréline Guérin 2f9f811fe2
basket: don't assign non published credits (#89810) 2024-04-23 11:53:35 +02:00
Lauréline Guérin 5d7b9e9a66
api: exclude non published credits (#89810) 2024-04-23 11:23:33 +02:00
Lauréline Guérin 6e51ef0545
misc: remove unused variable (#89810) 2024-04-23 10:03:05 +02:00
Lauréline Guérin 21045178b6
invoicing: promote pool, generate credit if draft invoice is negative (#89810) 2024-04-23 10:03:05 +02:00
Lauréline Guérin 6f45045d85
api: generate credit if closed draft invoice is negative (#89810) 2024-04-23 10:02:59 +02:00
20 changed files with 1162 additions and 171 deletions

View File

@ -17,6 +17,7 @@
import datetime
import uuid
from django.db.models import Q
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
@ -402,7 +403,13 @@ class RefundSerializer(serializers.ModelSerializer):
def validate_credit(self, value):
try:
credit = Credit.objects.get(uuid=value, regie=self.regie)
credit = Credit.objects.get(
Q(pool__isnull=True) | Q(pool__campaign__finalized=True),
uuid=value,
regie=self.regie,
date_publication__lte=datetime.date.today(),
cancelled_at__isnull=True,
)
except Credit.DoesNotExist:
raise ValidationError(_('Unknown credit.'))
if credit.remaining_amount == 0:

View File

@ -399,9 +399,11 @@ class InvoicingDraftInvoiceClose(APIView):
def post(self, request, regie_identifier, invoice_identifier):
regie = get_object_or_404(Regie, slug=regie_identifier)
invoice = get_object_or_404(DraftInvoice, regie=regie, uuid=invoice_identifier, pool__isnull=True)
final_invoice = invoice.promote()
final_object = invoice.promote()
return Response({'data': {'invoice_id': str(final_invoice.uuid)}})
if isinstance(final_object, Invoice):
return Response({'data': {'invoice_id': str(final_object.uuid)}})
return Response({'data': {'credit_id': str(final_object.uuid)}})
invoicing_draft_invoice_close = InvoicingDraftInvoiceClose.as_view()
@ -517,9 +519,12 @@ class InvoicingCredits(PayerMixin, APIView):
payer_external_id=request.GET.get('payer_external_id'),
)
return Credit.objects.filter(
Q(pool__isnull=True) | Q(pool__campaign__finalized=True),
regie=regie,
remaining_amount__gt=0,
payer_external_id=payer_external_id,
date_publication__lte=datetime.date.today(),
cancelled_at__isnull=True,
).order_by('-created_at')
def get(self, request, regie_identifier):
@ -557,9 +562,12 @@ class InvoicingHistoryCredits(InvoicingCredits):
payer_external_id=request.GET.get('payer_external_id'),
)
return Credit.objects.filter(
Q(pool__isnull=True) | Q(pool__campaign__finalized=True),
regie=regie,
remaining_amount=0,
payer_external_id=payer_external_id,
date_publication__lte=datetime.date.today(),
cancelled_at__isnull=True,
).order_by('-created_at')
@ -579,9 +587,12 @@ class InvoicingCreditPDF(PayerMixin, APIView):
)
credit = get_object_or_404(
Credit,
Q(pool__isnull=True) | Q(pool__campaign__finalized=True),
uuid=credit_identifier,
regie=regie,
payer_external_id=payer_external_id,
date_publication__lte=datetime.date.today(),
cancelled_at__isnull=True,
)
result = credit.html()
html = HTML(string=result)

View File

@ -164,7 +164,12 @@ class Basket(models.Model):
if self.total_amount < 0:
return 0
credit_qs = Credit.objects.filter(
remaining_amount__gt=0, regie=self.regie, payer_external_id=self.payer_external_id
models.Q(pool__isnull=True) | models.Q(pool__campaign__finalized=True),
date_publication__lte=datetime.date.today(),
cancelled_at__isnull=True,
remaining_amount__gt=0,
regie=self.regie,
payer_external_id=self.payer_external_id,
)
available_credit = sum(c.remaining_amount for c in credit_qs)
return -min(self.total_amount, available_credit)

View File

@ -1080,6 +1080,16 @@ class RegieCreditFilterSet(AgendaFieldsFilterSetMixin, django_filters.FilterSet)
],
method='filter_assigned',
)
cancelled = django_filters.ChoiceFilter(
label=_('Cancelled'),
widget=forms.RadioSelect,
empty_label=_('all'),
choices=[
('yes', _('Yes')),
('no', _('No')),
],
method='filter_cancelled',
)
agenda = django_filters.ChoiceFilter(
label=_('Activity'),
empty_label=_('all'),
@ -1137,6 +1147,13 @@ class RegieCreditFilterSet(AgendaFieldsFilterSetMixin, django_filters.FilterSet)
return queryset.filter(assigned_amount=0)
return queryset
def filter_cancelled(self, queryset, name, value):
if not value:
return queryset
if value == 'yes':
return queryset.filter(cancelled_at__isnull=False)
return queryset.filter(cancelled_at__isnull=True)
def filter_agenda(self, queryset, name, value):
if not value:
return queryset

View File

@ -0,0 +1,36 @@
import datetime
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('invoicing', '0087_cancellation_reason'),
]
operations = [
migrations.AddField(
model_name='credit',
name='date_publication',
field=models.DateField(default=datetime.date.today, verbose_name='Publication date'),
preserve_default=False,
),
migrations.AddField(
model_name='credit',
name='pool',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.PROTECT, to='invoicing.pool'
),
),
migrations.AddField(
model_name='journalline',
name='credit_line',
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name='journal_lines',
to='invoicing.creditline',
),
),
]

View File

@ -0,0 +1,40 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('invoicing', '0088_credit_from_pool'),
]
operations = [
migrations.AddField(
model_name='credit',
name='cancellation_description',
field=models.TextField(blank=True, verbose_name='Description'),
),
migrations.AddField(
model_name='credit',
name='cancellation_reason',
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to='invoicing.invoicecancellationreason',
verbose_name='Cancellation reason',
),
),
migrations.AddField(
model_name='credit',
name='cancelled_at',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='credit',
name='cancelled_by',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL
),
),
]

View File

@ -837,12 +837,13 @@ class DraftJournalLine(AbstractJournalLine):
'invoicing.DraftInvoiceLine', on_delete=models.PROTECT, null=True, related_name='journal_lines'
)
def promote(self, pool=None, invoice_line=None):
def promote(self, pool=None, invoice_line=None, credit_line=None):
final_line = copy.deepcopy(self)
final_line.__class__ = JournalLine
final_line.pk = None
final_line.pool = pool
final_line.invoice_line = invoice_line
final_line.credit_line = credit_line
final_line.error_status = ''
final_line.save()
@ -851,6 +852,9 @@ class JournalLine(AbstractJournalLine):
invoice_line = models.ForeignKey(
'invoicing.InvoiceLine', on_delete=models.PROTECT, null=True, related_name='journal_lines'
)
credit_line = models.ForeignKey(
'invoicing.CreditLine', on_delete=models.PROTECT, null=True, related_name='journal_lines'
)
class Counter(models.Model):
@ -891,6 +895,9 @@ class AbstractInvoiceObject(models.Model):
payer_last_name = models.CharField(max_length=250)
payer_address = models.TextField()
date_publication = models.DateField(_('Publication date'))
pool = models.ForeignKey(Pool, on_delete=models.PROTECT, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
@ -909,15 +916,12 @@ class AbstractInvoiceObject(models.Model):
class AbstractInvoice(AbstractInvoiceObject):
date_publication = models.DateField(_('Publication date'))
date_payment_deadline = models.DateField(_('Payment deadline'))
date_due = models.DateField(_('Due date'))
date_debit = models.DateField(_('Debit date'), null=True)
payer_demat = models.BooleanField(default=False)
payer_direct_debit = models.BooleanField(default=False)
pool = models.ForeignKey(Pool, on_delete=models.PROTECT, null=True)
payment_callback_url = models.URLField(blank=True)
class Meta:
@ -998,7 +1002,7 @@ class DraftInvoice(AbstractInvoice):
def promote(self, pool=None):
if self.total_amount >= 0:
return self.promote_into_invoice(pool=pool)
return self.promote_into_credit()
return self.promote_into_credit(pool=pool)
def promote_into_invoice(self, pool=None):
final_invoice = copy.deepcopy(self)
@ -1020,19 +1024,24 @@ class DraftInvoice(AbstractInvoice):
return final_invoice
def promote_into_credit(self):
def promote_into_credit(self, pool=None):
credit = copy.deepcopy(self)
credit.__class__ = Credit
credit.pk = None
credit.uuid = uuid.uuid4()
credit.pool = pool
credit.set_number()
credit.assigned_amount = 0
credit.remaining_amount = 0
credit.label = _('Credit from %s') % datetime.date.today().strftime('%d/%m/%Y')
credit.cancelled_at = None
credit.cancelled_by = None
credit.cancellation_reason = None
credit.cancellation_description = ''
credit.save()
for line in self.lines.all().order_by('pk'):
line.promote_into_credit(credit=credit)
line.promote_into_credit(pool=pool, credit=credit)
return credit
@ -1058,6 +1067,19 @@ class InvoiceCancellationReason(models.Model):
return slugify(self.label)
def get_cancellation_info(obj):
result = []
if not obj.cancelled_at:
return result
result.append((_('Cancelled on'), obj.cancelled_at.strftime('%d/%m/%Y %H:%M')))
if obj.cancelled_by:
result.append((_('Cancelled by'), obj.cancelled_by))
result.append((_('Reason'), obj.cancellation_reason))
if obj.cancellation_description:
result.append((_('Description'), linebreaksbr(obj.cancellation_description)))
return result
class Invoice(AbstractInvoice):
number = models.PositiveIntegerField(default=0)
formatted_number = models.CharField(max_length=200)
@ -1126,16 +1148,7 @@ class Invoice(AbstractInvoice):
return sorted(invoice_payments.values(), key=lambda a: a.payment.created_at)
def get_cancellation_info(self):
result = []
if not self.cancelled_at:
return result
result.append((_('Cancelled on'), self.cancelled_at.strftime('%d/%m/%Y %H:%M')))
if self.cancelled_by:
result.append((_('Cancelled by'), self.cancelled_by))
result.append((_('Reason'), self.cancellation_reason))
if self.cancellation_description:
result.append((_('Description'), linebreaksbr(self.cancellation_description)))
return result
return get_cancellation_info(self)
class Credit(AbstractInvoiceObject):
@ -1143,6 +1156,12 @@ class Credit(AbstractInvoiceObject):
formatted_number = models.CharField(max_length=200)
assigned_amount = models.DecimalField(max_digits=9, decimal_places=2, default=0)
remaining_amount = models.DecimalField(max_digits=9, decimal_places=2, default=0)
cancelled_at = models.DateTimeField(null=True)
cancelled_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
cancellation_reason = models.ForeignKey(
InvoiceCancellationReason, verbose_name=_('Cancellation reason'), on_delete=models.PROTECT, null=True
)
cancellation_description = models.TextField(_('Description'), blank=True)
class Meta:
constraints = [
@ -1207,6 +1226,9 @@ class Credit(AbstractInvoiceObject):
}
return template.render(context)
def get_cancellation_info(self):
return get_cancellation_info(self)
class AbstractInvoiceLineObject(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, null=True)
@ -1264,7 +1286,7 @@ class AbstractInvoiceLine(AbstractInvoiceLineObject):
class DraftInvoiceLine(AbstractInvoiceLine):
invoice = models.ForeignKey(DraftInvoice, on_delete=models.PROTECT, null=True, related_name='lines')
def promote(self, pool=None, invoice=None):
def promote(self, invoice, pool=None):
final_line = copy.deepcopy(self)
final_line.__class__ = InvoiceLine
final_line.pk = None
@ -1278,7 +1300,7 @@ class DraftInvoiceLine(AbstractInvoiceLine):
for line in self.journal_lines.all().order_by('pk'):
line.promote(pool=pool, invoice_line=final_line)
def promote_into_credit(self, credit=None):
def promote_into_credit(self, credit, pool=None):
credit_line = copy.deepcopy(self)
credit_line.__class__ = CreditLine
credit_line.pk = None
@ -1288,6 +1310,9 @@ class DraftInvoiceLine(AbstractInvoiceLine):
credit_line.remaining_amount = 0
credit_line.save()
for line in self.journal_lines.all().order_by('pk'):
line.promote(pool=pool, credit_line=credit_line)
class InvoiceLine(AbstractInvoiceLine):
invoice = models.ForeignKey(Invoice, on_delete=models.PROTECT, null=True, related_name='lines')
@ -1592,16 +1617,7 @@ class Payment(models.Model):
return result
def get_cancellation_info(self):
result = []
if not self.cancelled_at:
return result
result.append((_('Cancelled on'), self.cancelled_at.strftime('%d/%m/%Y %H:%M')))
if self.cancelled_by:
result.append((_('Cancelled by'), self.cancelled_by))
result.append((_('Reason'), self.cancellation_reason))
if self.cancellation_description:
result.append((_('Description'), linebreaksbr(self.cancellation_description)))
return result
return get_cancellation_info(self)
def get_invoice_payments(self):
if hasattr(self, 'prefetched_invoicelinepayments'):
@ -1691,11 +1707,15 @@ class CreditAssignment(models.Model):
@classmethod
def make_assignments(cls, regie, basket):
credit_qs = (
Credit.objects.select_for_update()
.filter(remaining_amount__gt=0, regie=regie, payer_external_id=basket.payer_external_id)
.order_by('pk')
credit_qs = Credit.objects.filter(
models.Q(pool__isnull=True) | models.Q(pool__campaign__finalized=True),
date_publication__lte=datetime.date.today(),
cancelled_at__isnull=True,
remaining_amount__gt=0,
regie=regie,
payer_external_id=basket.payer_external_id,
)
credit_qs = Credit.objects.select_for_update().filter(pk__in=credit_qs.values('id')).order_by('pk')
with transaction.atomic():
available_credit = sum(c.remaining_amount for c in credit_qs)
amount_to_assign = min(available_credit, basket.invoice.remaining_amount)

View File

@ -27,7 +27,7 @@
{% for reason in invoice_reason_list %}
<li>
<a href="{% url 'lingo-manager-invoicing-invoice-cancellation-reason-edit' reason.pk %}">{{ reason }}{% if reason.disabled %}<span class="extra-info"> ({% trans "disabled" %})</span>{% endif %}</a>
{% if not reason.invoice_set.exists %}
{% if not reason.invoice_set.exists and not reason.credit_set.exists %}
<a class="delete" rel="popup" href="{% url 'lingo-manager-invoicing-invoice-cancellation-reason-delete' reason.pk %}">{% trans "delete"%}</a>
{% endif %}
</li>

View File

@ -44,62 +44,73 @@
<td></td>
</tr>
{% endfor %}
<tr class="line" data-related-invoicing-element-id="{{ credit.pk }}">
<th colspan="7" class="assignments">
{% trans "Assignments" %}
</th>
</tr>
<tr class="line" data-related-invoicing-element-id="{{ credit.pk }}">
<td class="payment-num">{% trans "Payment" context 'payment' %}</td>
<td colspan="4">{% trans "Date" context 'payment' %}</td>
<td class="amount" colspan="2">{% trans "Amount" %}</td>
</tr>
{% for assignment in credit.creditassignment_set.all %}
{% if not credit.cancelled_at %}
<tr class="line" data-related-invoicing-element-id="{{ credit.pk }}">
{% if assignment.payment %}
<td>
<a href="{% url 'lingo-manager-invoicing-regie-payment-list' regie_pk=regie.pk %}?number={{ assignment.payment.formatted_number }}">{{ assignment.payment.formatted_number }}</a>
</td>
<td colspan="4">
{{ assignment.payment.created_at|date:"DATETIME_FORMAT" }}
</td>
{% elif assignment.invoice %}
<td>
<i>{% trans "Pending..." %}</i>
</td>
<td colspan="4"></td>
{% else %}
<td>
<a href="{% url 'lingo-manager-invoicing-regie-refund-list' regie_pk=regie.pk %}?number={{ assignment.refund.formatted_number }}">{{ assignment.refund.formatted_number }} ({% trans "Refund" %})</a>
</td>
<td colspan="4">
{{ assignment.refund.created_at|date:"DATETIME_FORMAT" }}
</td>
{% endif %}
<td class="amount" colspan="2">
{% blocktrans with amount=assignment.amount|floatformat:"2" %}{{ amount }}€{% endblocktrans %}
</td>
<th colspan="7" class="assignments">
{% trans "Assignments" %}
</th>
</tr>
{% empty %}
<tr class="line" data-related-invoicing-element-id="{{ credit.pk }}">
<td colspan="7" class="no-payments">
{% trans "No assignments for this credit" %}
</td>
</tr>
{% endfor %}
{% if credit.assigned_amount %}
<tr class="line {% if not credit.remaining_amount%}last-line{% endif %}" data-related-invoicing-element-id="{{ credit.pk }}">
<td colspan="2"></td>
<td class="invoice-details" colspan="5">
<i>{% trans "Assigned amount:" %} {% blocktrans with amount=credit.assigned_amount|floatformat:"2" %}{{ amount }}€{% endblocktrans %}</i>
</td>
</tr>
{% endif %}
{% if credit.remaining_amount %}
<tr class="line last-line" data-related-invoicing-element-id="{{ credit.pk }}">
<td colspan="2"></td>
<td class="invoice-details" colspan="5">
<i>{% trans "Remaining amount to assign:" %} {% blocktrans with amount=credit.remaining_amount|floatformat:"2" %}{{ amount }}€{% endblocktrans %}</i>
</td>
<td class="payment-num">{% trans "Payment" context 'payment' %}</td>
<td colspan="4">{% trans "Date" context 'payment' %}</td>
<td class="amount" colspan="2">{% trans "Amount" %}</td>
</tr>
{% for assignment in credit.creditassignment_set.all %}
<tr class="line" data-related-invoicing-element-id="{{ credit.pk }}">
{% if assignment.payment %}
<td>
<a href="{% url 'lingo-manager-invoicing-regie-payment-list' regie_pk=regie.pk %}?number={{ assignment.payment.formatted_number }}">{{ assignment.payment.formatted_number }}</a>
</td>
<td colspan="4">
{{ assignment.payment.created_at|date:"DATETIME_FORMAT" }}
</td>
{% elif assignment.invoice %}
<td>
<i>{% trans "Pending..." %}</i>
</td>
<td colspan="4"></td>
{% else %}
<td>
<a href="{% url 'lingo-manager-invoicing-regie-refund-list' regie_pk=regie.pk %}?number={{ assignment.refund.formatted_number }}">{{ assignment.refund.formatted_number }} ({% trans "Refund" %})</a>
</td>
<td colspan="4">
{{ assignment.refund.created_at|date:"DATETIME_FORMAT" }}
</td>
{% endif %}
<td class="amount" colspan="2">
{% blocktrans with amount=assignment.amount|floatformat:"2" %}{{ amount }}€{% endblocktrans %}
</td>
</tr>
{% empty %}
<tr class="line" data-related-invoicing-element-id="{{ credit.pk }}">
<td colspan="7" class="no-payments">
{% trans "No assignments for this credit" %}
</td>
</tr>
{% endfor %}
{% if credit.assigned_amount %}
<tr class="line {% if not credit.remaining_amount%}last-line{% endif %}" data-related-invoicing-element-id="{{ credit.pk }}">
<td colspan="2"></td>
<td class="invoice-details" colspan="5">
<i>{% trans "Assigned amount:" %} {% blocktrans with amount=credit.assigned_amount|floatformat:"2" %}{{ amount }}€{% endblocktrans %}</i>
</td>
</tr>
{% endif %}
{% if credit.remaining_amount %}
<tr class="line last-line" data-related-invoicing-element-id="{{ credit.pk }}">
<td colspan="2"></td>
<td class="invoice-details" colspan="5">
<i>{% trans "Remaining amount to assign:" %} {% blocktrans with amount=credit.remaining_amount|floatformat:"2" %}{{ amount }}€{% endblocktrans %}</i>
</td>
</tr>
{% endif %}
{% else %}
{% for label, value in credit.get_cancellation_info %}
<tr class="line {% if forloop.last %}last-line{% endif %}" data-related-invoicing-element-id="{{ credit.pk }}">
<td colspan="2"></td>
<td class="invoice-details" colspan="5">
<i>{% blocktrans %}{{ label }}:{% endblocktrans %} {{ value }}</i>
</td>
</tr>
{% endfor %}
{% endif %}

View File

@ -31,7 +31,9 @@
{% for credit in object_list %}
<tr class="credit untoggled" data-invoicing-element-id="{{ credit.pk }}" data-invoicing-element-lines-url="{% url 'lingo-manager-invoicing-regie-credit-line-list' regie_pk=regie.pk credit_pk=credit.pk %}">
<td colspan="6">
{% if credit.remaining_amount > 0 and credit.assigned_amount > 0 %}
{% if credit.cancelled_at %}
<span class="meta meta-error">{% trans "Cancelled" context "credit" %}</span>
{% elif credit.remaining_amount > 0 and credit.assigned_amount > 0 %}
<span class="meta meta-warning">{% trans "Partially assigned" %}</span>
{% elif credit.remaining_amount == 0 %}
<span class="meta meta-success">{% trans "Assigned" %}</span>

View File

@ -66,7 +66,7 @@ class InvoiceReasonDeleteView(DeleteView):
model = InvoiceCancellationReason
def get_queryset(self):
return InvoiceCancellationReason.objects.filter(invoice__isnull=True)
return InvoiceCancellationReason.objects.filter(invoice__isnull=True, credit__isnull=True)
def get_success_url(self):
return '%s#open:invoice' % reverse('lingo-manager-invoicing-cancellation-reason-list')

View File

@ -977,7 +977,7 @@ def test_detail_invoice(mock_payer, app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/invoice/%s/' % str(uuid.uuid4()), status=404)
resp = app.get('/api/regie/foo/invoice/%s/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404)
app.get('/api/regie/foo/invoice/%s/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404)
invoice = Invoice.objects.create(
label='My invoice',
@ -1027,14 +1027,14 @@ def test_detail_invoice(mock_payer, app, user):
# publication date is in the future
invoice.date_publication = datetime.date.today() + datetime.timedelta(days=1)
invoice.save()
resp = app.get('/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404)
app.get('/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404)
# other regie
other_regie = Regie.objects.create(label='Other Foo')
invoice.date_publication = datetime.date.today()
invoice.regie = other_regie
invoice.save()
resp = app.get('/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404)
app.get('/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404)
# part of amount was already paid
invoice.regie = regie
@ -1108,7 +1108,7 @@ def test_detail_invoice(mock_payer, app, user):
invoice_line_payment.amount = 1
invoice_line_payment.save()
mock_payer.return_value = 'payer:unknown'
resp = app.get('/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404)
app.get('/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404)
# campaign is not finalized
mock_payer.return_value = 'payer:1'
@ -1151,7 +1151,7 @@ def test_detail_invoice_for_payer(app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/invoice/%s/' % str(uuid.uuid4()), status=404)
resp = app.get(
app.get(
'/api/regie/foo/invoice/%s/' % str(uuid.uuid4()), params={'payer_external_id': 'payer:1'}, status=404
)
@ -1202,7 +1202,7 @@ def test_detail_invoice_for_payer(app, user):
# publication date is in the future
invoice.date_publication = datetime.date.today() + datetime.timedelta(days=1)
invoice.save()
resp = app.get(
app.get(
'/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'payer_external_id': 'payer:1'}, status=404
)
@ -1211,7 +1211,7 @@ def test_detail_invoice_for_payer(app, user):
invoice.date_publication = datetime.date.today()
invoice.regie = other_regie
invoice.save()
resp = app.get(
app.get(
'/api/regie/foo/invoice/%s/' % str(invoice.uuid), params={'payer_external_id': 'payer:1'}, status=404
)
@ -1331,9 +1331,7 @@ def test_pdf_invoice(mock_payer, app, user):
regie = Regie.objects.create(label='Foo')
app.get('/api/regie/foo/invoice/%s/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
'/api/regie/foo/invoice/%s/pdf/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404
)
app.get('/api/regie/foo/invoice/%s/pdf/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404)
invoice = Invoice.objects.create(
label='My invoice',
@ -1360,9 +1358,7 @@ def test_pdf_invoice(mock_payer, app, user):
# publication date is in the future
invoice.date_publication = datetime.date.today() + datetime.timedelta(days=1)
invoice.save()
resp = app.get(
'/api/regie/foo/invoice/%s/pdf/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404
)
app.get('/api/regie/foo/invoice/%s/pdf/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404)
# other regie
other_regie = Regie.objects.create(label='Other Foo')
@ -1422,7 +1418,7 @@ def test_pdf_invoice_for_payer(app, user):
regie = Regie.objects.create(label='Foo')
app.get('/api/regie/foo/invoice/%s/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
app.get(
'/api/regie/foo/invoice/%s/pdf/' % str(uuid.uuid4()),
params={'payer_external_id': 'payer:1'},
status=404,
@ -1525,7 +1521,7 @@ def test_pdf_invoice_payments(mock_payer, app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/invoice/%s/payments/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
app.get(
'/api/regie/foo/invoice/%s/payments/pdf/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404
)
@ -1568,7 +1564,7 @@ def test_pdf_invoice_payments(mock_payer, app, user):
# publication date is in the future
invoice.date_publication = datetime.date.today() + datetime.timedelta(days=1)
invoice.save()
resp = app.get(
app.get(
'/api/regie/foo/invoice/%s/payments/pdf/' % str(invoice.uuid), params={'NameID': 'foobar'}, status=404
)
@ -1658,7 +1654,7 @@ def test_pdf_invoice_payments_for_payer(app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/invoice/%s/payments/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
app.get(
'/api/regie/foo/invoice/%s/payments/pdf/' % str(uuid.uuid4()),
params={'payer_external_id': 'payer:1'},
status=404,
@ -1809,12 +1805,12 @@ def test_pay_invoice(app, user):
invoice.refresh_from_db()
# no payment type 'online'
resp = app.post('/api/regie/foo/invoice/%s/pay/' % str(invoice.uuid), status=404)
app.post('/api/regie/foo/invoice/%s/pay/' % str(invoice.uuid), status=404)
# no payment type 'online' for the regie
other_regie = Regie.objects.create(label='Bar')
PaymentType.create_defaults(other_regie)
resp = app.post('/api/regie/foo/invoice/%s/pay/' % str(invoice.uuid), status=404)
app.post('/api/regie/foo/invoice/%s/pay/' % str(invoice.uuid), status=404)
PaymentType.create_defaults(regie) # create default payment types
resp = app.post('/api/regie/foo/invoice/%s/pay/' % str(invoice.uuid))
@ -2199,6 +2195,7 @@ def test_close_draft_invoice(app, user):
resp = app.post('/api/regie/foo/draft-invoice/%s/close/' % str(invoice.uuid))
assert resp.json['err'] == 0
assert Credit.objects.count() == 0
final_invoice = Invoice.objects.latest('pk')
today = datetime.date.today()
assert resp.json['data'] == {'invoice_id': str(final_invoice.uuid)}
@ -2256,12 +2253,63 @@ def test_close_draft_invoice(app, user):
invoice.save()
app.post('/api/regie/foo/draft-invoice/%s/close/' % str(invoice.uuid), status=404)
invoice.pool = None
invoice.save()
line2 = DraftInvoiceLine.objects.create(
event_date=datetime.date(2023, 4, 21),
quantity=-1,
unit_amount=45,
invoice=invoice,
)
line2.refresh_from_db()
invoice.refresh_from_db()
final_line.delete()
final_invoice.delete()
resp = app.post('/api/regie/foo/draft-invoice/%s/close/' % str(invoice.uuid))
assert resp.json['err'] == 0
assert Invoice.objects.count() == 0
credit = Credit.objects.latest('pk')
assert resp.json['data'] == {'credit_id': str(credit.uuid)}
assert credit.regie == regie
assert credit.payer_external_id == invoice.payer_external_id
assert credit.payer_first_name == invoice.payer_first_name
assert credit.payer_last_name == invoice.payer_last_name
assert credit.payer_address == invoice.payer_address
assert credit.total_amount == -invoice.total_amount == 3
assert credit.number == 1
assert credit.formatted_number == 'A%02d-%s-0000001' % (regie.pk, today.strftime('%y-%m'))
assert credit.pool is None
assert credit.date_publication == datetime.date(2023, 4, 21)
credit_line1 = CreditLine.objects.order_by('pk')[0]
assert credit_line1.event_date == line.event_date
assert credit_line1.slug == line.slug
assert credit_line1.label == line.label
assert credit_line1.quantity == -line.quantity
assert credit_line1.unit_amount == line.unit_amount
assert credit_line1.total_amount == -line.total_amount
assert credit_line1.user_external_id == line.user_external_id
assert credit_line1.user_first_name == line.user_first_name
assert credit_line1.user_last_name == line.user_last_name
assert credit_line1.credit == credit
credit_line2 = CreditLine.objects.order_by('pk')[1]
assert credit_line2.event_date == line2.event_date
assert credit_line2.slug == line2.slug
assert credit_line2.label == line2.label
assert credit_line2.quantity == -line2.quantity
assert credit_line2.unit_amount == line2.unit_amount
assert credit_line2.total_amount == -line2.total_amount
assert credit_line2.user_external_id == line2.user_external_id
assert credit_line2.user_first_name == line2.user_first_name
assert credit_line2.user_last_name == line2.user_last_name
assert credit_line2.credit == credit
def test_add_injected_line(app, user):
app.post('/api/regie/foo/injected-lines/', status=403)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post('/api/regie/foo/injected-lines/', status=404)
app.post('/api/regie/foo/injected-lines/', status=404)
regie = Regie.objects.create(slug='foo')
resp = app.post('/api/regie/foo/injected-lines/', status=400)
@ -2315,7 +2363,7 @@ def test_add_payment(app, user):
app.post('/api/regie/foo/payments/', status=403)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post('/api/regie/foo/payments/', status=404)
app.post('/api/regie/foo/payments/', status=404)
regie = Regie.objects.create(slug='foo')
resp = app.post('/api/regie/foo/payments/', status=400)
@ -3015,9 +3063,7 @@ def test_pdf_payment(mock_payer, app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/payment/%s/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
'/api/regie/foo/payment/%s/pdf/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404
)
app.get('/api/regie/foo/payment/%s/pdf/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404)
invoice = Invoice.objects.create(
label='My invoice',
@ -3056,13 +3102,11 @@ def test_pdf_payment(mock_payer, app, user):
other_regie = Regie.objects.create(label='Other Foo')
payment.regie = other_regie
payment.save()
resp = app.get(
'/api/regie/foo/payment/%s/pdf/' % str(payment.uuid), params={'NameID': 'foobar'}, status=404
)
app.get('/api/regie/foo/payment/%s/pdf/' % str(payment.uuid), params={'NameID': 'foobar'}, status=404)
# no matching payer id
mock_payer.return_value = 'payer:unknown'
resp = app.get('/api/regie/foo/payment/%s/' % str(payment.uuid), params={'NameID': 'foobar'}, status=404)
app.get('/api/regie/foo/payment/%s/' % str(payment.uuid), params={'NameID': 'foobar'}, status=404)
# payer error
mock_payer.side_effect = PayerError
@ -3079,7 +3123,7 @@ def test_pdf_payment_for_payer(app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/payment/%s/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
app.get(
'/api/regie/foo/payment/%s/pdf/' % str(uuid.uuid4()),
params={'payer_external_id': 'payer:1'},
status=404,
@ -3123,7 +3167,7 @@ def test_pdf_payment_for_payer(app, user):
other_regie = Regie.objects.create(label='Other Foo')
payment.regie = other_regie
payment.save()
resp = app.get(
app.get(
'/api/regie/foo/payment/%s/pdf/' % str(payment.uuid),
params={'payer_external_id': 'payer:1'},
status=404,
@ -3159,6 +3203,7 @@ def test_list_credits(mock_payer, app, user):
payment.save()
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -3209,17 +3254,60 @@ def test_list_credits(mock_payer, app, user):
]
assert mock_payer.call_args_list == [mock.call(regie, mock.ANY, 'foobar')]
# publication date is in the future
credit.date_publication = datetime.date.today() + datetime.timedelta(days=1)
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# credit is cancelled
credit.date_publication = datetime.date(2022, 10, 1)
credit.cancelled_at = now()
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# other regie
other_regie = Regie.objects.create(label='Other Foo')
credit.cancelled_at = None
credit.regie = other_regie
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# no matching payer id
# campaign is not finalized
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit.regie = regie
credit.pool = pool
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# campaign is finalized
campaign.finalized = True
campaign.save()
resp = app.get('/api/regie/foo/credits/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
# no matching payer id
mock_payer.return_value = 'payer:unknown'
resp = app.get('/api/regie/foo/credits/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
@ -3275,6 +3363,7 @@ def test_list_credits_for_payer(app, user):
payment.save()
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -3323,17 +3412,60 @@ def test_list_credits_for_payer(app, user):
}
]
# publication date is in the future
credit.date_publication = datetime.date.today() + datetime.timedelta(days=1)
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# credit is cancelled
credit.date_publication = datetime.date(2022, 10, 1)
credit.cancelled_at = now()
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# other regie
other_regie = Regie.objects.create(label='Other Foo')
credit.cancelled_at = None
credit.regie = other_regie
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# credit fully assigned
# campaign is not finalized
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit.regie = regie
credit.pool = pool
credit.save()
resp = app.get('/api/regie/foo/credits/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# campaign is finalized
campaign.finalized = True
campaign.save()
resp = app.get('/api/regie/foo/credits/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
# credit fully assigned
CreditAssignment.objects.create(
invoice=invoice,
payment=payment,
@ -3374,6 +3506,7 @@ def test_list_history_credits(mock_payer, app, user):
payment.save()
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -3413,6 +3546,7 @@ def test_list_history_credits(mock_payer, app, user):
credit=credit,
amount=41,
)
credit.refresh_from_db()
resp = app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == [
@ -3436,17 +3570,60 @@ def test_list_history_credits(mock_payer, app, user):
]
assert mock_payer.call_args_list == [mock.call(regie, mock.ANY, 'foobar')]
# publication date is in the future
credit.date_publication = datetime.date.today() + datetime.timedelta(days=1)
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# credit is cancelled
credit.date_publication = datetime.date(2022, 10, 1)
credit.cancelled_at = now()
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# other regie
other_regie = Regie.objects.create(label='Other Foo')
credit.cancelled_at = None
credit.regie = other_regie
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# no matching payer id
# campaign is not finalized
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit.regie = regie
credit.pool = pool
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# campaign is finalized
campaign.finalized = True
campaign.save()
resp = app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
# no matching payer id
mock_payer.return_value = 'payer:unknown'
resp = app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'})
assert resp.json['err'] == 0
@ -3454,7 +3631,7 @@ def test_list_history_credits(mock_payer, app, user):
# payer error
mock_payer.side_effect = PayerError
app.get('/api/regie/foo/credits/', params={'NameID': 'foobar'}, status=404)
app.get('/api/regie/foo/credits/history/', params={'NameID': 'foobar'}, status=404)
def test_list_history_credits_for_payer(app, user):
@ -3489,6 +3666,7 @@ def test_list_history_credits_for_payer(app, user):
payment.save()
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -3526,6 +3704,7 @@ def test_list_history_credits_for_payer(app, user):
credit=credit,
amount=41,
)
credit.refresh_from_db()
resp = app.get('/api/regie/foo/credits/history/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == [
@ -3548,14 +3727,59 @@ def test_list_history_credits_for_payer(app, user):
}
]
# publication date is in the future
credit.date_publication = datetime.date.today() + datetime.timedelta(days=1)
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# credit is cancelled
credit.date_publication = datetime.date(2022, 10, 1)
credit.cancelled_at = now()
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# other regie
other_regie = Regie.objects.create(label='Other Foo')
credit.cancelled_at = None
credit.regie = other_regie
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# campaign is not finalized
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit.regie = regie
credit.pool = pool
credit.save()
resp = app.get('/api/regie/foo/credits/history/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert resp.json['data'] == []
# campaign is finalized
campaign.finalized = True
campaign.save()
resp = app.get('/api/regie/foo/credits/history/', params={'payer_external_id': 'payer:1'})
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
@mock.patch.object(Regie, 'get_payer_external_id_from_nameid', autospec=True)
def test_pdf_credit(mock_payer, app, user):
@ -3568,12 +3792,11 @@ def test_pdf_credit(mock_payer, app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/credit/%s/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
'/api/regie/foo/credit/%s/pdf/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404
)
app.get('/api/regie/foo/credit/%s/pdf/' % str(uuid.uuid4()), params={'NameID': 'foobar'}, status=404)
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -3585,17 +3808,55 @@ def test_pdf_credit(mock_payer, app, user):
resp = app.get('/api/regie/foo/credit/%s/pdf/' % str(credit.uuid), params={'NameID': 'foobar'})
assert resp.headers['Content-Disposition'] == 'attachment; filename="%s.pdf"' % credit.formatted_number
# publication date is in the future
credit.date_publication = datetime.date.today() + datetime.timedelta(days=1)
credit.save()
app.get('/api/regie/foo/credit/%s/pdf/' % str(credit.uuid), params={'NameID': 'foobar'}, status=404)
# credit is cancelled
credit.date_publication = datetime.date(2022, 10, 1)
credit.cancelled_at = now()
credit.save()
app.get('/api/regie/foo/credit/%s/pdf/' % str(credit.uuid), params={'NameID': 'foobar'}, status=404)
# other regie
other_regie = Regie.objects.create(label='Other Foo')
credit.cancelled_at = None
credit.regie = other_regie
credit.save()
resp = app.get(
'/api/regie/foo/credit/%s/pdf/' % str(credit.uuid), params={'NameID': 'foobar'}, status=404
app.get('/api/regie/foo/credit/%s/pdf/' % str(credit.uuid), params={'NameID': 'foobar'}, status=404)
# campaign is not finalized
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit.regie = regie
credit.pool = pool
credit.save()
app.get('/api/regie/foo/credit/%s/pdf/' % str(credit.uuid), params={'NameID': 'foobar'}, status=404)
# campaign is finalized
campaign.finalized = True
campaign.save()
app.get(
'/api/regie/foo/credit/%s/pdf/' % str(credit.uuid),
params={'NameID': 'foobar'},
)
# no matching payer id
mock_payer.return_value = 'payer:unknown'
resp = app.get('/api/regie/foo/credit/%s/' % str(credit.uuid), params={'NameID': 'foobar'}, status=404)
app.get('/api/regie/foo/credit/%s/' % str(credit.uuid), params={'NameID': 'foobar'}, status=404)
# payer error
mock_payer.side_effect = PayerError
@ -3612,7 +3873,7 @@ def test_pdf_credit_for_payer(app, user):
PaymentType.create_defaults(regie)
app.get('/api/regie/foo/credit/%s/pdf/' % str(uuid.uuid4()), status=404)
resp = app.get(
app.get(
'/api/regie/foo/credit/%s/pdf/' % str(uuid.uuid4()),
params={'payer_external_id': 'payer:1'},
status=404,
@ -3620,6 +3881,7 @@ def test_pdf_credit_for_payer(app, user):
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -3636,18 +3898,74 @@ def test_pdf_credit_for_payer(app, user):
other_regie = Regie.objects.create(label='Other Foo')
credit.regie = other_regie
credit.save()
resp = app.get(
# publication date is in the future
credit.date_publication = datetime.date.today() + datetime.timedelta(days=1)
credit.save()
app.get(
'/api/regie/foo/credit/%s/pdf/' % str(credit.uuid),
params={'payer_external_id': 'payer:1'},
status=404,
)
# credit is cancelled
credit.date_publication = datetime.date(2022, 10, 1)
credit.cancelled_at = now()
credit.save()
app.get(
'/api/regie/foo/credit/%s/pdf/' % str(credit.uuid),
params={'payer_external_id': 'payer:1'},
status=404,
)
# other regie
other_regie = Regie.objects.create(label='Other Foo')
credit.cancelled_at = None
credit.regie = other_regie
credit.save()
app.get(
'/api/regie/foo/credit/%s/pdf/' % str(credit.uuid),
params={'payer_external_id': 'payer:1'},
status=404,
)
# campaign is not finalized
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit.regie = regie
credit.pool = pool
credit.save()
app.get(
'/api/regie/foo/credit/%s/pdf/' % str(credit.uuid),
params={'payer_external_id': 'payer:1'},
status=404,
)
# campaign is finalized
campaign.finalized = True
campaign.save()
app.get(
'/api/regie/foo/credit/%s/pdf/' % str(credit.uuid),
params={'payer_external_id': 'payer:1'},
)
def test_add_refund(app, user):
app.post('/api/regie/foo/refunds/', status=403)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post('/api/regie/foo/refunds/', status=404)
app.post('/api/regie/foo/refunds/', status=404)
regie = Regie.objects.create(slug='foo')
other_regie = Regie.objects.create(slug='bar')
@ -3666,6 +3984,7 @@ def test_add_refund(app, user):
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -3684,17 +4003,92 @@ def test_add_refund(app, user):
user_first_name='User1',
user_last_name='Name1',
)
# not the same regie
other_credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=other_regie,
payer_external_id='payer:1',
payer_first_name='First1',
payer_last_name='Name1',
payer_address='41 rue des kangourous\n99999 Kangourou Ville',
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
params = {
'credit': str(other_credit.uuid), # not the same regie
'credit': str(other_credit.uuid),
}
resp = app.post('/api/regie/foo/refunds/', params=params, status=400)
assert resp.json['err']
assert resp.json['errors'] == {'credit': ['Unknown credit.']}
other_credit = Credit.objects.create(
date_publication=datetime.date.today() + datetime.timedelta(days=1), # not publicated
regie=regie,
payer_external_id='payer:1',
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
params = {
'credit': str(other_credit.uuid),
}
resp = app.post('/api/regie/foo/refunds/', params=params, status=400)
assert resp.json['err']
assert resp.json['errors'] == {'credit': ['Unknown credit.']}
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
cancelled_at=now(), # cancelled
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
params = {
'credit': str(other_credit.uuid),
}
resp = app.post('/api/regie/foo/refunds/', params=params, status=400)
assert resp.json['err']
assert resp.json['errors'] == {'credit': ['Unknown credit.']}
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
pool=pool, # not finalized pool
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
params = {
'credit': str(other_credit.uuid),
}
resp = app.post('/api/regie/foo/refunds/', params=params, status=400)
assert resp.json['err']
@ -3745,6 +4139,10 @@ def test_add_refund(app, user):
user_last_name='Name1',
)
credit.refresh_from_db()
campaign.finalized = True
campaign.save()
credit.pool = pool
credit.save()
assert credit.total_amount == 55
assert credit.assigned_amount == 42
assert credit.remaining_amount == 13

View File

@ -10,6 +10,7 @@ from pyquery import PyQuery
from lingo.basket.models import Basket, BasketLine, BasketLineItem
from lingo.invoicing.models import (
Campaign,
Credit,
CreditAssignment,
CreditLine,
@ -20,6 +21,7 @@ from lingo.invoicing.models import (
InvoiceLinePayment,
Payment,
PaymentType,
Pool,
Regie,
)
from tests.utils import login
@ -169,6 +171,7 @@ def test_basket_detail(app, simple_user):
# with available credit
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -317,6 +320,7 @@ def test_basket_invoice_pdf(app, simple_user):
basket.status = 'tobepaid'
basket.save()
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -489,6 +493,7 @@ def test_basket_validate_generate_invoice(app, simple_user):
# with credits, generated invoice is partially paid with credit
credit1 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -498,9 +503,25 @@ def test_basket_validate_generate_invoice(app, simple_user):
quantity=1,
unit_amount=1,
)
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=True,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit2 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
pool=pool,
)
CreditLine.objects.create(
credit=credit2,
@ -508,26 +529,77 @@ def test_basket_validate_generate_invoice(app, simple_user):
quantity=3,
unit_amount=1,
)
other_credit1 = Credit.objects.create(
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:2',
)
CreditLine.objects.create(
credit=other_credit1,
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=1,
unit_amount=1,
)
other_credit2 = Credit.objects.create(
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=other_regie,
payer_external_id='payer:1',
)
CreditLine.objects.create(
credit=other_credit2,
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=1,
unit_amount=1,
)
other_credit = Credit.objects.create(
date_publication=datetime.date.today() + datetime.timedelta(days=1), # not publicated
regie=regie,
payer_external_id='payer:1',
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
pool=pool, # not finalized pool
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
cancelled_at=now(), # cancelled
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
basket.status = 'open'
basket.save()
resp = app.get('/basket/validate/')
@ -631,6 +703,7 @@ def test_basket_validate_generate_invoice_nothing_to_pay(app, simple_user):
line.payment_callback_url = 'http://payment1.com'
line.save()
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -730,6 +803,7 @@ def test_basket_validate_generate_credit(app, simple_user):
# credit is not used if basket amount is negative
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -760,6 +834,8 @@ def test_basket_validate_generate_credit(app, simple_user):
assert credit.payer_last_name == 'Last'
assert credit.payer_address == '41 rue des kangourous\n99999 Kangourou Ville'
assert credit.lines.count() == 1
assert credit.pool is None
assert credit.date_publication == datetime.date(2023, 4, 21)
(line1,) = credit.lines.all().order_by('pk')
assert line1.event_date == datetime.date(2022, 9, 1)
assert line1.slug == 'event-a-foo-bar'
@ -848,6 +924,7 @@ def test_basket_cancel(app, simple_user):
app.get('/basket/cancel/', status=404)
# the invoice is partially paid with a credit
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)

View File

@ -6,6 +6,7 @@ from django.utils.timezone import now
from lingo.basket.models import Basket, BasketLine
from lingo.invoicing.models import (
Campaign,
Credit,
CreditAssignment,
CreditLine,
@ -13,6 +14,7 @@ from lingo.invoicing.models import (
DraftInvoiceLine,
Invoice,
InvoiceLine,
Pool,
Regie,
)
@ -55,6 +57,7 @@ def test_basket_expiration():
)
# the invoice is partially paid with a credit
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -98,6 +101,7 @@ def test_basket_expiration():
expiry_at=now() + datetime.timedelta(minutes=1),
)
credit2 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -233,6 +237,7 @@ def test_basket_amounts_with_draft_invoice():
# empty credit
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -255,6 +260,7 @@ def test_basket_amounts_with_draft_invoice():
# credit for other payer
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:2',
)
@ -272,6 +278,7 @@ def test_basket_amounts_with_draft_invoice():
# credit for other regie
other_regie = Regie.objects.create(label='Bar')
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=other_regie,
payer_external_id='payer:1',
)
@ -286,7 +293,94 @@ def test_basket_amounts_with_draft_invoice():
assert basket.credit_amount == -1
assert basket.remaining_amount == 9
# not publicated credit
other_credit = Credit.objects.create(
date_publication=datetime.date.today() + datetime.timedelta(days=1),
regie=regie,
payer_external_id='payer:1',
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
basket.draft_invoice.refresh_from_db()
assert basket.total_amount == 10
assert basket.credit_amount == -1
assert basket.remaining_amount == 9
# non finalized campaign
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
pool=pool,
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
basket.draft_invoice.refresh_from_db()
assert basket.total_amount == 10
assert basket.credit_amount == -1
assert basket.remaining_amount == 9
# cancelled credit
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
cancelled_at=now(),
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
basket.draft_invoice.refresh_from_db()
assert basket.total_amount == 10
assert basket.credit_amount == -1
assert basket.remaining_amount == 9
# credit == amount to pay
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=True,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
pool=pool,
)
CreditLine.objects.create(
credit=credit,
event_date=datetime.date(2022, 9, 1),
@ -392,6 +486,7 @@ def test_basket_amounts_with_invoice():
# empty credit
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)

View File

@ -9,6 +9,7 @@ from django.utils.timezone import now
from lingo.basket.models import Basket, BasketLine, BasketLineItem
from lingo.epayment.models import PaymentBackend, Transaction
from lingo.invoicing.models import (
Campaign,
Credit,
CreditAssignment,
CreditLine,
@ -16,6 +17,7 @@ from lingo.invoicing.models import (
DraftInvoiceLine,
Payment,
PaymentType,
Pool,
Regie,
)
from tests.utils import login
@ -288,6 +290,7 @@ def test_basket_payment_with_assigned_credits(app, simple_user):
line.save()
credit1 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
)
@ -297,9 +300,25 @@ def test_basket_payment_with_assigned_credits(app, simple_user):
quantity=1,
unit_amount=1,
)
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=True,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
credit2 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
pool=pool,
)
CreditLine.objects.create(
credit=credit2,
@ -309,6 +328,7 @@ def test_basket_payment_with_assigned_credits(app, simple_user):
)
other_regie = Regie.objects.create(label='Foo')
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=other_regie, # other regie
payer_external_id='payer:1',
)
@ -319,6 +339,7 @@ def test_basket_payment_with_assigned_credits(app, simple_user):
unit_amount=1,
)
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:2', # other payer
)
@ -328,6 +349,55 @@ def test_basket_payment_with_assigned_credits(app, simple_user):
quantity=3,
unit_amount=1,
)
other_credit = Credit.objects.create(
date_publication=datetime.date.today() + datetime.timedelta(days=1), # not publicated
regie=regie,
payer_external_id='payer:1',
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
campaign = Campaign.objects.create(
regie=regie,
date_start=datetime.date(2022, 9, 1),
date_end=datetime.date(2022, 10, 1),
date_publication=datetime.date(2022, 10, 1),
date_payment_deadline=datetime.date(2022, 10, 31),
date_due=datetime.date(2022, 10, 31),
date_debit=datetime.date(2022, 11, 15),
finalized=False,
)
pool = Pool.objects.create(
campaign=campaign,
draft=False,
)
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
pool=pool, # not finalized pool
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
other_credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
cancelled_at=now(), # cancelled
)
CreditLine.objects.create(
credit=other_credit,
event_date=datetime.date(2022, 9, 1),
quantity=3,
unit_amount=1,
)
resp = app.get('/basket/validate/')
resp = resp.form.submit()

View File

@ -3,6 +3,7 @@ import datetime
import pytest
from lingo.invoicing.models import (
Credit,
Invoice,
InvoiceCancellationReason,
Payment,
@ -82,6 +83,18 @@ def test_delete_invoice_reason(app, admin_user):
invoice.delete()
credit = Credit.objects.create(
regie=regie,
date_publication=datetime.date(2023, 4, 21),
cancellation_reason=invoice_reason,
)
resp = app.get('/manage/invoicing/cancellation-reasons/')
assert '/manage/invoicing/cancellation-reason/invoice/%s/delete/' % invoice_reason.pk not in resp
app.get('/manage/invoicing/cancellation-reason/invoice/%s/delete/' % invoice_reason.pk, status=404)
credit.delete()
resp = app.get('/manage/invoicing/cancellation-reasons/')
resp = resp.click(href='/manage/invoicing/cancellation-reason/invoice/%s/delete/' % invoice_reason.pk)
resp = resp.form.submit()

View File

@ -13,6 +13,7 @@ from lingo.invoicing.models import (
CreditAssignment,
CreditLine,
Invoice,
InvoiceCancellationReason,
Payment,
PaymentType,
Refund,
@ -35,6 +36,7 @@ def test_regie_credits(app, admin_user):
regie=regie,
)
credit1 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -44,6 +46,7 @@ def test_regie_credits(app, admin_user):
credit1.set_number()
credit1.save()
credit2 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:2',
payer_first_name='First2',
@ -53,6 +56,7 @@ def test_regie_credits(app, admin_user):
credit2.set_number()
credit2.save()
credit3 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:3',
payer_first_name='First3',
@ -61,6 +65,20 @@ def test_regie_credits(app, admin_user):
)
credit3.set_number()
credit3.save()
credit4 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:3',
payer_first_name='First3',
payer_last_name='Name3',
payer_address='43 rue des kangourous\n99999 Kangourou Ville',
cancelled_at=now(),
cancelled_by=admin_user,
cancellation_reason=InvoiceCancellationReason.objects.create(label='Final pool deletion'),
cancellation_description='foo bar\nblah',
)
credit4.set_number()
credit4.save()
CreditLine.objects.create(
slug='event-a-foo-bar',
@ -175,6 +193,21 @@ def test_regie_credits(app, admin_user):
assert credit3.remaining_amount == 1
assert credit3.assigned_amount == 0
CreditLine.objects.create(
slug='injected',
event_date=datetime.date(2022, 9, 1),
credit=credit4,
quantity=1,
unit_amount=1,
label='Event A',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
)
credit4.refresh_from_db()
assert credit4.remaining_amount == 1
assert credit4.assigned_amount == 0
app = login(app)
resp = app.get('/manage/invoicing/regie/%s/credits/' % regie.pk)
assert resp.pyquery(
@ -291,31 +324,58 @@ def test_regie_credits(app, admin_user):
'Remaining amount to assign: 1.00€',
]
assert resp.pyquery(
'tr[data-invoicing-element-id="%s"]' % credit4.pk
).text() == 'Cancelled Credit A%02d-%s-0000004 dated %s for First3 Name3, amount 1.00€ - download' % (
regie.pk,
credit4.created_at.strftime('%y-%m'),
credit4.created_at.strftime('%d/%m/%Y'),
)
assert len(resp.pyquery('tr[data-invoicing-element-id="%s"] a' % credit4.pk)) == 2
lines_url = resp.pyquery('tr[data-invoicing-element-id="%s"]' % credit4.pk).attr(
'data-invoicing-element-lines-url'
)
assert lines_url == '/manage/invoicing/ajax/regie/%s/credit/%s/lines/' % (
regie.pk,
credit4.pk,
)
lines_resp = app.get(lines_url)
assert len(lines_resp.pyquery('tr')) == 7
assert [PyQuery(tr).text() for tr in lines_resp.pyquery('tr')] == [
'User1 Name1',
'Description\nAccounting code\nAmount\nQuantity\nSubtotal',
'Event A\n1.00€\n1\n1.00€',
'Cancelled on: %s' % credit4.cancelled_at.strftime('%d/%m/%Y %H:%M'),
'Cancelled by: admin',
'Reason: Final pool deletion',
'Description: foo bar\nblah',
]
# test filters
today = now().date()
tomorrow = today + datetime.timedelta(days=1)
yesterday = today - datetime.timedelta(days=1)
params = [
({'number': credit1.formatted_number}, 1),
({'number': credit1.created_at.strftime('%y-%m')}, 3),
({'created_at_after': today.strftime('%Y-%m-%d')}, 3),
({'number': credit1.created_at.strftime('%y-%m')}, 4),
({'created_at_after': today.strftime('%Y-%m-%d')}, 4),
({'created_at_after': tomorrow.strftime('%Y-%m-%d')}, 0),
({'created_at_before': yesterday.strftime('%Y-%m-%d')}, 0),
({'created_at_before': today.strftime('%Y-%m-%d')}, 3),
({'created_at_before': today.strftime('%Y-%m-%d')}, 4),
({'payment_number': payment1.formatted_number}, 1),
({'payment_number': payment1.created_at.strftime('%y-%m')}, 1),
({'payer_external_id': 'payer:1'}, 1),
({'payer_external_id': 'payer:2'}, 1),
({'payer_first_name': 'first'}, 3),
({'payer_first_name': 'first'}, 4),
({'payer_first_name': 'first1'}, 1),
({'payer_last_name': 'name'}, 3),
({'payer_last_name': 'name'}, 4),
({'payer_last_name': 'name1'}, 1),
({'user_external_id': 'user:1'}, 3),
({'user_external_id': 'user:1'}, 4),
({'user_external_id': 'user:2'}, 1),
({'user_first_name': 'user'}, 3),
({'user_first_name': 'user'}, 4),
({'user_first_name': 'user2'}, 1),
({'user_last_name': 'name'}, 3),
({'user_last_name': 'name1'}, 3),
({'user_last_name': 'name'}, 4),
({'user_last_name': 'name1'}, 4),
(
{
'total_amount_min': '1',
@ -328,25 +388,25 @@ def test_regie_credits(app, admin_user):
'total_amount_min': '1',
'total_amount_min_lookup': 'gte',
},
3,
4,
),
(
{
'total_amount_max': '6.2',
'total_amount_max_lookup': 'lt',
},
2,
3,
),
(
{
'total_amount_max': '6.2',
'total_amount_max_lookup': 'lte',
},
3,
4,
),
({'assigned': 'yes'}, 1),
({'assigned': 'partially'}, 1),
({'assigned': 'no'}, 1),
({'assigned': 'no'}, 2),
({'agenda': 'agenda-a'}, 2),
({'agenda': 'agenda-b'}, 1),
({'event': 'agenda-a@event-a'}, 1),
@ -355,6 +415,8 @@ def test_regie_credits(app, admin_user):
({'accounting_code': '42'}, 0),
({'accounting_code': '424242'}, 2),
({'accounting_code': '424243'}, 1),
({'cancelled': 'yes'}, 1),
({'cancelled': 'no'}, 3),
]
for param, result in params:
resp = app.get(
@ -368,6 +430,7 @@ def test_regie_credit_pdf(app, admin_user):
regie = Regie.objects.create(label='Foo', invoice_main_colour='#9141ac')
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -466,6 +529,7 @@ def test_regie_refunds(app, admin_user):
regie=regie,
)
credit1 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',
@ -475,6 +539,7 @@ def test_regie_refunds(app, admin_user):
credit1.set_number()
credit1.save()
credit2 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:2',
payer_first_name='First2',
@ -484,6 +549,7 @@ def test_regie_refunds(app, admin_user):
credit2.set_number()
credit2.save()
credit3 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:3',
payer_first_name='First3',

View File

@ -650,6 +650,7 @@ def test_regie_payment_cancel(app, admin_user):
)
credit = Credit.objects.create(
label='Credit from 01/09/2022',
date_publication=datetime.date(2022, 10, 1),
regie=regie,
payer_external_id='payer:1',
payer_first_name='First1',

View File

@ -13,6 +13,8 @@ from lingo.invoicing import utils
from lingo.invoicing.models import (
Campaign,
Counter,
Credit,
CreditLine,
DraftInvoice,
DraftInvoiceLine,
DraftJournalLine,
@ -3906,6 +3908,57 @@ def test_promote_pool():
payer_direct_debit=False,
)
invoice4 = DraftInvoice.objects.create(
date_publication=campaign.date_publication,
date_payment_deadline=campaign.date_payment_deadline,
date_due=campaign.date_due,
regie=regie,
pool=pool,
payer_external_id='payer:1',
payer_first_name='First1',
payer_last_name='Last1',
payer_address='41 rue des kangourous\n99999 Kangourou Ville',
payer_demat=True,
payer_direct_debit=False,
)
iline41 = DraftInvoiceLine.objects.create(
event_date=datetime.date(2022, 9, 1),
slug='label-41',
label='Label 41',
quantity=1,
unit_amount=-1,
event_slug='label-41',
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
payer_external_id='payer:2',
payer_first_name='First2',
payer_last_name='Last2',
payer_address='42 rue des kangourous\n99999 Kangourou Ville',
payer_demat=False,
payer_direct_debit=True,
invoice=invoice4,
pool=pool,
)
line41 = DraftJournalLine.objects.create(
event_date=datetime.date(2022, 9, 1),
slug='label-41',
label='Label 41',
amount=-1,
user_external_id='user:1',
user_first_name='User1',
user_last_name='Name1',
payer_external_id='payer:2',
payer_first_name='First2',
payer_last_name='Last2',
payer_address='42 rue des kangourous\n99999 Kangourou Ville',
payer_demat=False,
payer_direct_debit=True,
status='success',
pool=pool,
invoice_line=iline41,
)
old_invoice = DraftInvoice.objects.create(
date_publication=campaign.date_publication,
date_payment_deadline=campaign.date_payment_deadline,
@ -4056,8 +4109,10 @@ def test_promote_pool():
iline12.refresh_from_db()
iline21.refresh_from_db()
iline22.refresh_from_db()
iline41.refresh_from_db()
invoice1.refresh_from_db()
invoice2.refresh_from_db()
invoice4.refresh_from_db()
# orphan invoice
orphan_invoice = DraftInvoice.objects.create(
@ -4111,11 +4166,13 @@ 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() == 6
assert DraftInvoiceLine.objects.count() == 9
assert DraftJournalLine.objects.count() == 11
assert DraftInvoice.objects.count() == 7
assert DraftInvoiceLine.objects.count() == 10
assert DraftJournalLine.objects.count() == 12
assert Invoice.objects.count() == 0
assert InvoiceLine.objects.count() == 0
assert Credit.objects.count() == 0
assert CreditLine.objects.count() == 0
assert JournalLine.objects.count() == 0
assert InjectedLine.objects.count() == 4
@ -4125,14 +4182,17 @@ 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() == 6
assert DraftInvoiceLine.objects.count() == 9
assert DraftJournalLine.objects.count() == 11
assert DraftInvoice.objects.count() == 7
assert DraftInvoiceLine.objects.count() == 10
assert DraftJournalLine.objects.count() == 12
assert Invoice.objects.count() == 3
assert InvoiceLine.objects.count() == 4
assert JournalLine.objects.count() == 6
assert Credit.objects.count() == 1
assert CreditLine.objects.count() == 1
assert JournalLine.objects.count() == 7
assert InjectedLine.objects.count() == 4
assert Counter.objects.get(regie=regie, name=today.strftime('%y')).value == 3
assert Counter.objects.get(regie=regie, kind='invoice', name=today.strftime('%y')).value == 3
assert Counter.objects.get(regie=regie, kind='credit', name=today.strftime('%y')).value == 1
test_counts()
@ -4353,7 +4413,54 @@ def test_promote_pool():
assert final_line22.from_injected_line is None
assert final_line22.invoice_line == final_iline22
final_orphan_line1 = JournalLine.objects.order_by('pk')[4]
credit = Credit.objects.order_by('pk')[0]
assert credit.date_publication == invoice4.date_publication
assert credit.regie == regie
assert credit.pool == final_pool
assert credit.payer_external_id == invoice4.payer_external_id
assert credit.payer_first_name == invoice4.payer_first_name
assert credit.payer_last_name == invoice4.payer_last_name
assert credit.total_amount == -invoice4.total_amount == 1
assert credit.number == 1
assert credit.formatted_number == 'A%02d-%s-0000001' % (regie.pk, today.strftime('%y-%m'))
credit_line41 = CreditLine.objects.order_by('pk')[0]
assert credit_line41.event_date == iline41.event_date
assert credit_line41.slug == iline41.slug
assert credit_line41.label == iline41.label
assert credit_line41.quantity == -iline41.quantity
assert credit_line41.unit_amount == iline41.unit_amount
assert credit_line41.total_amount == -iline41.total_amount
assert credit_line41.user_external_id == iline41.user_external_id
assert credit_line41.user_first_name == iline41.user_first_name
assert credit_line41.user_last_name == iline41.user_last_name
assert credit_line41.event_slug == iline41.event_slug
assert credit_line41.event_label == iline41.event_label
assert credit_line41.agenda_slug == iline41.agenda_slug
assert credit_line41.activity_label == iline41.activity_label
assert credit_line41.credit == credit
final_line41 = JournalLine.objects.order_by('pk')[4]
assert final_line41.event_date == line41.event_date
assert final_line41.slug == line41.slug
assert final_line41.label == line41.label
assert final_line41.amount == line41.amount
assert final_line41.user_external_id == line41.user_external_id
assert final_line41.user_first_name == line41.user_first_name
assert final_line41.user_last_name == line41.user_last_name
assert final_line41.payer_external_id == line41.payer_external_id
assert final_line41.payer_first_name == line41.payer_first_name
assert final_line41.payer_last_name == line41.payer_last_name
assert final_line41.payer_address == line41.payer_address
assert final_line41.payer_demat == line41.payer_demat
assert final_line41.payer_direct_debit == line41.payer_direct_debit
assert final_line41.event == line41.event
assert final_line41.pricing_data == line41.pricing_data
assert final_line41.status == line41.status
assert final_line41.pool == final_pool
assert final_line41.from_injected_line is None
assert final_line41.credit_line == credit_line41
final_orphan_line1 = JournalLine.objects.order_by('pk')[5]
assert final_orphan_line1.event_date == orphan_line1.event_date
assert final_orphan_line1.slug == orphan_line1.slug
assert final_orphan_line1.label == orphan_line1.label
@ -4374,7 +4481,7 @@ def test_promote_pool():
assert final_orphan_line1.from_injected_line is None
assert final_orphan_line1.invoice_line is None
final_orphan_line2 = JournalLine.objects.order_by('pk')[5]
final_orphan_line2 = JournalLine.objects.order_by('pk')[6]
assert final_orphan_line2.event_date == orphan_line2.event_date
assert final_orphan_line2.slug == orphan_line2.slug
assert final_orphan_line2.label == orphan_line2.label

View File

@ -528,7 +528,10 @@ def test_invoice_payments(orphan):
def test_creditline_total_amount():
regie = Regie.objects.create()
credit = Credit.objects.create(regie=regie)
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
)
# create line
line = CreditLine.objects.create(
@ -566,9 +569,15 @@ def test_creditline_total_amount():
def test_credit_total_amount():
regie = Regie.objects.create()
credit = Credit.objects.create(regie=regie)
credit = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
)
assert credit.total_amount == 0
credit2 = Credit.objects.create(regie=regie)
credit2 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
)
assert credit2.total_amount == 0
line = CreditLine.objects.create(
@ -664,14 +673,20 @@ def test_credit_assignments():
regie = Regie.objects.create()
PaymentType.create_defaults(regie)
credit1 = Credit.objects.create(regie=regie)
credit1 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
)
CreditLine.objects.create(
event_date=datetime.date.today(),
credit=credit1,
quantity=1,
unit_amount=42,
)
credit2 = Credit.objects.create(regie=regie)
credit2 = Credit.objects.create(
date_publication=datetime.date(2022, 10, 1),
regie=regie,
)
line2 = CreditLine.objects.create(
event_date=datetime.date.today(),
credit=credit2,