invoicing: views to manage Payers (#78015)

This commit is contained in:
Lauréline Guérin 2023-06-02 14:05:20 +02:00
parent 312d3d288b
commit d194fd558d
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
15 changed files with 650 additions and 6 deletions

View File

@ -20,11 +20,20 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from gadjo.forms.widgets import MultiSelectWidget
from lingo.invoicing.models import Campaign, DraftInvoice, DraftInvoiceLine, Invoice, InvoiceLine, Regie
from lingo.invoicing.models import (
Campaign,
DraftInvoice,
DraftInvoiceLine,
Invoice,
InvoiceLine,
Payer,
Regie,
)
class ExportForm(forms.Form):
regies = forms.BooleanField(label=_('Regies'), required=False, initial=True)
payers = forms.BooleanField(label=_('Payers'), required=False, initial=True)
class ImportForm(forms.Form):
@ -434,3 +443,30 @@ class RegieInvoiceFilterSet(django_filters.FilterSet):
if value == 'no':
return queryset.filter(paid_amount=0)
return queryset
class PayerForm(forms.ModelForm):
class Meta:
model = Payer
fields = [
'label',
'slug',
'description',
'carddef_reference',
'payer_external_id_prefix',
'payer_external_id_template',
]
def clean_slug(self):
slug = self.cleaned_data['slug']
if Payer.objects.filter(slug=slug).exclude(pk=self.instance.pk).exists():
raise forms.ValidationError(_('Another payer exists with the same identifier.'))
return slug
class PayerMappingForm(forms.ModelForm):
class Meta:
model = Payer
fields = ['user_fields_mapping']

View File

@ -68,6 +68,36 @@ class Payer(models.Model):
class Meta:
ordering = ['label']
def __str__(self):
return self.label
def save(self, *args, **kwargs):
if not self.slug:
self.slug = generate_slug(self)
super().save(*args, **kwargs)
@property
def base_slug(self):
return slugify(self.label)
def export_json(self):
return {
'label': self.label,
'slug': self.slug,
'description': self.description,
'carddef_reference': self.carddef_reference,
'payer_external_id_prefix': self.payer_external_id_prefix,
'payer_external_id_template': self.payer_external_id_template,
'user_fields_mapping': self.user_fields_mapping,
}
@classmethod
def import_json(cls, data):
data = copy.deepcopy(data)
payer, created = cls.objects.update_or_create(slug=data['slug'], defaults=data)
return created, payer
class Regie(models.Model):
label = models.CharField(_('Label'), max_length=150)

View File

@ -24,6 +24,10 @@
{% trans "Regies" %}
<p>{% trans "Invoicing regies." %}</p>
</a>
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-payer-list' %}">
{% trans "Payers" %}
<p>{% trans "Define here how payers are determined." %}</p>
</a>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,53 @@
{% extends "lingo/invoicing/manager_payer_list.html" %}
{% load i18n gadjo %}
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'lingo-manager-invoicing-payer-detail' payer.pk %}">{{ payer }}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans 'Payer' %} - {{ payer }}</h2>
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<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>
</ul>
</span>
{% endblock %}
{% block content %}
<div class="section">
<div class="pk-tabs">
<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>
</div>
<div class="pk-tabs--container">
<div aria-labelledby="tab-settings" id="panel-settings" role="tabpanel" tabindex="0">
{% if payer.description %}
<h3>{% trans "Description" %}</h3>
<p>{{ payer.description|linebreaksbr }}</p>
{% endif %}
<h3>{% trans "Parameters" %}</h3>
<ul>
<li>{% trans "Identifier:" %} {{ payer.slug }}</li>
<li>{% trans "Card Model:" %} <code>{{ payer.carddef_reference }}</code></li>
<li>{% trans "Prefix for payer external id:" %} <pre>{{ payer.payer_external_id_prefix }}</pre></li>
<li>{% trans "Template for payer external id:" %} <pre>{{ payer.payer_external_id_template }}</pre></li>
</ul>
</div>
<div aria-labelledby="tab-mapping" hidden="" id="panel-mapping" role="tabpanel" tabindex="0">
<div class="panel--buttons">
<a class="pk-button" href="{% url 'lingo-manager-invoicing-payer-edit-mapping' pk=payer.pk %}" rel="popup">{% trans "Edit mapping" %}</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,35 @@
{% extends "lingo/invoicing/manager_payer_list.html" %}
{% load i18n gadjo %}
{% block breadcrumb %}
{{ block.super }}
{% if object.pk %}
<a href="{% url 'lingo-manager-invoicing-payer-detail' payer.pk %}">{{ payer }}</a>
<a href="{% url 'lingo-manager-invoicing-payer-edit' payer.pk %}">{% trans "Edit" %}</a>
{% else %}
<a href="{% url 'lingo-manager-invoicing-payer-add' %}">{% trans "New payer" %}</a>
{% endif %}
{% endblock %}
{% block appbar %}
{% if payer.pk %}
<h2>{% trans "Edit payer" %} - {{ payer }}</h2>
{% else %}
<h2>{% trans "New payer" %}</h2>
{% endif %}
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|with_template }}
<div class="buttons">
<button>{% trans "Submit" %}</button>
{% if object.pk %}
<a class="cancel" href="{% url 'lingo-manager-invoicing-payer-detail' payer.pk %}">{% trans 'Cancel' %}</a>
{% else %}
<a class="cancel" href="{% url 'lingo-manager-invoicing-payer-list' %}">{% trans 'Cancel' %}</a>
{% endif %}
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,48 @@
{% extends "lingo/invoicing/manager_home.html" %}
{% load i18n %}
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'lingo-manager-invoicing-payer-list' %}">{% trans "Payers" %}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans 'Payers' %}</h2>
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<ul class="extra-actions-menu">
<li>
<a href="{# url 'lingo-manager-invoicing-payer-import' #}">{% trans 'Import' %}</a>
</li>
<li>
<a href="{# url 'lingo-manager-invoicing-payer-export' #}">{% trans 'Export' %}</a>
</li>
</ul>
<a rel="popup" href="{% url 'lingo-manager-invoicing-payer-add' %}">{% trans 'New payer' %}</a>
</span>
{% endblock %}
{% block content %}
{% if object_list %}
<div>
<ul class="objects-list single-links">
{% for payer in object_list %}
<li>
<a href="{% url 'lingo-manager-invoicing-payer-detail' pk=payer.pk %}">
{{ payer.label }}
<span class="extra-info"> [{% trans "identifier:" %} {{ payer.slug }}]</span>
</a>
</li>
{% endfor %}
</ul>
</div>
{% else %}
<div class="big-msg-info">
{% blocktrans %}
This site doesn't have any payer yet. Click on the "New" button in the top
right of the page to add a first one.
{% endblocktrans %}
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "lingo/invoicing/manager_payer_list.html" %}
{% load i18n gadjo %}
{% block breadcrumb %}
<a href="{% url 'lingo-manager-invoicing-payer-detail' payer.pk %}">{{ payer }}</a>
<a href="{% url 'lingo-manager-invoicing-payer-edit-mapping' payer.pk %}">{% trans "Edit mapping" %}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans "Edit mapping" %} - {{ payer }}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|with_template }}
<div class="buttons">
<button>{% trans "Submit" %}</button>
{% if object.pk %}
<a class="cancel" href="{% url 'lingo-manager-invoicing-payer-detail' payer.pk %}">{% trans 'Cancel' %}</a>
{% else %}
<a class="cancel" href="{% url 'lingo-manager-invoicing-payer-list' %}">{% trans 'Cancel' %}</a>
{% endif %}
</div>
</form>
{% endblock %}

