invoicing: FK between Regie and Payer (#78015)

This commit is contained in:
Lauréline Guérin 2023-06-02 14:35:14 +02:00
parent d194fd558d
commit 739aaf17cd
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
9 changed files with 103 additions and 5 deletions

View File

@ -43,7 +43,7 @@ class ImportForm(forms.Form):
class RegieForm(forms.ModelForm):
class Meta:
model = Regie
fields = ['label', 'slug', 'description', 'cashier_role', 'counter_name', 'number_format']
fields = ['label', 'slug', 'description', 'payer', 'cashier_role', 'counter_name', 'number_format']
def clean_slug(self):
slug = self.cleaned_data['slug']

View File

@ -0,0 +1,18 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('invoicing', '0035_payer'),
]
operations = [
migrations.AddField(
model_name='regie',
name='payer',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='invoicing.payer'
),
),
]

View File

@ -34,7 +34,7 @@ from django.utils.translation import gettext_lazy as _
from lingo.agendas.chrono import ChronoError, lock_events_check
from lingo.agendas.models import Agenda
from lingo.utils.misc import generate_slug
from lingo.utils.misc import LingoImportError, generate_slug
class RegieImportError(Exception):
@ -114,6 +114,7 @@ class Regie(models.Model):
verbose_name=_('Cashier Role'),
on_delete=models.SET_NULL,
)
payer = models.ForeignKey(Payer, on_delete=models.PROTECT, blank=True, null=True)
counter_name = models.CharField(
_('Counter name'),
@ -149,6 +150,7 @@ class Regie(models.Model):
'permissions': {
'cashier': self.cashier_role.name if self.cashier_role else None,
},
'payer': self.payer.slug if self.payer else None,
}
@classmethod
@ -163,6 +165,11 @@ class Regie(models.Model):
raise RegieImportError('Missing role: %s' % role_name)
except Group.MultipleObjectsReturned:
raise RegieImportError('Multiple role exist with the name: %s' % role_name)
if data['payer']:
try:
data['payer'] = Payer.objects.get(slug=data['payer'])
except Payer.DoesNotExist:
raise LingoImportError(_('Missing "%s" payer') % data['payer'])
regie, created = cls.objects.update_or_create(slug=data['slug'], defaults=data)
return created, regie

View File

@ -13,7 +13,9 @@
<ul class="extra-actions-menu">
<li><a href="{% url 'lingo-manager-invoicing-payer-edit' pk=payer.pk %}" rel="popup">{% trans "Edit" %}</a></li>
<li><a href="{% url 'lingo-manager-invoicing-payer-export' pk=payer.pk %}">{% trans 'Export' %}</a></li>
<li><a href="{% url 'lingo-manager-invoicing-payer-delete' pk=payer.pk %}" rel="popup">{% trans "Delete" %}</a></li>
{% if not regies %}
<li><a href="{% url 'lingo-manager-invoicing-payer-delete' pk=payer.pk %}" rel="popup">{% trans "Delete" %}</a></li>
{% endif %}
</ul>
</span>
{% endblock %}
@ -24,6 +26,7 @@
<div class="pk-tabs--tab-list" role="tablist">
<button aria-controls="panel-settings" aria-selected="true" id="tab-settings" role="tab" tabindex="0">{% trans "Settings" %}</button>
<button aria-controls="panel-mapping" aria-selected="false" id="tab-mapping" role="tab" tabindex="-1">{% trans "Mapping" %}</button>
<button aria-controls="panel-usage" aria-selected="false" id="tab-usage" role="tab" tabindex="-1">{% trans "Used in regies" %}</button>
</div>
<div class="pk-tabs--container">
@ -47,6 +50,26 @@
</div>
</div>
<div aria-labelledby="tab-usage" hidden="" id="panel-usage" role="tabpanel" tabindex="0">
{% if regies %}
<ul class="objects-list single-links">
{% for regie in regies %}
<li>
<a href="{% url 'lingo-manager-invoicing-regie-detail' pk=regie.pk %}">
{{ regie.label }}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<div class="big-msg-info">
{% blocktrans trimmed %}
This Payer is not used yet.
{% endblocktrans %}
</div>
{% endif %}
</div>
</div>
</div>
</div>

View File

