nanterre: ajout fonction pour passage à la majorité (fixes #31830)
Pour l'instant sans l'envoi de trames aux autres logiciels.
This commit is contained in:
parent
59ad8d8fc2
commit
c05abeb6d3
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue