saga: limite à 6 mois et récupération des factures du conjoint (fixes #17731)
Le noms des redevables est disponible dans le dictionnaire d'une facture dans ['extra']['redevable'].
This commit is contained in:
parent
8a93b28207
commit
0d51ab873a
1
setup.py
1
setup.py
|
@ -105,6 +105,7 @@ setup(
|
|||
'six',
|
||||
'djangorestframework<3.4',
|
||||
'pytz',
|
||||
'python-dateutil',
|
||||
],
|
||||
zip_safe=False,
|
||||
cmdclass={
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import httmock
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
@ -28,10 +30,15 @@ def test_tiers_saga(app, settings, nanterre_classic_family):
|
|||
f['jean'].content['cles_de_federation']['technocarte'] = '1234'
|
||||
f['jean'].save()
|
||||
|
||||
f['marie'].content['cles_de_federation']['technocarte'] = '4567'
|
||||
f['marie'].save()
|
||||
|
||||
assert 'saga_tiers' not in f['jean'].content['cles_de_federation']
|
||||
|
||||
# Définition du début de la séquence des codes tiers
|
||||
utils.set_saga_sequence(42)
|
||||
|
||||
# Calcul du code tiers pour Jean
|
||||
response = app.get(reverse('rsu-api-saga-tiers', kwargs={
|
||||
'application': 'technocarte',
|
||||
'identifier': '1234'
|
||||
|
@ -46,6 +53,16 @@ def test_tiers_saga(app, settings, nanterre_classic_family):
|
|||
}))
|
||||
assert response.json['code'] == 'RG0000000000042'
|
||||
|
||||
# Calcul du code tiers pour Marie
|
||||
response = app.get(reverse('rsu-api-saga-tiers', kwargs={
|
||||
'application': 'technocarte',
|
||||
'identifier': '4567'
|
||||
}))
|
||||
assert response.json['code'] == 'RG0000000000043'
|
||||
f['marie'].refresh_from_db()
|
||||
assert 'saga_tiers' in f['marie'].content['cles_de_federation']
|
||||
|
||||
# Mock du web-service SAGA, répond vite celui là
|
||||
@httmock.urlmatch()
|
||||
def saga_ok(url, request):
|
||||
assert url.netloc == 'saga.example.com'
|
||||
|
@ -91,7 +108,8 @@ def test_tiers_saga(app, settings, nanterre_classic_family):
|
|||
'identifier': f['jean'].id,
|
||||
}))
|
||||
assert response.json['err'] == 0
|
||||
assert len(response.json['data']) == 2
|
||||
assert len(response.json['data']) == 4
|
||||
assert response.json['data'][0]['extra']['redevable']
|
||||
num = response.json['data'][0]['num']
|
||||
num2 = response.json['data'][1]['num']
|
||||
|
||||
|
|
|
@ -16,10 +16,13 @@
|
|||
# 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 logging
|
||||
import isodate
|
||||
import copy
|
||||
import re
|
||||
import traceback
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
@ -46,19 +49,21 @@ def flatten_errors(serializer_errors):
|
|||
return errors
|
||||
|
||||
|
||||
def individu_to_text(individu):
|
||||
def individu_to_text(individu, short=False):
|
||||
d = individu.content
|
||||
text = d['nom_de_naissance'] + ' '
|
||||
if d.get('nom_d_usage'):
|
||||
text += '(' + d['nom_d_usage'] + ') '
|
||||
text += d['prenoms']
|
||||
date = isodate.parse_date(d['date_de_naissance'])
|
||||
text += ' - %02d/%02d/%04d - ' % (date.day, date.month, date.year)
|
||||
text += d['genre']
|
||||
if d.get('statut_legal'):
|
||||
text += '/' + d['statut_legal']
|
||||
if not short:
|
||||
date = isodate.parse_date(d['date_de_naissance'])
|
||||
text += ' - %02d/%02d/%04d - ' % (date.day, date.month, date.year)
|
||||
text += d['genre']
|
||||
if d.get('statut_legal'):
|
||||
text += '/' + d['statut_legal']
|
||||
return text
|
||||
|
||||
|
||||
def adresse_to_text(adresse):
|
||||
d = adresse.content
|
||||
text = '%(streetnumber)s%(streetnumberext)s %(streetname)s, ' % d
|
||||
|
@ -70,6 +75,7 @@ def adresse_to_text(adresse):
|
|||
text += ' (%(country)s)' % d
|
||||
return text
|
||||
|
||||
|
||||
def individu_to_response(individu, add_text=False, add_conjoint=True, add_enfant=True,
|
||||
add_parents=True):
|
||||
'''Serialize a person'''
|
||||
|
@ -1355,15 +1361,25 @@ saga_tiers = SagaTiers.as_view()
|
|||
|
||||
|
||||
class SagaMixin(object):
|
||||
individu = None
|
||||
|
||||
def error_response(self, error, status=200):
|
||||
logger = logging.getLogger('zoo_nanterre.saga')
|
||||
if self.individu:
|
||||
logger.warning(u'SAGA: %s pour %s', error, self.individu)
|
||||
else:
|
||||
logger.warning(u'SAGA: %s', error)
|
||||
return None, Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
error
|
||||
]
|
||||
}, status=status)
|
||||
|
||||
def get_ws(self):
|
||||
app_dfn = utils.get_application('saga')
|
||||
if not app_dfn or 'url' not in app_dfn:
|
||||
return None, Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
'URL de l\'application SAGA non configurée',
|
||||
]
|
||||
})
|
||||
return self.error_response('URL de l\'application SAGA non configurée')
|
||||
|
||||
ws = self.ws = saga.Saga(app_dfn['url'],
|
||||
timeout=app_dfn.get('timeout'),
|
||||
|
@ -1372,15 +1388,24 @@ class SagaMixin(object):
|
|||
return ws, None
|
||||
|
||||
def get_factures(self, identifier):
|
||||
self.individu = individu = self.get_individu(identifier)
|
||||
self.individu = self.get_individu(identifier)
|
||||
conjoint = utils.conjoint(self.individu)[0]
|
||||
debut = datetime.date.today() - relativedelta(months=6)
|
||||
|
||||
factures, error_response = self.get_facture_for_individu(self.individu, debut=debut)
|
||||
if error_response:
|
||||
return None, error_response
|
||||
if conjoint:
|
||||
factures_conjoint, error_response = self.get_facture_for_individu(conjoint, debut=debut)
|
||||
if not error_response:
|
||||
factures.extend(factures_conjoint)
|
||||
# retrier les facture par date d'emission
|
||||
factures.sort(key=lambda f: f.date_facture)
|
||||
return factures, None
|
||||
|
||||
def get_facture_for_individu(self, individu, debut=None):
|
||||
if 'saga_tiers' not in individu.content['cles_de_federation']:
|
||||
return None, Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
'l\'individu n\'a pas de code tiers SAGA',
|
||||
]
|
||||
})
|
||||
return self.error_response('l\'individu n\'a pas de code tiers SAGA')
|
||||
code_tiers = individu.content['cles_de_federation']['saga_tiers']
|
||||
|
||||
ws, error_response = self.get_ws()
|
||||
|
@ -1395,23 +1420,17 @@ class SagaMixin(object):
|
|||
if not federation:
|
||||
federation, error = ws.resolve_code_tiers(code_tiers)
|
||||
if error:
|
||||
return None, Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
error,
|
||||
]
|
||||
})
|
||||
return self.error_response(error)
|
||||
individu.content['cles_de_federation']['saga'] = federation
|
||||
individu.save()
|
||||
factures, error = ws.factures(federation)
|
||||
factures, error = ws.factures(federation, debut=debut)
|
||||
if error:
|
||||
return None, Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
error,
|
||||
]
|
||||
})
|
||||
return self.error_response(error)
|
||||
|
||||
# définir le redevable sur les factures
|
||||
redevable = individu_to_text(individu, short=True)
|
||||
for facture in factures:
|
||||
facture.extra['redevable'] = redevable
|
||||
return factures, None
|
||||
|
||||
|
||||
|
@ -1453,10 +1472,7 @@ class SagaTransaction(SagaFactures):
|
|||
def post(self, request, identifier, format=None):
|
||||
serializer = TransactionSagaSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': flatten_errors(serializer.errors),
|
||||
}, status=400)
|
||||
return self.error_response(flatten_errors(serializer.errors), status=400)
|
||||
|
||||
data = serializer.validated_data
|
||||
|
||||
|
@ -1471,12 +1487,7 @@ class SagaTransaction(SagaFactures):
|
|||
if facture.num in data['num_factures']:
|
||||
factures_a_payer.append(facture)
|
||||
if not factures_a_payer:
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
u'numéro(s) de facture inconnu',
|
||||
]
|
||||
})
|
||||
return self.error_response(u'numéro(s) de facture inconnu')
|
||||
|
||||
data = serializer.validated_data
|
||||
email = data.get('email') or self.individu.content['email'] or ''
|
||||
|
@ -1486,13 +1497,7 @@ class SagaTransaction(SagaFactures):
|
|||
urlretour_synchrone=data['urlretour_synchrone'],
|
||||
email=email)
|
||||
if error:
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
error,
|
||||
]
|
||||
})
|
||||
|
||||
return self.error_response(error)
|
||||
return Response({
|
||||
'err': 0,
|
||||
'data': {
|
||||
|
@ -1511,10 +1516,7 @@ class SagaRetourAsynchrone(SagaMixin, APIView):
|
|||
def post(self, request, format=None):
|
||||
serializer = RetourSagaSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': flatten_errors(serializer.errors),
|
||||
}, status=400)
|
||||
return self.error_response(flatten_errors(serializer.errors))
|
||||
|
||||
ws, error_response = self.get_ws()
|
||||
if error_response:
|
||||
|
@ -1522,12 +1524,7 @@ class SagaRetourAsynchrone(SagaMixin, APIView):
|
|||
|
||||
result, error = ws.page_retour_asynchrone(serializer.validated_data['idop'])
|
||||
if error:
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
error,
|
||||
]
|
||||
})
|
||||
return self.error_response(error)
|
||||
|
||||
return Response({
|
||||
'err': 0,
|
||||
|
@ -1541,10 +1538,7 @@ class SagaRetourSynchrone(SagaMixin, APIView):
|
|||
def post(self, request, format=None):
|
||||
serializer = RetourSagaSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': flatten_errors(serializer.errors),
|
||||
}, status=400)
|
||||
return self.error_response(flatten_errors(serializer.errors))
|
||||
|
||||
ws, error_response = self.get_ws()
|
||||
if error_response:
|
||||
|
@ -1552,12 +1546,7 @@ class SagaRetourSynchrone(SagaMixin, APIView):
|
|||
|
||||
result, error = ws.page_retour_synchrone(serializer.validated_data['idop'])
|
||||
if error:
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': [
|
||||
error,
|
||||
]
|
||||
})
|
||||
return self.error_response(error)
|
||||
|
||||
return Response({
|
||||
'err': 0,
|
||||
|
|
|
@ -13,7 +13,7 @@ from collections import namedtuple
|
|||
|
||||
Facture = namedtuple('Facture', ['date_facture', 'date_limite_recouvrement', 'etat',
|
||||
'incident_paiement', 'montant_initial', 'num', 'reste_a_payer',
|
||||
'creances', 'commentaire'])
|
||||
'creances', 'commentaire', 'extra'])
|
||||
|
||||
Creance = namedtuple('Creance', [
|
||||
'imputation',
|
||||
|
@ -97,20 +97,27 @@ class Saga(object):
|
|||
if t:
|
||||
return t.text
|
||||
|
||||
def factures(self, federation):
|
||||
def factures(self, federation, debut=None, fin=None):
|
||||
'''
|
||||
federation - string
|
||||
debut - datetime
|
||||
fin - datetime
|
||||
'''
|
||||
body = '''
|
||||
<etatFactureParTiersFedere>
|
||||
<num_tiers>{federation}</num_tiers>
|
||||
<num_service>{num_service}</num_service>
|
||||
<type_facture>toute</type_facture>
|
||||
<periode_debut></periode_debut>
|
||||
<periode_fin></periode_fin>
|
||||
<periode_debut>{periode_debut}</periode_debut>
|
||||
<periode_fin>{periode_fin}</periode_fin>
|
||||
<detail>oui</detail>
|
||||
</etatFactureParTiersFedere>'''
|
||||
|
||||
tree, error = self.soap_call(
|
||||
self.creance_url, body, 'etatFactureParTiersFedereReturn',
|
||||
num_service=self.num_service,
|
||||
periode_debut=debut.strftime('%d/%m/%Y') if debut else '',
|
||||
periode_fin=fin.strftime('%d/%m/%Y') if fin else '',
|
||||
federation=federation, ns=self.ns)
|
||||
if tree is None:
|
||||
return None, error
|
||||
|
@ -147,7 +154,8 @@ class Saga(object):
|
|||
num=a['num'],
|
||||
reste_a_payer=decimal.Decimal(a['reste_a_payer']),
|
||||
commentaire=self.get_child_content(t, 'commentaire'),
|
||||
creances=list(helper2()))
|
||||
creances=list(helper2()),
|
||||
extra={})
|
||||
|
||||
yield facture
|
||||
return list(helper()), None
|
||||
|
|
Loading…
Reference in New Issue