@ -40,6 +40,7 @@
<h3>{% trans "Parameters" %}</h3>
<ul>
<li>{% trans "Identifier:" %} {{ regie.slug }}</li>
<li>{% trans "Payer:" %} {% if regie.payer %}<a href="{% url 'lingo-manager-invoicing-payer-detail' pk=regie.payer.pk %}">{{ regie.payer|default:'' }}</a>{% endif %}</li>
<li>{% trans "Cashier role:" %} {{ regie.cashier_role|default:'' }}</li>
<li>{% trans "Counter name:" %} <code>{{ regie.counter_name }}</code></li>
<li>{% trans "Number format:" %} <code>{{ regie.number_format }}</code></li>

View File

@ -57,6 +57,7 @@ class PayerDetailView(DetailView):
def get_context_data(self, **kwargs):
kwargs['payer'] = self.object
kwargs['regies'] = self.object.regie_set.all()
return super().get_context_data(**kwargs)
@ -91,6 +92,9 @@ class PayerDeleteView(DeleteView):
template_name = 'lingo/manager_confirm_delete.html'
model = Payer
def get_queryset(self):
return super().get_queryset().filter(regie__isnull=True)
def get_success_url(self):
return reverse('lingo-manager-invoicing-payer-list')

View File

@ -1,6 +1,6 @@
import pytest
from lingo.invoicing.models import Payer
from lingo.invoicing.models import Payer, Regie
from tests.utils import login
pytestmark = pytest.mark.django_db
@ -70,3 +70,10 @@ def test_delete_payer(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/invoicing/payers/')
assert Payer.objects.exists() is False
# can not delete payer used in regie
payer.save()
Regie.objects.create(label='foo', payer=payer)
resp = app.get('/manage/invoicing/payer/%s/' % payer.pk)
assert '/manage/invoicing/payer/%s/delete/' % payer.pk not in resp
resp = app.get('/manage/invoicing/payer/%s/delete/' % payer.pk, status=404)

View File

@ -16,6 +16,7 @@ from lingo.invoicing.models import (
Invoice,
InvoiceLine,
InvoicePayment,
Payer,
Payment,
Pool,
Regie,
@ -94,7 +95,9 @@ def test_manager_invoicing_regie_detail(app, admin_user):
assert descr.text == 'foo description'
slug = resp.pyquery('div#panel-settings ul li')[0]
assert slug.text == 'Identifier: foo'
cashier_role = resp.pyquery('div#panel-settings ul li')[1]
slug = resp.pyquery('div#panel-settings ul li')[1]
assert slug.text == 'Payer: '
cashier_role = resp.pyquery('div#panel-settings ul li')[2]
assert cashier_role.text == 'Cashier role: role-foo'
usage = resp.pyquery('div#panel-usage div')[0]
assert 'This Regie is not used yet.' in usage.text
@ -172,6 +175,15 @@ def test_manager_invoicing_regie_edit(app, admin_user):
response = form.submit()
assert response.context['form'].errors['slug'] == ['Another regie exists with the same identifier.']
payer = Payer.objects.create(label='Foo Bar')
resp = app.get(reverse('lingo-manager-invoicing-regie-edit', kwargs={'pk': regie.pk}))
form = resp.form
form.set('payer', payer.pk)
resp = resp.form.submit().follow()
regie.refresh_from_db()
assert regie.payer == payer
assert 'Payer: <a href="/manage/invoicing/payer/%s/">Foo Bar</a>' % payer.pk in resp
def test_manager_invoicing_regie_delete(app, admin_user):
app = login(app)

View File

@ -4,6 +4,7 @@ import pytest
from lingo.invoicing.models import Payer, Regie
from lingo.invoicing.utils import export_site, import_site
from lingo.utils.misc import LingoImportError
pytestmark = pytest.mark.django_db
@ -55,6 +56,31 @@ def test_import_export_regies(app):
assert regie.slug == 'foo-bar'
def test_import_export_regie_with_payer(app):
payer = Payer.objects.create(label='Foo')
regie = Regie.objects.create(label='Bar', payer=payer)
data = export_site()
regie.payer = None
regie.save()
payer.delete()
del data['payers']
with pytest.raises(LingoImportError) as excinfo:
import_site(data)
assert str(excinfo.value) == 'Missing "foo" payer'
Payer.objects.create(label='Foobar')
with pytest.raises(LingoImportError) as excinfo:
import_site(data)
assert str(excinfo.value) == 'Missing "foo" payer'
payer = Payer.objects.create(label='Foo')
import_site(data)
regie.refresh_from_db()
assert regie.payer == payer
def test_import_export_payers(app):
payload = export_site()
assert len(payload['payers']) == 0