View File

@ -18,6 +18,7 @@ from django.urls import path
from .views import campaign as campaign_views
from .views import home as home_views
from .views import payer as payer_views
from .views import pool as pool_views
from .views import regie as regie_views
@ -146,4 +147,35 @@ urlpatterns = [
regie_views.regie_invoice_line_list,
name='lingo-manager-invoicing-regie-invoice-line-list',
),
path('payers/', payer_views.payers_list, name='lingo-manager-invoicing-payer-list'),
path(
'payer/add/',
payer_views.payer_add,
name='lingo-manager-invoicing-payer-add',
),
path(
'payer/<int:pk>/',
payer_views.payer_detail,
name='lingo-manager-invoicing-payer-detail',
),
path(
'payer/<int:pk>/edit/',
payer_views.payer_edit,
name='lingo-manager-invoicing-payer-edit',
),
path(
'payer/<int:pk>/mapping/',
payer_views.payer_edit_mapping,
name='lingo-manager-invoicing-payer-edit-mapping',
),
path(
'payer/<int:pk>/delete/',
payer_views.payer_delete,
name='lingo-manager-invoicing-payer-delete',
),
path(
'payer/<int:pk>/export/',
payer_views.payer_export,
name='lingo-manager-invoicing-payer-export',
),
]

View File

@ -23,7 +23,7 @@ from django.utils.translation import gettext_lazy as _
from lingo.agendas.chrono import get_check_status, get_subscriptions
from lingo.agendas.models import Agenda
from lingo.invoicing.models import DraftInvoice, DraftInvoiceLine, InjectedLine, Regie
from lingo.invoicing.models import DraftInvoice, DraftInvoiceLine, InjectedLine, Payer, Regie
from lingo.pricing.models import AgendaPricing, AgendaPricingNotFound, PricingError
@ -315,11 +315,14 @@ def generate_invoices_from_lines(all_lines, pool):
def export_site(
regies=True,
payers=True,
):
'''Dump site objects to JSON-dumpable dictionnary'''
data = {}
if regies:
data['regies'] = [x.export_json() for x in Regie.objects.all()]
if payers:
data['payers'] = [x.export_json() for x in Payer.objects.all()]
return data
@ -328,11 +331,12 @@ def import_site(data):
key: collections.defaultdict(list)
for key in [
'regies',
'payers',
]
}
with transaction.atomic():
for cls, key in ((Regie, 'regies'),):
for cls, key in ((Payer, 'payers'), (Regie, 'regies')):
objs = data.get(key, [])
for obj in objs:
created, obj = cls.import_json(obj)

View File

@ -90,6 +90,20 @@ class ConfigImportView(FormView):
x,
),
},
'payers': {
'create_noop': _('No payer created.'),
'create': lambda x: ngettext(
'A payer has been created.',
'%(count)d payers have been created.',
x,
),
'update_noop': _('No payer updated.'),
'update': lambda x: ngettext(
'A payer has been updated.',
'%(count)d payers have been updated.',
x,
),
},
}
global_noop = True
@ -113,8 +127,8 @@ class ConfigImportView(FormView):
else:
obj_results['messages'] = message2
(r_count,) = (len(results['regies']['all']),)
if (r_count,) == (1,):
(r_count, p_count) = (len(results['regies']['all']), len(results['payers']['all']))
if (r_count, p_count) == (1, 0):
# only one regie imported, redirect to regie page
return HttpResponseRedirect(
reverse(
@ -122,11 +136,20 @@ class ConfigImportView(FormView):
kwargs={'pk': results['regies']['all'][0].pk},
)
)
if (r_count, p_count) == (0, 1):
# only one payer imported, redirect to payer page
return HttpResponseRedirect(
reverse(
'lingo-manager-invoicing-payer-detail',
kwargs={'pk': results['payers']['all'][0].pk},
)
)
if global_noop:
messages.info(self.request, _('No data found.'))
else:
messages.info(self.request, results['regies']['messages'])
messages.info(self.request, results['payers']['messages'])
return super().form_valid(form)

View File

@ -0,0 +1,115 @@
# lingo - payment and billing system
# Copyright (C) 2023 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import json
from django.http import HttpResponse
from django.urls import reverse
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
from lingo.invoicing.forms import PayerForm, PayerMappingForm
from lingo.invoicing.models import Payer
class PayersListView(ListView):
template_name = 'lingo/invoicing/manager_payer_list.html'
model = Payer
payers_list = PayersListView.as_view()
class PayerAddView(CreateView):
template_name = 'lingo/invoicing/manager_payer_form.html'
model = Payer
fields = [
'label',
'description',
'carddef_reference',
'payer_external_id_prefix',
'payer_external_id_template',
]
def get_success_url(self):
return reverse('lingo-manager-invoicing-payer-detail', args=[self.object.pk])
payer_add = PayerAddView.as_view()
class PayerDetailView(DetailView):
template_name = 'lingo/invoicing/manager_payer_detail.html'
model = Payer
def get_context_data(self, **kwargs):
kwargs['payer'] = self.object
return super().get_context_data(**kwargs)
payer_detail = PayerDetailView.as_view()
class PayerEditView(UpdateView):
template_name = 'lingo/invoicing/manager_payer_form.html'
model = Payer
form_class = PayerForm
def get_success_url(self):
return reverse('lingo-manager-invoicing-payer-detail', args=[self.object.pk])
payer_edit = PayerEditView.as_view()
class PayerEditMappingView(UpdateView):
template_name = 'lingo/invoicing/manager_payer_mapping_form.html'
model = Payer
form_class = PayerMappingForm
def get_success_url(self):
return '%s#open:mapping' % reverse('lingo-manager-invoicing-payer-detail', args=[self.object.pk])
payer_edit_mapping = PayerEditMappingView.as_view()
class PayerDeleteView(DeleteView):
template_name = 'lingo/manager_confirm_delete.html'
model = Payer
def get_success_url(self):
return reverse('lingo-manager-invoicing-payer-list')
payer_delete = PayerDeleteView.as_view()
class PayerExport(DetailView):
model = Payer
def get(self, request, *args, **kwargs):
response = HttpResponse(content_type='application/json')
today = datetime.date.today()
attachment = 'attachment; filename="export_payer_{}_{}.json"'.format(
self.get_object().slug, today.strftime('%Y%m%d')
)
response['Content-Disposition'] = attachment
json.dump({'payers': [self.get_object().export_json()]}, response, indent=2)
return response
payer_export = PayerExport.as_view()

View File

@ -22,3 +22,7 @@ def test_manager_invoicing_home(app, admin_user):
'div#lingo-manager-main div a[href="%s"]' % reverse('lingo-manager-invoicing-regie-list')
)
assert anchor.text().startswith('Regies')
anchor = resp.pyquery(
'div#lingo-manager-main div a[href="%s"]' % reverse('lingo-manager-invoicing-payer-list')
)
assert anchor.text().strip().startswith('Payers')

View File

@ -4,7 +4,7 @@ import json
import pytest
from webtest import Upload
from lingo.invoicing.models import Regie
from lingo.invoicing.models import Payer, Regie
from tests.utils import login
pytestmark = pytest.mark.django_db
@ -25,6 +25,7 @@ def test_export_site(freezer, app, admin_user):
site_json = json.loads(resp.text)
assert site_json == {
'regies': [],
'payers': [],
}
Regie.objects.create(label='Foo Bar')
@ -34,6 +35,15 @@ def test_export_site(freezer, app, admin_user):
site_json = json.loads(resp.text)
assert len(site_json['regies']) == 1
resp = app.get('/manage/invoicing/export/')
resp.form['regies'] = False
resp.form['payers'] = False
resp = resp.form.submit()
site_json = json.loads(resp.text)
assert 'regies' not in site_json
assert 'payers' not in site_json
@pytest.mark.freeze_time('2023-06-02')
def test_import_regie(app, admin_user):
@ -93,3 +103,63 @@ def test_import_regie(app, admin_user):
resp = resp.form.submit().follow()
assert '3 regies have been created. No regie updated.' in resp.text
assert Regie.objects.count() == 3
@pytest.mark.freeze_time('2023-06-02')
def test_import_payer(app, admin_user):
payer = Payer.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/invoicing/payer/%s/' % payer.pk)
resp = resp.click(href='/manage/invoicing/payer/%s/export/' % payer.pk)
assert resp.headers['content-type'] == 'application/json'
assert resp.headers['content-disposition'] == 'attachment; filename="export_payer_foo-bar_20230602.json"'
payer_export = resp.text
# existing payer
resp = app.get('/manage/invoicing/', status=200)
resp = resp.click('Import')
resp.form['config_json'] = Upload('export.json', payer_export.encode('utf-8'), 'application/json')
resp = resp.form.submit()
assert resp.location.endswith('/manage/invoicing/payer/%s/' % payer.pk)
resp = resp.follow()
assert 'No payer created. A payer has been updated.' not in resp.text
assert Payer.objects.count() == 1
# new payer
Payer.objects.all().delete()
resp = app.get('/manage/invoicing/', status=200)
resp = resp.click('Import')
resp.form['config_json'] = Upload('export.json', payer_export.encode('utf-8'), 'application/json')
resp = resp.form.submit()
payer = Payer.objects.latest('pk')
assert resp.location.endswith('/manage/invoicing/payer/%s/' % payer.pk)
resp = resp.follow()
assert 'A payer has been created. No payer updated.' not in resp.text
assert Payer.objects.count() == 1
# multiple payers
payers = json.loads(payer_export)
payers['payers'].append(copy.copy(payers['payers'][0]))
payers['payers'].append(copy.copy(payers['payers'][0]))
payers['payers'][1]['label'] = 'Foo bar 2'
payers['payers'][1]['slug'] = 'foo-bar-2'
payers['payers'][2]['label'] = 'Foo bar 3'
payers['payers'][2]['slug'] = 'foo-bar-3'
resp = app.get('/manage/invoicing/', status=200)
resp = resp.click('Import')
resp.form['config_json'] = Upload('export.json', json.dumps(payers).encode('utf-8'), 'application/json')
resp = resp.form.submit()
assert resp.location.endswith('/manage/invoicing/')
resp = resp.follow()
assert '2 payers have been created. A payer has been updated.' in resp.text
assert Payer.objects.count() == 3
Payer.objects.all().delete()
resp = app.get('/manage/invoicing/', status=200)
resp = resp.click('Import')
resp.form['config_json'] = Upload('export.json', json.dumps(payers).encode('utf-8'), 'application/json')
resp = resp.form.submit().follow()
assert '3 payers have been created. No payer updated.' in resp.text
assert Payer.objects.count() == 3

View File

@ -0,0 +1,72 @@
import pytest
from lingo.invoicing.models import Payer
from tests.utils import login
pytestmark = pytest.mark.django_db
def test_add_payer(app, admin_user):
app = login(app)
resp = app.get('/manage/invoicing/')
resp = resp.click('Payers')
resp = resp.click('New payer')
resp.form['label'] = 'Foo bar'
resp.form['description'] = 'a little description'
resp.form['carddef_reference'] = 'foo'
resp.form['payer_external_id_prefix'] = 'payer:'
resp.form['payer_external_id_template'] = 'bar'
resp = resp.form.submit()
payer = Payer.objects.latest('pk')
assert resp.location.endswith('/manage/invoicing/payer/%s/' % payer.pk)
assert payer.label == 'Foo bar'
assert payer.slug == 'foo-bar'
assert payer.description == 'a little description'
assert payer.carddef_reference == 'foo'
assert payer.payer_external_id_prefix == 'payer:'
assert payer.payer_external_id_template == 'bar'
def test_edit_payer(app, admin_user):
payer = Payer.objects.create(label='Foo bar', carddef_reference='foo')
payer2 = Payer.objects.create(label='baz')
app = login(app)
resp = app.get('/manage/invoicing/payers/')
resp = resp.click(href='/manage/invoicing/payer/%s/' % payer.pk)
resp = resp.click(href='/manage/invoicing/payer/%s/edit/' % payer.pk)
resp.form['label'] = 'Foo bar baz'
resp.form['slug'] = payer2.slug
resp = resp.form.submit()
assert resp.context['form'].errors['slug'] == ['Another payer exists with the same identifier.']
resp.form['slug'] = 'baz2'
resp = resp.form.submit()
assert resp.location.endswith('/manage/invoicing/payer/%s/' % payer.pk)
payer.refresh_from_db()
assert payer.label == 'Foo bar baz'
assert payer.slug == 'baz2'
def test_edit_payer_mapping(app, admin_user):
payer = Payer.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/invoicing/payer/%s/' % payer.pk)
resp = resp.click(href='/manage/invoicing/payer/%s/mapping/' % payer.pk)
resp.form['user_fields_mapping'] = '{"foo": "bar"}'
resp = resp.form.submit()
assert resp.location.endswith('/manage/invoicing/payer/%s/#open:mapping' % payer.pk)
payer.refresh_from_db()
assert payer.user_fields_mapping == {'foo': 'bar'}
def test_delete_payer(app, admin_user):
payer = Payer.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/invoicing/payer/%s/' % payer.pk)
resp = resp.click(href='/manage/invoicing/payer/%s/delete/' % payer.pk)
resp = resp.form.submit()
assert resp.location.endswith('/manage/invoicing/payers/')
assert Payer.objects.exists() is False

