summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Dauvergne <bdauvergne@entrouvert.com>2019-03-28 18:25:50 (GMT)
committerBenjamin Dauvergne <bdauvergne@entrouvert.com>2019-04-15 12:41:56 (GMT)
commitc05abeb6d363ed468b2407274862bd0154105481 (patch)
tree0026a76001ce3b72fed0cee3024b19337b96e326
parent59ad8d8fc202669b962e0c3a5404bd122935195f (diff)
downloadzoo-c05abeb6d363ed468b2407274862bd0154105481.zip
zoo-c05abeb6d363ed468b2407274862bd0154105481.tar.gz
zoo-c05abeb6d363ed468b2407274862bd0154105481.tar.bz2
nanterre: ajout fonction pour passage à la majorité (fixes #31830)
Pour l'instant sans l'envoi de trames aux autres logiciels.
-rw-r--r--tests/test_nanterre.py78
-rw-r--r--zoo/zoo_nanterre/fragments.py45
-rw-r--r--zoo/zoo_nanterre/management/commands/rsu-cron.py13
-rw-r--r--zoo/zoo_nanterre/utils.py56
4 files changed, 191 insertions, 1 deletions
diff --git a/tests/test_nanterre.py b/tests/test_nanterre.py
index 2235298..e85b99e 100644
--- a/tests/test_nanterre.py
+++ b/tests/test_nanterre.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
-import pytest
+import json
import datetime
import isodate
import requests
import threading
import urlparse
+import pytest
import httmock
from django.core.urlresolvers import reverse
@@ -1216,3 +1217,78 @@ def test_rsu_cron(db, settings, nanterre_classic_family):
assert Log.objects.count() == 20
call_command('rsu-cron', verbosity=0)
assert Log.objects.count() == 10
+
+
+def test_passage_a_la_majorite(db, settings, nanterre_classic_family, freezer):
+ from zoo.zoo_nanterre.utils import passage_a_la_majorite
+ from django.core.management import call_command
+
+ freezer.move_to('2019-01-01')
+
+ # set dummy cles de federation with technocarte
+ for individu in Entity.objects.filter(schema__slug='individu'):
+ individu.content['cles_de_federation'] = {'technocarte': str(individu.id)}
+ individu.save()
+ for key in nanterre_classic_family:
+ nanterre_classic_family[key].refresh_from_db()
+
+ assert Entity.objects.filter(schema__slug='individu').count() == 4
+ assert Entity.objects.filter(content__statut_legal='majeur').count() == 2
+ assert Entity.objects.filter(schema__slug='adresse').count() == 1
+ assert Relation.objects.filter(schema__slug='habite').count() == 4
+ assert Relation.objects.filter(schema__slug='responsabilite-legale').count() == 4
+
+ # passage à la majorité de lilou
+ requests = []
+
+ @httmock.urlmatch()
+ def technocarte_ok(url, request):
+ requests.append(request)
+ return httmock.response(
+ 200, 'null',
+ {
+ 'Content-Type': 'application/json',
+ })
+ with httmock.HTTMock(technocarte_ok):
+ result = passage_a_la_majorite()
+
+ assert result['updated_entities'] == 1
+ assert result['deleted_relations'] == 2
+ assert not result['errors']
+ assert Entity.objects.filter(schema__slug='individu').count() == 4
+ assert Entity.objects.filter(schema__slug='adresse').count() == 2
+ assert Relation.objects.filter(schema__slug='habite').count() == 4
+ assert Relation.objects.filter(schema__slug='responsabilite-legale').count() == 2
+ assert Entity.objects.filter(content__statut_legal='majeur').count() == 3
+ assert Entity.objects.filter(content__prenoms='LILOU', content__statut_legal='majeur').count() == 1
+
+ assert len(requests) == 1
+ req_content = json.loads(requests[0].body)
+ assert req_content['metadonnees']['service'] == 'passage-majorite'
+ assert len(req_content['fragments']) == 3
+ assert req_content['fragments'][0]['type'] == 'maj-adresse'
+ assert req_content['fragments'][1]['type'] == 'suppression-relation'
+ assert (req_content['fragments'][1]['fragment']['beneficiaire1']
+ == nanterre_classic_family['jean'].content['cles_de_federation']['technocarte'])
+ assert (req_content['fragments'][1]['fragment']['beneficiaire2']
+ == nanterre_classic_family['lilou'].content['cles_de_federation']['technocarte'])
+ assert req_content['fragments'][2]['type'] == 'suppression-relation'
+ assert (req_content['fragments'][2]['fragment']['beneficiaire1']
+ == nanterre_classic_family['marie'].content['cles_de_federation']['technocarte'])
+ assert (req_content['fragments'][2]['fragment']['beneficiaire2']
+ == nanterre_classic_family['lilou'].content['cles_de_federation']['technocarte'])
+
+ freezer.move_to('2025-12-31')
+ result = passage_a_la_majorite()
+ assert result['updated_entities'] == 0
+ assert result['deleted_relations'] == 0
+ assert not result['errors']
+ assert Entity.objects.filter(content__statut_legal='majeur').count() == 3
+ assert Entity.objects.filter(content__prenoms='LILOU', content__statut_legal='majeur').count() == 1
+
+ # passage à la majorité de Kévin le jour de son anniversaire
+ freezer.move_to('2026-01-01')
+ call_command('rsu-cron')
+ assert Entity.objects.filter(content__statut_legal='majeur').count() == 4
+ assert Entity.objects.filter(content__statut_legal='mineur').count() == 0
+ assert Entity.objects.filter(content__prenoms=u'KÉVIN', content__statut_legal='majeur').count() == 1
diff --git a/zoo/zoo_nanterre/fragments.py b/zoo/zoo_nanterre/fragments.py
index d6c196f..28b9a89 100644
--- a/zoo/zoo_nanterre/fragments.py
+++ b/zoo/zoo_nanterre/fragments.py
@@ -814,3 +814,48 @@ class SuppressionResponsabiliteEnfant(RelationSynchro):
assert relation.schema.slug == utils.RESPONSABILITE_LEGALE_REL
self.suppression_relation(relation)
self.mise_a_jour_adresse(relation.right)
+
+
+class PassageALaMajorite(RelationSynchro):
+ service = 'passage-majorite'
+
+ @classmethod
+ def create(cls, application, individu, relations, meta=None):
+ individus = set([individu])
+ for relation in relations:
+ individus.add(relation.left)
+ individus.add(relation.right)
+ self = super(RelationSynchro, cls).create(application, individus, meta=meta)
+ self.mise_a_jour_adresse(individu)
+ for relation in relations:
+ assert cls.condition(application, relation)
+ self.suppression_relation(relation)
+ return self
+
+ @classmethod
+ def condition(self, application, relation):
+ left_cond = application in relation.left.content['cles_de_federation']
+ right_cond = application in relation.right.content['cles_de_federation']
+ return left_cond and right_cond
+
+ @classmethod
+ def par_application(cls, application, individu, relations, meta=None, transaction=None):
+ relations = [relation for relation in relations if cls.condition(application, relation)]
+ action = cls.create(application, individu, relations, meta=meta)
+ job = Job.create(action, transaction=transaction)
+ if job.state != Job.STATE_SUCCESS:
+ if action.human_result:
+ yield action.human_result
+ else:
+ yield 'Erreur interne'
+
+ @classmethod
+ def pour_chaque_application(cls, individu, relations, meta=None, transaction=None):
+ def helper():
+ for application in utils.get_applications(rsu_ws_url=True):
+ if application not in individu.content['cles_de_federation']:
+ continue
+ for message in cls.par_application(application, individu, relations, meta=meta,
+ transaction=transaction):
+ yield message
+ return list(helper())
diff --git a/zoo/zoo_nanterre/management/commands/rsu-cron.py b/zoo/zoo_nanterre/management/commands/rsu-cron.py
index 4d44c88..34d5ee7 100644
--- a/zoo/zoo_nanterre/management/commands/rsu-cron.py
+++ b/zoo/zoo_nanterre/management/commands/rsu-cron.py
@@ -21,15 +21,18 @@ import datetime
from django.core.management.base import BaseCommand
from django.conf import settings
+from django.db import transaction
from django.utils.timezone import now
from zoo.zoo_data.models import Log
+from zoo.zoo_nanterre.utils import passage_a_la_majorite
class Command(BaseCommand):
def handle(self, *args, **options):
self.options = options
self.clean_journal()
+ self.passage_a_la_majorite()
def clean_journal(self):
'''Supprime les entrées du journal plus vieilles que ZOO_NANTERRE_LOG_EXPIRATION_DAYS'''
@@ -40,3 +43,13 @@ class Command(BaseCommand):
count, count_by_class = Log.objects.filter(timestamp__lt=threshold).delete()
if count and self.options['verbosity'] > 0:
print('Suppression de %d entrées du journal.' % count)
+
+ def passage_a_la_majorite(self):
+ with transaction.atomic():
+ result = passage_a_la_majorite()
+ if result['errors'] and self.options['verbosity'] > 0:
+ print('Mise à jour de %d enfants.' % result['updated_entities'])
+ for individu in result['errors']:
+ print('Erreur:', individu, result['errors'][individu])
+ elif result['updated_entities'] and self.options['verbosity'] > 1:
+ print('Mise à jour de %d enfants.' % result['updated_entities'])
diff --git a/zoo/zoo_nanterre/utils.py b/zoo/zoo_nanterre/utils.py
index c0cd4c9..f88ca3c 100644
--- a/zoo/zoo_nanterre/utils.py
+++ b/zoo/zoo_nanterre/utils.py
@@ -33,6 +33,7 @@ import collections
import json
import time
+from dateutil.relativedelta import relativedelta
import psycopg2
from django.conf import settings
@@ -1282,3 +1283,58 @@ def csv_export_response(rows, filename):
r['Content-Disposition'] = 'attachment; filename="%s"' % filename
return r
+
+def passage_a_la_majorite():
+ from . import fragments
+
+ entity_schema = EntitySchema.objects.get(slug=INDIVIDU_ENT)
+ responsabilite_legale_schema = RelationSchema.objects.get(slug=RESPONSABILITE_LEGALE_REL)
+
+ non_majeurs = Entity.objects.filter(schema=entity_schema).exclude(content__statut_legal='majeur')
+ birthdate_threshold = now().date() - relativedelta(years=18)
+ non_majeurs_to_update = non_majeurs.filter(content__date_de_naissance__timestamp__lte=birthdate_threshold)
+ updated_entities = non_majeurs_to_update.count()
+
+ # delete responsabilite-legale relations with parents
+ relations_to_delete = list(Relation.objects.filter(
+ schema=responsabilite_legale_schema,
+ right__in=non_majeurs_to_update))
+ deleted_relations = len(relations_to_delete)
+
+ errors = {}
+ # updating
+ for individu in non_majeurs_to_update:
+ individu.content['statut_legal'] = 'majeur'
+ individu.save()
+ # computing new adress, reusing first adresse
+ new_adresse, new_adresse_rel = adresses(individu)[0]
+ # delete link to old address
+ new_adresse_rel.delete()
+ # create new address
+ new_adresse.pk = None
+ new_adresse.save()
+ assert new_adresse.pk
+ # link new address to old mineur
+ new_adresse_rel.pk = None
+ new_adresse_rel.content['principale'] = False
+ new_adresse_rel.right = new_adresse
+ new_adresse_rel.save()
+ assert len(adresses(individu)) == 1
+ relations_parentales = [rel for parent, rel in parents(individu)]
+ # launch messages to applications
+ messages = fragments.PassageALaMajorite.pour_chaque_application(individu, relations_parentales)
+ if messages:
+ errors[individu] = messages
+
+ # delete relations
+ for relation in relations_to_delete:
+ relation.delete()
+
+ # we cannot delete relation if no entity is updated
+ assert updated_entities or (not deleted_relations)
+
+ return {
+ 'updated_entities': updated_entities,
+ 'deleted_relations': deleted_relations,
+ 'errors': errors,
+ }