View File

@ -0,0 +1,92 @@
import copy
import pytest
from lingo.invoicing.models import Payer, Regie
from lingo.invoicing.utils import export_site, import_site
pytestmark = pytest.mark.django_db
def test_import_export(app):
Regie.objects.create(label='Foo Bar')
Payer.objects.create(label='Foo Bar')
data = export_site()
assert len(data['regies']) == 1
assert len(data['payers']) == 1
import_site(data={})
assert Regie.objects.count() == 1
assert Payer.objects.count() == 1
def test_import_export_regies(app):
payload = export_site()
assert len(payload['regies']) == 0
regie = Regie.objects.create(label='Foo bar')
payload = export_site()
assert len(payload['regies']) == 1
regie.delete()
assert not Regie.objects.exists()
import_site(copy.deepcopy(payload))
assert Regie.objects.count() == 1
regie = Regie.objects.first()
assert regie.label == 'Foo bar'
assert regie.slug == 'foo-bar'
# update
update_payload = copy.deepcopy(payload)
update_payload['regies'][0]['label'] = 'Foo bar Updated'
import_site(update_payload)
regie.refresh_from_db()
assert regie.label == 'Foo bar Updated'
# insert another regie
regie.slug = 'foo-bar-updated'
regie.save()
import_site(copy.deepcopy(payload))
assert Regie.objects.count() == 2
regie = Regie.objects.latest('pk')
assert regie.label == 'Foo bar'
assert regie.slug == 'foo-bar'
def test_import_export_payers(app):
payload = export_site()
assert len(payload['payers']) == 0
payer = Payer.objects.create(label='Foo bar', user_fields_mapping={'foo': 'bar'})
payload = export_site()
assert len(payload['payers']) == 1
payer.delete()
assert not Payer.objects.exists()
import_site(copy.deepcopy(payload))
assert Payer.objects.count() == 1
payer = Payer.objects.first()
assert payer.label == 'Foo bar'
assert payer.slug == 'foo-bar'
assert payer.user_fields_mapping == {'foo': 'bar'}
# update
update_payload = copy.deepcopy(payload)
update_payload['payers'][0]['label'] = 'Foo bar Updated'
import_site(update_payload)
payer.refresh_from_db()
assert payer.label == 'Foo bar Updated'
# insert another payer
payer.slug = 'foo-bar-updated'
payer.save()
import_site(copy.deepcopy(payload))
assert Payer.objects.count() == 2
payer = Payer.objects.latest('pk')
assert payer.label == 'Foo bar'
assert payer.slug == 'foo-bar'
assert payer.user_fields_mapping == {'foo': 'bar'}