"""
Ce fichier gere les sessions de formation
"""
import ast
import smtplib
from email import encoders
from email.mime.base import MIMEBase
from calendar import monthrange
import jinja2
import pymongo
from flask import send_file
from pymongo import MongoClient
import json
from bson import ObjectId
import re
from datetime import datetime, date

from xhtml2pdf import pisa

import Contact
import E_Sign_Document
import Session_Formation_Sequence
import attached_file_mgt
import module_editique
import partner_client
import prj_common as mycommon
import secrets
import inspect
import sys, os
import csv
import pandas as pd
from pymongo import ReturnDocument
import GlobalVariable as MYSY_GV
from math import isnan
import GlobalVariable as MYSY_GV
from datetime import timedelta
from datetime import timedelta
import Inscription_mgt as Inscription_mgt
from zipfile import ZipFile
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import partner_base_setup as partner_base_setup
"""
Fonction de creation et mise à jour  d'une session de formation.


/!\ : le champ 'source' definit la source de la creation de la session

si le champ 'session_id' est renseigné alors c'est une mise à jour.
"""
def Add_Update_SessionFormation(diction):
    try:

        '''
               # Verification que les champs reçus dans l'API sont bien dans la liste des champs autorisés
               # Cela evite le cas ou une entité tierce ajouter les valeurs inconnu dans l'API
               # Ici on doit mettre tous les champs possible (obligatoire ou non) de la BDD dans la liste
               # field_list.
               '''
        field_list = ['token', 'date_debut', 'date_fin', 'nb_participant',  'adresse',
                      'code_postal', 'ville',  'code_session',
                      'class_internal_url', 'session_status', 'date_debut_inscription', 'date_fin_inscription',
                      'attestation_certif',  "distantiel", "presentiel", "prix_session", 'contenu_ftion', 'lms_class_code',
                      'session_ondemande', 'source', 'session_id', 'session_etape', 'pays', 'formateur_id',
                      'titre', 'location_type', 'is_bpf', 'site_formation_id', 'price_by']

        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé.")
                return False, " Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token',  'class_internal_url','code_session']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Toutes les informations necessaires n'ont pas été fournies"

        mydata = {}
        query_key = {}

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        # Verifier la validité du token
        retval = mycommon.check_partner_token_validity("", my_token)
        if retval is False:
            return "Err_Connexion", " La session de connexion n'est pas valide"

        data_for_check_connexion = {'token':my_token}
        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(data_for_check_connexion)
        if (local_status is not True):
            return local_status, my_partner


        session_id = ""
        if ("session_id" in diction.keys()):
            session_id = diction['session_id']



        code_session = ""
        if ("code_session" in diction.keys()):
            mydata['code_session'] = diction['code_session']
            query_key['code_session'] = diction['code_session']


        class_internal_url = ""
        if ("class_internal_url" in diction.keys()):
            mydata['class_internal_url'] = diction['class_internal_url']
            class_internal_url = diction['class_internal_url']
            query_key['class_internal_url'] = diction['class_internal_url']

        class_source = ""
        if ("source" in diction.keys()):
            if diction['source']:
                class_source = diction['source']


        partner_recid = mycommon.get_parnter_recid_from_token(my_token)
        if (partner_recid is False):
            mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible  de récupérer les données du partenaire ")
            return False, "Impossible de récupérer les données du partenaire "


        """
        update du 20/08/23 : 
        on ajouter un champs 'owner_recid' a la session.
        en effet 2 partenaire peuvent avoir exactement la meme formation et les memes code session. 
        il faut arriver à les distinguer tout de meme
        """
        mydata['partner_owner_recid'] = str(partner_recid)


        if ("date_debut" in diction.keys()):
            mydata['date_debut'] = str(diction['date_debut'])[0:10]

        etape = ""
        if ("session_etape" in diction.keys()):
            etape = str(diction['session_etape'])


        mydata['session_etape'] = etape


        if ("date_fin" in diction.keys()):
            mydata['date_fin'] = str(diction['date_fin'])[0:10]


        if ("distantiel" in diction.keys()):
            mydata['distantiel'] = str(mycommon.tryInt(str(diction['distantiel'])))
        else:
            mydata['distantiel'] = "0"

        if ("presentiel" in diction.keys()):
            mydata['presentiel'] = str(mycommon.tryInt(str(diction['presentiel'])))
        else:
            mydata['presentiel'] = "0"


        if ("session_ondemande" in diction.keys()):
            mydata['session_ondemande'] = str(mycommon.tryInt(str(diction['session_ondemande'])))
        else:
            mydata['session_ondemande'] = "0"



        if ("nb_participant" in diction.keys()):
            nb_particants = str(mycommon.tryInt(str(diction['nb_participant'])))
            if( nb_particants == "0" ):
                nb_particants = "1"
            mydata['nb_participant'] = str(mycommon.tryInt(str(diction['nb_participant'])))
        else:
            mydata['nb_participant'] = "1"

        if ("prix_session" in diction.keys()):
            mydata['prix_session'] = diction['prix_session']

        if ("price_by" in diction.keys()):
            if( str(diction['price_by']) not in MYSY_GV.TRAINING_PRICE):
                mycommon.myprint(str(inspect.stack()[0][3]) + " Le prix par "+str(diction['price_by'])+" n'est valide. Les valeurs autorisées sont "+str(MYSY_GV.TRAINING_PRICE) )
                return False, " Le prix par "+str(diction['price_by'])+" n'est valide. Les valeurs autorisées sont "+str(MYSY_GV.TRAINING_PRICE)+" "

            mydata['price_by'] = diction['price_by']
        else:
            mydata['price_by'] = "perstagiaire"


        if ("titre" in diction.keys()):
            mydata['titre'] = diction['titre']
        else:
            mydata['titre'] = ""


        if ("site_formation_id" in diction.keys()):
            mydata['site_formation_id'] = diction['site_formation_id']
        else:
            mydata['site_formation_id'] = ""


        if ("location_type" in diction.keys()):
            mydata['location_type'] = str(diction['location_type']).lower()
            if (str(diction['location_type']).lower() not in MYSY_GV.TRAINING_LOCATION_TYPE):
                mycommon.myprint(str(inspect.stack()[0][3]) + " - Le champ 'location_type' est incorrecte.")
                return False, "Le champ 'location_type' est incorrect."


        if ("is_bpf" in diction.keys()):
            if( str(diction['is_bpf']) not in ['0', '1']):
                mycommon.myprint(str(inspect.stack()[0][3]) + " - Le champ 'bpf' est incorrect. Les valeurs acceptées sont : '1' ou '0' ")
                return False, "Le champ 'bpf' est incorrect.  Les valeurs acceptées sont : '1' ou '0' "

            mydata['is_bpf'] = diction['is_bpf']


        formateur_id = ""
        if ("formateur_id" in diction.keys() and diction['formateur_id']):
            formateur_id = diction['formateur_id']
            # Verification de la validité du formateur (collection employé)
            is_formateur_id_ok = MYSY_GV.dbname['ressource_humaine'].count_documents({'_id':ObjectId(str(formateur_id)),
                                                                                      'partner_recid':str(partner_recid),
                                                                                      'valide':'1',
                                                                                      'locked':'0'
                                                                                      })

            if(is_formateur_id_ok <= 0  ):
                mycommon.myprint(str(inspect.stack()[0][3]) + " - L'identifiant du formateur est invalide")
                return False, " L'identifiant du formateur est invalide "

        mydata['formateur_id'] = formateur_id




        if ("adresse" in diction.keys()):
            if diction['adresse']:
                mydata['adresse'] = diction['adresse']
            else:
                mydata['adresse'] = " "


        if ("code_postal" in diction.keys()):
            if diction['code_postal']:
                if( "." in str(diction['code_postal']) ):
                    #/!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                    mydata['code_postal'] = str(diction['code_postal']).split(".")[0]
                elif( "." in str(diction['code_postal']) ):
                    #/!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                    mydata['code_postal'] = str(diction['code_postal']).split(",")[0]
                else:
                    mydata['code_postal'] = str(diction['code_postal'])

            else:
                mydata['code_postal'] = ""

        if ("ville" in diction.keys()):
            if diction['ville']:
                mydata['ville'] = diction['ville']
            else:
                mydata['ville'] = ""

        if ("pays" in diction.keys()):
            if diction['pays']:
                mydata['pays'] = diction['pays']
            else:
                mydata['pays'] = ""

        """ 
        if ("attestation_certif" in diction.keys()):
            mydata['attestation_certif'] = diction['attestation_certif']
        """

        if ("session_status" in diction.keys()):
            mydata['session_status'] = str(mycommon.tryInt(str(diction['session_status'])))
        else:
            mydata['session_status'] = ""

        if ("date_debut_inscription" in diction.keys()):
            mydata['date_debut_inscription'] = str(diction['date_debut_inscription'])[0:10]
        else:
            mydata['date_debut_inscription'] = ""

        if ("date_fin_inscription" in diction.keys()):
            mydata['date_fin_inscription'] = str(diction['date_fin_inscription'])[0:10]
        else:
            mydata['date_fin_inscription'] = ""

        """"
        if ("formateur" in diction.keys()):
            mydata['formateur'] = diction['formateur']
        """

        if ("contenu_ftion" in diction.keys()):
            mydata['contenu_ftion'] = diction['contenu_ftion']
        else:
            mydata['contenu_ftion'] = ""

        """
                        Update du 22/10/2023 - Gestion des champs spécifiques ajoutés par le partenaire
                        """

        # Recuperation des champs spécifiques se trouvant dans le dictionnaire. ils commencent tous par 'my_'
        for val in diction.keys():
            if (val.startswith('my_')):
                if (MYSY_GV.dbname['base_specific_fields'].count_documents(
                        {'partner_owner_recid': str(partner_recid),
                         'related_collection': 'session_formation',
                         'field_name': str(val),
                         'valide': '1',
                         'locked': '0'}) != 1):
                    mycommon.myprint(str(
                        inspect.stack()[0][3]) + "  Le champ  '" + val + "' n'est pas autorisé ")
                    return False, " Les informations fournies sont incorrectes"

                mydata[str(val)] = diction[str(val)]


        mydata['date_update'] = str(datetime.now())
        mydata['update_by'] = str(my_partner['_id'])
        mydata['valide'] = "1"

        # Controle de cohérence sur les dates
        local_status = mycommon.CheckisDate(str(diction['date_debut'])[0:10])
        if (local_status is False):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " - Formation : "+str(code_session)+" : Impossible de créer/mettre à jour la session de formation. La date de debut n'est pas au format jj/mm/aaaa ")

            return False, " - Formation : "+str(code_session)+" : Impossible de créer/mettre à jour la session de formation. La date de debut n'est pas au format jj/mm/aaaa "

        local_status = mycommon.CheckisDate(str(diction['date_fin'])[0:10])
        if (local_status is False):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " -Formation : "+str(code_session)+" : Impossible de créer/mettre à jour la session de formation. La date de fin n'est pas au format jj/mm/aaaa ")

            return False, " -Formation : "+str(code_session)+" :  La date de fin n'est pas au format jj/mm/aaaa "


        if (datetime.strptime(str(diction['date_debut'])[0:10], '%d/%m/%Y') > datetime.strptime(str(diction['date_fin'])[0:10], '%d/%m/%Y')):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " Session de  Formation : La date debut " + str(
                    diction['date_debut'])[0:10] +
                " est postérieure à la date de fin " + str(diction['date_fin'])[0:10] )

            return False, " Session de Formation : La date debut de formation " + str(diction['date_debut'])[0:10] + \
                   " est postérieure à la date de fin de formation " + str(diction['date_fin'])[0:10] + "  "

        # Verification de la cohérence des dates d'inscription si on a bien une date debut et une date de fin
        if( "date_debut_inscription" in diction.keys() and "date_fin_inscription" in diction.keys() ):
            if (str(diction['date_debut_inscription']).strip() != "" and str(
                    diction['date_fin_inscription']).strip() != ""):
                local_status = mycommon.CheckisDate(str(diction['date_debut_inscription'])[0:10])
                if (local_status is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " - Formation : " + str(
                            code_session) + " : Impossible de créer/mettre à jour la session de formation. La  date_debut_inscription  n'est pas au format jj/mm/aaaa ")

                    return False, " -Formation : " + str(
                        code_session) + " : La date de début d'inscription  n'est pas au format jj/mm/aaaa "

                local_status = mycommon.CheckisDate(str(diction['date_fin_inscription'])[0:10])
                if (local_status is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " -Formation : " + str(
                            code_session) + " : La date de fin d'inscription n'est pas au format jj/mm/aaaa ")

                    return False, " - Formation : " + str(
                        code_session) + " :La date de fin d'inscription n'est pas au format jj/mm/aaaa "


                if (datetime.strptime(str(diction['date_debut_inscription'])[0:10], '%d/%m/%Y') > datetime.strptime(
                        str(diction['date_fin_inscription'])[0:10], '%d/%m/%Y')):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " Formation : "+str(code_session)+" : La  date de début d'inscription " + str(diction['date_debut_inscription'])[0:10] + \
                           " est postérieure à la date de fin d'inscription  " + str(diction['date_fin_inscription'])[0:10] + "  " )

                    return False, " Formation : "+str(code_session)+" : La  date de début d'inscription " + str(diction['date_debut_inscription'])[0:10] + \
                           " est postérieure à la date de fin d'inscription  " + str(diction['date_fin_inscription'])[0:10] + "  "



                # Verification : Date de debut inscription n'est pas >  date de fin de formation
                if (datetime.strptime(str(diction['date_debut_inscription'])[0:10], '%d/%m/%Y') > datetime.strptime(
                        str(diction['date_fin'])[0:10], '%d/%m/%Y')):
                    mycommon.myprint(
                        str(inspect.stack()[0][3]) + " - Session de Formation : La date de debut d'inscription " + str( diction['date_debut_inscription'])+ " est postérieure à la date de fin de formation  " + str(diction['date_fin']))

                    return False, " Session de Formation : La date de debut d'inscription  " + str( diction['date_debut_inscription'])[0:10] + " est postérieure à la date de fin de formation  " + str(diction['date_fin'])[0:10]+" "

                # Verification : Date de fin inscription n'est pas >  date de fin de formation
                if (datetime.strptime(str(diction['date_fin_inscription'])[0:10], '%d/%m/%Y') > datetime.strptime(
                        str(diction['date_fin'])[0:10], '%d/%m/%Y')):
                    mycommon.myprint(
                        str(inspect.stack()[0][3]) + " -Formation : "+str(code_session)+" :  La date de fin d'inscription " + str(
                            diction['date_fin_inscription'])[0:10] + " est postérieure à la date de fin de la formation  " + str(
                            diction['date_fin'])[0:10])

                    return False, " Formation : "+str(code_session)+" : La date de fin d'inscription  " + str(
                        diction['date_fin_inscription'])[0:10] + " est postérieure à la date de fin de la formation  " + str(
                        diction['date_fin'])[0:10] + " "


        """
        mytoday = datetime.today().strftime("%d/%m/%Y")
        if (datetime.strptime(str(diction['date_debut'])[0:10], '%d/%m/%Y') < datetime.strptime(str(mytoday).strip(), '%d/%m/%Y')) :
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " - Impossible de créer/mettre à jour la session de formation : La date de debut de sessions " + str(
                    diction['date_debut']) +" est antérieure à la date du jour  ")

            return False, "  - Impossible de créer/mettre à jour la session de formation : La date de debut de sessions " + str(
                    diction['date_debut']) +" est antérieure à la date du jour   "

        """
        # Fin Controle de cohérence sur les dates


        """
        Verification avant ajout ou mise à jour
        1 - si le champ 'session_id' est renseingé alors c'est une mise à jour.
        Dans le cas d'une mise à jour, 
        je verifie s'il y a des inscription avec les code session actuellement en base, si oui, alors on refuse la modication
        /!\ : DU CODE SESSION, mais on accepte les autres champs car le session est deja utilisé.
        
        2 - Si le champ 'session_id' N'EST PAS renseingé alors c'est une creation. 
        On verifie que ce code session n'est pas deja utilisé pour cette formation.
        .
        
        """
        Warning_Message = ""
        if(len(str(session_id)) > 0 ):
            # Il s'agit d'une mise à jour

            # Etape 1 : recuperation du code session en base
            local_retval1 = MYSY_GV.dbname['session_formation'].find_one({'_id':ObjectId(str(session_id))})
            if( local_retval1 is None or 'code_session' not in  local_retval1.keys() or 'class_internal_url' not in  local_retval1.keys() ):
                mycommon.myprint(str(inspect.stack()[0][3]) + " - Cette session n'as pas de code session ")
                return False, "Impossible de récupérer le code de la session (1) "

            existing_session_code = local_retval1['code_session']
            existing_class_internal_url = local_retval1['class_internal_url']
            current_step = local_retval1['session_etape']





            local_retval = MYSY_GV.dbname['inscription'].count_documents({'session_id':str(existing_session_code),
                                                                          'class_internal_url':str(existing_class_internal_url)})

            if( local_retval > 0):
                mycommon.myprint(str(inspect.stack()[0][3]) + " - Cette session a déjà des inscriptions. Le code de la session ne peut etre modifié. Les autres champs ont été correctement mis à jour")
                mydata['code_session'] = existing_session_code  # On remet le actuellement en base de données
                Warning_Message = "Cette session a déjà des inscriptions. Le code de la session ne peut etre modifié. Les autres champs ont été correctement mis à jour"


            #print(" ###laaa mydata = ", mydata)
            coll_name = MYSY_GV.dbname['session_formation']
            ret_val = coll_name.find_one_and_update(
                {'_id':ObjectId(str(session_id)), 'valide':'1'},
                {"$set": mydata},
                upsert=False,
                return_document=ReturnDocument.AFTER
            )

            if (ret_val is None or '_id' not in ret_val.keys()):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Impossible d'ajouter/mettre à jour la session '" + str(
                        diction['code_session']) + "' ")
                return False, "Impossible d'ajouter/mettre à jour la session '" + str(diction['code_session']) + "' "

            new_step = ret_val['session_etape']


            """
            # Ajout de l'evenement dans l'historique
            """

            # Cas particulier : Verifier s'il y a eu changement d'session_etape, pour préciser cela dans le log de l'historique
            major_change_text = ""
            if( str(new_step) != str(current_step)):
                major_change_text = " Changement étape : "+str(current_step)+" ==> "+str(new_step)+" "

            now = str(datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
            history_event_dict = {}
            history_event_dict['token'] = diction['token']
            history_event_dict['related_collection'] = "session_formation"
            history_event_dict['related_collection_recid'] = str(ret_val['_id'])
            history_event_dict['action_date'] = str(now)
            if( major_change_text == "" ):
                history_event_dict['action_description'] = "Mise à jour "
            else:
                history_event_dict['action_description'] = str(major_change_text)

            local_status, local_retval = mycommon.Collection_Historique.Add_Historique_Event(history_event_dict)
            if (local_status is False):
                mycommon.myprint(
                    " WARNING : Impossible de logguer l'historique pour l'évènement : " + str(history_event_dict))


            #print("len(str(Warning_Message)) = ", len(str(Warning_Message)), )
            if(len(str(Warning_Message)) > 2 ):
                return True, str(Warning_Message)

            return True, " La session de formation  à bien été mise à jour"

        if (len(str(session_id)) <= 0):
            # La session n'existe pas, on fait une simple creation

            #print(" ### mydata = ", mydata)
            coll_name = MYSY_GV.dbname['session_formation']
            ret_val = coll_name.insert_one(mydata)

            local_inserted_id = ret_val.inserted_id

            if (ret_val is None or not hasattr(ret_val, 'inserted_id') ):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Impossible d'ajouter la session '" + str(
                        diction['code_session']) + "' ")
                return False, "Impossible d'ajouter la session '" + str(diction['code_session']) + "' "

            """
                    # Ajout de l'evenement dans l'historique
                    """
            now = str(datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
            history_event_dict = {}
            history_event_dict['token'] = diction['token']
            history_event_dict['related_collection'] = "session_formation"
            history_event_dict['related_collection_recid'] = str(local_inserted_id)
            history_event_dict['action_date'] = str(now)
            history_event_dict['action_description'] = "Creation "
            local_status, local_retval = mycommon.Collection_Historique.Add_Historique_Event(history_event_dict)
            if (local_status is False):
                mycommon.myprint(
                    " WARNING : Impossible de logguer l'historique pour l'évènement : " + str(history_event_dict))


            return True, " La session de formation  à bien été créée"


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de créer / mettre à jour la session de formation"


"""
Fonction de recuperation d'une session de formation. 

/!\ : il ne doit pas y avoir plus d'une session.

si le result > 1 une session, alors erreur d'inchorence d'info.
"""

def GetSessionFormation(diction):
    try:

        field_list = ['token', 'class_internal_url', 'session_id', ]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, "Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', 'class_internal_url', 'session_id', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Toutes les informations necessaires n'ont pas été fournies"

        # Recuperation du recid du partner
        mydata = {}
        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data({'token':str(diction['token'])})
        if (local_status is not True):
            return local_status, my_partner

        partner_recid = my_partner['recid']

        class_internal_url = ""
        if ("class_internal_url" in diction.keys()):
            if diction['class_internal_url']:
                class_internal_url = diction['class_internal_url']

        session_id = ""
        if ("session_id" in diction.keys()):
            if diction['session_id']:
                session_id = diction['session_id']

        if(str(session_id).strip() == ""):
            return True, []

        coll_session = MYSY_GV.dbname['session_formation']

        myquery = [{'$match':{'_id':ObjectId(str(session_id)), 'class_internal_url':class_internal_url, 'partner_owner_recid':str(partner_recid)}},
            {'$lookup':
                {
                    'from': 'myclass',
                    'localField': 'class_internal_url',
                    'foreignField': 'internal_url',
                    'pipeline': [{'$project': {'title': 1, 'lms_class_code':1, 'external_code':1, 'published':1}}],
                    'as': 'myclass'
                }
            }
        ]

        #print(" ##### myquery GetSessionFormation = "+str(myquery))
        RetObject = []
        nb_val = 0
        for retval in coll_session.aggregate(myquery):
            #print(" ##### retval = " + str(retval))
            user = {}
            user = retval
            user['id'] = str(nb_val)
            nb_val = nb_val + 1
            title = ""
            lms_class_code = ""
            class_external_code = ""
            class_ispublished = "0"

            if ('myclass' in retval.keys() and len(retval['myclass']) > 0):

                if( "title" in retval['myclass'][0].keys()):
                    title = retval['myclass'][0]['title']

                if ("lms_class_code" in retval['myclass'][0].keys()):
                    lms_class_code = retval['myclass'][0]['lms_class_code']


                if ("external_code" in retval['myclass'][0].keys()):
                    class_external_code = retval['myclass'][0]['external_code']


                if ("published" in retval['myclass'][0].keys()):
                    class_ispublished = retval['myclass'][0]['published']

            user['title'] = title
            user['lms_class_code'] = lms_class_code
            user['class_external_code'] = class_external_code
            user['class_ispublished'] = class_ispublished

            if ("invoiced_statut" in retval.keys()):
                user['invoiced_statut'] = retval['invoiced_statut']
            else:
                user['invoiced_statut'] = "0"

            formateur_nom_prenom = ""
            # Si il y a un code formateur_id, alors on va recuperer les nom et prenom du formation
            if( "formateur_id" in retval.keys() and retval['formateur_id']):

                formateur_data = MYSY_GV.dbname['ressource_humaine'].find_one({'_id':ObjectId(str(retval['formateur_id'])),
                                                                               'valide':'1',
                                                                               'locked':'0',
                                                                               'partner_recid':str(partner_recid)
                                                                               })

                if(formateur_data and "nom" in formateur_data.keys() and "prenom" in formateur_data.keys() ):
                    formateur_nom_prenom = str(formateur_data['nom'])+" "+str(formateur_data['prenom'])


            user['formateur_nom_prenom'] = formateur_nom_prenom

            site_formation_id = ""
            site_formation_code = ""
            if("site_formation_id" in retval.keys() and retval['site_formation_id']) :
                site_formation_data = MYSY_GV.dbname['site_formation'].find_one({'_id':ObjectId(str(retval['site_formation_id'])),
                                                                                 'valide':'1',
                                                                                 'locked':'0',
                                                                                 'partner_owner_recid':str(my_partner['recid'])})

                if( site_formation_data and "code_site" in site_formation_data.keys() ):
                    site_formation_code = site_formation_data['code_site']
                    site_formation_id = str(retval['site_formation_id'])

            user['site_formation_id'] = site_formation_id
            user['site_formation_code'] = site_formation_code

            #print(" ### user = ", user)
            RetObject.append(mycommon.JSONEncoder().encode(user))



        # Si le nombre de session trouvé est > 1 alors incohérence, retourner une message d'erreur
        if( nb_val > 1 ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " - la query  "+str(myquery)+" retourne "+str(nb_val)+" session. ceci n'est pas normal. il ne doit pas y avoir plus 1 session")
            return False, " Les informations de la session "+str(session_id)+" sont incohérentes. Merci de contacter le support"

        return True, RetObject



    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la session de formation"


"""
Recuperer les données d'un session de formation à partir du _Id seulement et token
"""
def Get_Given_SessionFormation_From_Id(diction):
    try:

        field_list = ['token', 'session_id', ]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, "de récupérer la liste des stagiaires . Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', 'session_id', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des stagiaires, Toutes les informations necessaires n'ont pas été fournies"

        # Recuperation du recid du partner
        mydata = {}
        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data({'token':str(diction['token'])})
        if (local_status is not True):
            return local_status, my_partner

        partner_recid = my_partner['recid']



        session_id = ""
        if ("session_id" in diction.keys()):
            if diction['session_id']:
                session_id = diction['session_id']

        if(str(session_id).strip() == ""):
            return True, []

        coll_session = MYSY_GV.dbname['session_formation']

        myquery = [{'$match':{'_id':ObjectId(str(session_id)),  'partner_owner_recid':str(partner_recid)}},
            {'$lookup':
                {
                    'from': 'myclass',
                    'localField': 'class_internal_url',
                    'foreignField': 'internal_url',
                    'pipeline': [{'$project': {'title': 1, 'lms_class_code':1, 'external_code':1, 'published':1}}],
                    'as': 'myclass'
                }
            }
        ]

        print(" ##### myquery Get_Given_SessionFormation_From_Is = "+str(myquery))
        RetObject = []

        nb_val = 0
        for retval in coll_session.aggregate(myquery):
            #print(" ##### retval = " + str(retval))
            user = {}
            user = retval
            user['id'] = str(nb_val)
            nb_val = nb_val + 1
            title = ""
            lms_class_code = ""
            class_external_code = ""
            class_ispublished = "0"

            if ('myclass' in retval.keys() and len(retval['myclass']) > 0):

                if( "title" in retval['myclass'][0].keys()):
                    title = retval['myclass'][0]['title']

                if ("lms_class_code" in retval['myclass'][0].keys()):
                    lms_class_code = retval['myclass'][0]['lms_class_code']


                if ("external_code" in retval['myclass'][0].keys()):
                    class_external_code = retval['myclass'][0]['external_code']


                if ("published" in retval['myclass'][0].keys()):
                    class_ispublished = retval['myclass'][0]['published']

            user['title'] = title
            user['lms_class_code'] = lms_class_code
            user['class_external_code'] = class_external_code
            user['class_ispublished'] = class_ispublished

            if ("invoiced_statut" in retval.keys() ):
                user['invoiced_statut'] = retval['invoiced_statut']

            else:
                user['invoiced_statut'] = "0"


            formateur_nom_prenom = ""
            # Si il y a un code formateur_id, alors on va recuperer les nom et prenom du formation
            if( "formateur_id" in retval.keys() and retval['formateur_id']):

                formateur_data = MYSY_GV.dbname['ressource_humaine'].find_one({'_id':ObjectId(str(retval['formateur_id'])),
                                                                               'valide':'1',
                                                                               'locked':'0',
                                                                               'partner_recid':str(partner_recid)
                                                                               })

                if(formateur_data and "nom" in formateur_data.keys() and "prenom" in formateur_data.keys() ):
                    formateur_nom_prenom = str(formateur_data['nom'])+" "+str(formateur_data['prenom'])


            user['formateur_nom_prenom'] = formateur_nom_prenom
            #print(" ### user = ", user)
            RetObject.append(mycommon.JSONEncoder().encode(user))



        # Si le nombre de session trouvé est > 1 alors incohérence, retourner une message d'erreur
        if( nb_val > 1 ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " - la query  "+str(myquery)+" retourne "+str(nb_val)+" session. ceci n'est pas normal. il ne doit pas y avoir plus 1 session")
            return False, " Les informations de la session "+str(session_id)+" sont incohérentes. Merci de contacter le support"

        return True, RetObject



    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la session de formation"


"""
Cette fonction recupere la liste de toutes les sessions de formation actives et valides
d'une formation données ET les sessions "on demande"

/!\ : Cette fonction est utilisée en mode non connecté et en mode  connecté.
Donc faire attention au controle de la connexion
"""
def GetActiveSessionFormation_List(diction):
    try:

        field_list = ['token', 'class_internal_url']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Impossible de récupérer la liste des session de formation"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = [  'class_internal_url', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des session de formation"

        # Le controle de token n'est effectué que une valeur est fournie dans le token
        if( 'token' in diction.keys() and len(str(diction['token'])) > 0) :
            local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
            if (local_status is not True):
                return local_status, my_partner


        # Recuperation du recid du partner
        mydata = {}

        class_internal_url = ""
        if ("class_internal_url" in diction.keys()):
            if diction['class_internal_url']:
                class_internal_url = diction['class_internal_url']

        coll_session = MYSY_GV.dbname['session_formation']
        myquery = {}
        myquery['class_internal_url'] = class_internal_url


        myquery['valide'] = "1"
        myquery['session_status'] = "1"


        RetObject = []

        print(" ### GetActiveSessionFormation_List myquery = ", myquery)
        for retval in coll_session.find(myquery):

            local_tmp = retval
            ## Verification des conditions supplementaires :
            # en realité c'est parce que c'est chaud de le faire avec pymongo
            tmp_debut_inscritp = str(local_tmp['date_debut_inscription']).strip().split(" ")
            tmp_fin_inscritp = str(local_tmp['date_fin_inscription']).strip().split(" ")

            debut_inscr = datetime.strptime(str(tmp_debut_inscritp[0]).strip(), '%d/%m/%Y')
            fin_inscr = datetime.strptime(str(tmp_fin_inscritp[0]).strip(), '%d/%m/%Y')

            #print(" #### date_debut_inscription = ", debut_inscr,  " date_fin_inscription = ", fin_inscr, " NOW = ",datetime.now())
            if(debut_inscr <= datetime.now() and  fin_inscr >= datetime.now()):
                RetObject.append(mycommon.JSONEncoder().encode(retval))
            else:
                if ("session_ondemande" in local_tmp.keys()):
                    if( str(local_tmp['session_ondemande']).strip() == "1"):
                        RetObject.append(mycommon.JSONEncoder().encode(retval))

        #print("####  GetActiveSessionFormation_List RetObject = "+str(RetObject))
        return True, RetObject


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la liste des sessions de formation valides et actives."



"""
Cette fonction recuperer les liste de sessions de formations
d'un partenaire, mais avec une liste reduite de champs
- _id
- code_session
- titre
- class_internal_url
- date_debut
- date_fin
 
"""
def Get_Partner_Session_Ftion_Reduice_Fields(diction):
    try:

        field_list = ['token',]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Impossible de récupérer la liste des session de formation"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = [  'token', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des session de formation"

        # Le controle de token n'est effectué que une valeur est fournie dans le token
        if( 'token' in diction.keys() and len(str(diction['token'])) > 0) :
            local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
            if (local_status is not True):
                return local_status, my_partner

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        RetObject = []

        RetObject = []
        val_tmp = 0

        for retval in MYSY_GV.dbname['session_formation'].find({'partner_owner_recid':str(my_partner['recid']),
                                                                'valide':'1'},
                                                               {'_id':1, 'code_session':1, 'titre':1,
                                                                'class_internal_url':1, 'date_debut':1,
                                                                'date_fin':1}):
            user = retval
            user['id'] = str(val_tmp)

            RetObject.append(mycommon.JSONEncoder().encode(user))
            val_tmp = val_tmp + 1

        return True, RetObject


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la liste des sessions de formation valides et actives."




"""
Cette fonction recupere UNIQUEMENT LES VILLES ET SI A DISTANCEsessions de formation actives et valides
d'une formation données ET les sessions "on demande"

on doit renvoyer les villes de manière unique et eviter les doublon de villes

"""
def GetActiveSession_Cities_And_Distance_Formation_List(diction):
    try:

        field_list = ['token', 'class_internal_url']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Impossible de récupérer la liste des session de formation"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = [  'class_internal_url', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des session de formation"

        # Le controle de token n'est effectué que une valeur est fournie dans le token
        if( 'token' in diction.keys() and len(str(diction['token'])) > 0) :
            local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
            if (local_status is not True):
                return local_status, my_partner


        # Recuperation du recid du partner
        mydata = {}

        liste_session_cities = []

        class_internal_url = ""
        if ("class_internal_url" in diction.keys()):
            if diction['class_internal_url']:
                class_internal_url = diction['class_internal_url']

        coll_session = MYSY_GV.dbname['session_formation']
        myquery = {}
        myquery['class_internal_url'] = class_internal_url


        myquery['valide'] = "1"
        myquery['session_status'] = "1"


        RetObject = []

        print(" ### GetActiveSessionFormation_List myquery = ", myquery)
        for retval in coll_session.find(myquery):

            local_tmp = retval
            ## Verification des conditions supplementaires :
            # en realité c'est parce que c'est chaud de le faire avec pymongo
            tmp_debut_inscritp = str(local_tmp['date_debut_inscription']).strip().split(" ")
            tmp_fin_inscritp = str(local_tmp['date_fin_inscription']).strip().split(" ")

            debut_inscr = datetime.strptime(str(tmp_debut_inscritp[0]).strip(), '%d/%m/%Y')
            fin_inscr = datetime.strptime(str(tmp_fin_inscritp[0]).strip(), '%d/%m/%Y')

            #print(" #### date_debut_inscription = ", debut_inscr,  " date_fin_inscription = ", fin_inscr, " NOW = ",datetime.now())
            if(debut_inscr <= datetime.now() and  fin_inscr >= datetime.now()):

                if( "ville" in retval.keys() and retval['ville']):
                    if( str(retval['ville']) not in liste_session_cities):
                        liste_session_cities.append(str(retval['ville']))
                        RetObject.append(mycommon.JSONEncoder().encode(str(retval['ville'])))

                elif( "distantiel" in retval.keys() and str(retval['distantiel']) == "1"):
                    if ( "A Distance" not in liste_session_cities):
                        liste_session_cities.append("A Distance")
                        RetObject.append(mycommon.JSONEncoder().encode("A Distance"))

            else:
                if ("session_ondemande" in local_tmp.keys()):
                    if( str(local_tmp['session_ondemande']).strip() == "1"):
                        if ("A la demande" not in liste_session_cities):
                            liste_session_cities.append("A la demande")
                            RetObject.append(mycommon.JSONEncoder().encode("A la demande"))


        #print("####  GetActiveSessionFormation_List RetObject = "+str(RetObject))
        return True, RetObject


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la liste des villes des sessions de formation valides et actives."




"""
Cette fonction récupérer la liste des toutes les sessions
de formation valides pour une formation données.
Qu'elles soient cloturées ou pas."""
def GetAllValideSessionFormation_List(diction):
    try:

        field_list = ['token', 'class_internal_url']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Impossible de récupérer la liste des session de formation"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', 'class_internal_url', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des session de formation"

        # Recuperation du recid du partner
        mydata = {}

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        class_internal_url = ""
        if ("class_internal_url" in diction.keys()):
            if diction['class_internal_url']:
                class_internal_url = diction['class_internal_url']

        coll_session = MYSY_GV.dbname['session_formation']
        myquery = {}
        myquery['class_internal_url'] = class_internal_url
        myquery['partner_owner_recid'] = str(my_partner['recid'])
        myquery['valide'] = "1"

        print(" ##### myquery tt = "+str(myquery))
        RetObject = []

        for retval in coll_session.find(myquery):
            #print(" ##### retval = " + str(retval))
            RetObject.append(mycommon.JSONEncoder().encode(retval))

        return True, RetObject


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la liste des sessions de formation valides et actives."


"""
Cette fonction recupere toutes les sessions de formation d'un partenaire
"""
def GetAllValideSessionPartner_List(diction):
    try:

        field_list = ['token']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Impossible de récupérer la liste des session de formation"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des session de formation"

        # Recuperation du recid du partner
        mydata = {}

        token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner


        coll_session = MYSY_GV.dbname['session_formation']

        filt_class_partner_recid = {'partner_owner_recid': str(my_partner['recid'])}

        query = [{'$match': {'partner_owner_recid':str(my_partner['recid'])}},
                 { '$sort': {'_id': -1}},
            {'$lookup':
                {
            'from': 'myclass',
           'localField': 'class_internal_url',
           'foreignField':'internal_url',
          'pipeline': [{'$match': filt_class_partner_recid}, {'$project': {'title': 1, 'domaine': 1,
                                                                             'duration': 1, 'duration_unit': 1,  'external_code':1, 'published':1}}],
           'as': 'myclass_collection'
                }
            }
        ]
        #print("#### query = ", query)
        RetObject = []
        cpt = 1
        for retVal in MYSY_GV.dbname['session_formation'].aggregate(query):
            if ('myclass_collection' in retVal.keys() and len(retVal['myclass_collection']) > 0):
                val = {}
                val['id'] = str(cpt)
                cpt = cpt + 1
                val['class_internal_url'] = retVal['class_internal_url']
                val['_id'] = retVal['_id']
                val['code_session'] = retVal['code_session']

                if( "session_etape" in retVal.keys()):
                    val['session_etape'] = retVal['session_etape']
                else:
                    val['session_etape'] = ""

                if ("invoiced_statut" in retVal.keys()):
                    val['invoiced_statut'] = retVal['invoiced_statut']
                else:
                    val['invoiced_statut'] = "0"

                if( "titre" in retVal.keys()):
                    val['titre'] = retVal['titre']
                else:
                    val['titre'] = ""

                if ("location_type" in retVal.keys()):
                    val['location_type'] = retVal['location_type']
                else:
                    val['location_type'] = ""

                if ("is_bpf" in retVal.keys()):
                    val['is_bpf'] = retVal['is_bpf']
                else:
                    val['is_bpf'] = ""


                if ("session_status" in retVal.keys()):
                    val['session_status'] = retVal['session_status']
                else:
                    val['session_status'] = "0"



                val['date_debut'] = retVal['date_debut'][0:10]
                val['date_fin'] = retVal['date_fin'][0:10]
                val['date_debut_inscription'] = retVal['date_debut_inscription'][0:10]
                val['date_fin_inscription'] = retVal['date_fin_inscription'][0:10]

                val['distantiel'] = retVal['distantiel']

                site_formation_id = ""
                site_formation_code = ""
                if ("site_formation_id" in retVal.keys() and retVal['site_formation_id']):
                    site_formation_data = MYSY_GV.dbname['site_formation'].find_one(
                        {'_id': ObjectId(str(retVal['site_formation_id'])),
                         'valide': '1',
                         'locked': '0',
                         'partner_owner_recid': str(my_partner['recid'])})

                    if (site_formation_data and "code_site" in site_formation_data.keys()):
                        site_formation_code = site_formation_data['code_site']
                        site_formation_id = str(retVal['site_formation_id'])

                val['site_formation_id'] = site_formation_id
                val['site_formation_code'] = site_formation_code

                if ("formateur_id" in retVal.keys() and  retVal['formateur_id']):
                    val['formateur_id'] = retVal['formateur_id']


                    # On va aller chercher le nom et prenom du formateur
                    fomateur_data = MYSY_GV.dbname['ressource_humaine'].find_one(
                        {'_id': ObjectId(str(retVal['formateur_id'])),
                         'valide': '1',
                         'locked': '0',
                         'partner_recid': str(my_partner['recid'])})
                    if (fomateur_data is None):
                        val['formateur'] = " Invalide"
                    else:
                        nom = ""
                        prenom = ""
                        if ("nom" in fomateur_data.keys()):
                            nom = fomateur_data['nom']
                        if ("prenom" in fomateur_data.keys()):
                            prenom = fomateur_data['prenom']
                        val['formateur'] = str(nom) + " " + str(prenom)

                else:
                    val['formateur_id'] = ""


                nb_participant = "1"
                if( "nb_participant" in retVal.keys()):
                    nb_participant = retVal['nb_participant']

                val['nb_participant'] = nb_participant


                val['presentiel'] = retVal['presentiel']
                val['prix_session'] = retVal['prix_session']

                val['title'] = retVal['myclass_collection'][0]['title']
                val['class_published'] = retVal['myclass_collection'][0]['published']

                if ("domaine" in retVal['myclass_collection'][0].keys()):
                    val['domaine'] = retVal['myclass_collection'][0]['domaine']
                else:
                    val['domaine'] = ""

                val['class_external_code'] = retVal['myclass_collection'][0]['external_code']

                if( str(retVal['myclass_collection'][0]['duration_unit']) == "heure"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration'])+" h"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "jour"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " j"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "semaine"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " s"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "mois"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " m"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "annee"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " a"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "user_rythme"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " u"

                else :
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " ?"

                val['duration'] = str(retVal['myclass_collection'][0]['duration'])
                val['duration_unit'] = retVal['myclass_collection'][0]['duration_unit']

                ## Recuperation du nombre d'inscrits
                Count_Inscrit = MYSY_GV.dbname['inscription'].count_documents({'session_id':str(retVal['_id']), 'class_internal_url':str(retVal['class_internal_url']),
                                                                               'status':"1", 'partner_owner_recid':str(my_partner['recid'])})

                qry_nb_inscrit = {'session_id':str(retVal['_id']), 'class_internal_url':str(retVal['class_internal_url']),
                                                                               'status':"1", 'partner_owner_recid':str(my_partner['recid'])}


                val['nb_inscrit'] = str(Count_Inscrit)

                ## Recuperation du nombre de preinscrits
                Count_Preinscrit = MYSY_GV.dbname['inscription'].count_documents(
                    {'session_id': str(retVal['_id']), 'class_internal_url': str(retVal['class_internal_url']),
                     'status': "0",
                     'partner_owner_recid':str(my_partner['recid'])})

                val['nb_preinscrit'] = str(Count_Preinscrit)

                taux_remplissage = "0"
                if( str(nb_participant) != "0" and str(nb_participant).strip() != ""):
                    taux_remplissage = round(int(mycommon.tryInt(Count_Inscrit)) / int(mycommon.tryInt(nb_participant)), 2)

                val['taux_remplissage'] = str(taux_remplissage)

                """
                29/05/2024 : On va aller recuperer le nombre de personnes inscrites sur des devis non envoyés et non validés.
                Ceci permet de connaitre le nombre potentiel d'inscription en attentes.
                
                Regles : Cela concerne uniquement les devis envoyés aux clients et prospects mais non validé
                
                """

                locl_find_qry = {
                    '$and': [{'partner_owner_recid': str(my_partner['recid']), 'valide': '1', 'locked': '0',
                              'order_header_type':'devis', 'is_validated':'0', 'date_envoi_quotation': { "$exists": True }  }, {}, ]}


                local_new_myquery_find_order = [{'$match': locl_find_qry},
                                          {'$sort': {'_id': -1}},
                                          {"$addFields": {"partner_order_header_Id": {"$toString": "$_id"}}},
                                          {'$lookup':
                                              {
                                                  'from': 'partner_order_line',
                                                  'localField': "partner_order_header_Id",
                                                  'foreignField': 'order_header_id',
                                                  'pipeline': [
                                                      {'$match':
                                                           {'$and':
                                                                [
                                                                    {'order_line_formation':str(retVal['class_internal_url'])},
                                                                 {'partner_owner_recid': str(my_partner['recid']),
                                                                  'order_line_session_id':str(retVal['_id'])},
                                                                    {'valide': '1'}]}}, ],
                                                  'as': 'partner_order_line_collection'
                                              }
                                          },
                                                {
                                                    '$unwind': '$partner_order_line_collection'
                                                }
                                          ]
                total_qty = 0
                tab_quotation_ref = []

                for local_New_retVal in MYSY_GV.dbname['partner_order_header'].aggregate(local_new_myquery_find_order):
                    #print(" ### local_New_retVal = ", local_New_retVal)
                    if( "partner_order_line_collection" in  local_New_retVal.keys() ):
                        if( "order_line_qty" in local_New_retVal['partner_order_line_collection'].keys() and
                        len(str(local_New_retVal['date_envoi_quotation'])) > 5 ):
                            total_qty = total_qty + mycommon.tryFloat(local_New_retVal['partner_order_line_collection']['order_line_qty'])
                            tab_quotation_ref.append(str(local_New_retVal['order_header_ref_interne']))

                #print(" ### total_qty = ", total_qty)
                #print(" ### tab_quotation_ref = ", tab_quotation_ref)

                val['qty_in_quotation'] = str(total_qty)
                val['qty_in_quotation_list_quotation'] =  ', '.join(tab_quotation_ref)

                """
                Pour chaque session recuperer le statut de controle d'alert
                """
                is_session_alert = ""
                session_alert_message = ""

                local_diction = {}
                local_diction['token'] = str(diction['token'])
                local_diction['session_id'] = str(retVal['_id'])


                local_check_session_alert_status, local_check_session_alert_retval, local_check_session_alert_is_warning = mycommon.Check_Partner_Session_Alert(local_diction)

                
                if( local_check_session_alert_status ):
                    is_session_alert = local_check_session_alert_is_warning
                    session_alert_message = local_check_session_alert_retval


                val['is_session_alert'] = is_session_alert
                val['session_alert_message'] = session_alert_message



                RetObject.append(mycommon.JSONEncoder().encode(val))

        return True, RetObject


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la liste des sessions de formation valides et actives."


"""
Recuperation de la liste avec des filter du type like 
"""
def GetAllValideSessionPartner_List_filter_like(diction):
    try:

        field_list = ['token','class_title', 'code_session', 'class_external_code', 'session_start_date', 'session_end_date']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Impossible de récupérer la liste des session de formation"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des session de formation"

        # Recuperation du recid du partner
        mydata = {}

        token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        nb_hour_per_day = mycommon.Get_Partner_Hour_Per_Day(str(my_partner['recid']))
        if (nb_hour_per_day is False):
            nb_hour_per_day = "7"


        filt_class_title = {}
        if ("class_title" in diction.keys()):
            filt_class_title = {'title': {'$regex': str(diction['class_title']),"$options": "i"}}

        filt_class_external_code = {}
        if ("class_external_code" in diction.keys()):
            filt_class_external_code = {'external_code': {'$regex': str(diction['class_external_code']), "$options": "i"}}

        filt_code_session = {}
        if ("code_session" in diction.keys()):
            filt_code_session = {'code_session': {'$regex': str(diction['code_session']), "$options": "i"}}

        filt_session_start_date = ""
        if ("session_start_date" in diction.keys()):
            filt_session_start_date = str(diction['session_start_date'])[0:10]
            local_status = mycommon.CheckisDate(filt_session_start_date)
            if (local_status is False):
                mycommon.myprint(str(inspect.stack()[0][3]) + " Le filtre : 'date de debut de session' n'est pas au format jj/mm/aaaa.")
                return False, " Le filtre : 'date de debut de session' n'est pas au format jj/mm/aaaa."


        filt_session_end_date = ""
        if ("session_end_date" in diction.keys()):
            filt_session_end_date = str(diction['session_end_date'])[0:10]
            local_status = mycommon.CheckisDate(filt_session_end_date)
            if (local_status is False):
                mycommon.myprint(str(
                    inspect.stack()[0][3]) + " Le filtre : 'date de fin de session' n'est pas au format jj/mm/aaaa.")
                return False, " Le filtre : 'date de fin de session' n'est pas au format jj/mm/aaaa."

        coll_session = MYSY_GV.dbname['session_formation']

        query = [ {'$match':{ '$and' : [ filt_code_session,{'partner_owner_recid':str(my_partner['recid'])}] }} ,
                  {'$sort': {'_id': -1}},
            {'$lookup':
                {
                    'from': 'myclass',
                    'localField': 'class_internal_url',
                    'foreignField': 'internal_url',
                    'pipeline': [{'$match': { '$and' : [ filt_class_title,filt_class_external_code, {'partner_owner_recid':str(my_partner['recid'])} ]} }, {'$project': {'title': 1, 'domaine':1,
                                                                                               'duration':1, 'duration_unit':1, 'external_code':1}}],
                    'as': 'myclass_collection'
                }
            }
        ]

        #print("#### GetAllValideSessionPartner_List_filter_likequery = ", query)
        RetObject = []
        cpt = 1
        for retVal in MYSY_GV.dbname['session_formation'].aggregate(query):
            if( 'myclass_collection' in  retVal.keys() and len(retVal['myclass_collection']) > 0  ):

                val = {}
                val['id'] = str(cpt)
                cpt = cpt + 1
                val['_id'] = retVal['_id']
                val['class_internal_url'] = retVal['class_internal_url']
                val['code_session'] = retVal['code_session']

                if ("titre" in retVal.keys()):
                    val['titre'] = retVal['titre']
                else:
                    val['titre'] = ""

                if ("location_type" in retVal.keys()):
                    val['location_type'] = retVal['location_type']
                else:
                    val['location_type'] = ""

                if ("is_bpf" in retVal.keys()):
                    val['is_bpf'] = retVal['is_bpf']
                else:
                    val['is_bpf'] = ""


                if( "session_etape" in retVal.keys()):
                    val['session_etape'] = retVal['session_etape']
                else:
                    val['session_etape'] = ""

                if ("invoiced_statut" in retVal.keys()):
                    val['invoiced_statut'] = retVal['invoiced_statut']
                else:
                    val['invoiced_statut'] = "0"

                if( "session_status" in retVal.keys()):
                    val['session_status'] = retVal['session_status']
                else:
                    val['session_status'] = "0"

                val['date_debut'] = retVal['date_debut'][0:10]
                val['date_fin'] = retVal['date_fin'][0:10]
                val['date_debut_inscription'] = retVal['date_debut_inscription'][0:10]
                val['date_fin_inscription'] = retVal['date_fin_inscription'][0:10]

                if ("distantiel" in retVal.keys()):
                    val['distantiel'] = retVal['distantiel']
                else:
                    val['distantiel'] = "0"

                site_formation_id = ""
                site_formation_code = ""
                if ("site_formation_id" in retVal.keys() and retVal['site_formation_id']):
                    site_formation_data = MYSY_GV.dbname['site_formation'].find_one(
                        {'_id': ObjectId(str(retVal['site_formation_id'])),
                         'valide': '1',
                         'locked': '0',
                         'partner_owner_recid': str(my_partner['recid'])})

                    if (site_formation_data and "code_site" in site_formation_data.keys()):
                        site_formation_code = site_formation_data['code_site']
                        site_formation_id = str(retVal['site_formation_id'])

                val['site_formation_id'] = site_formation_id
                val['site_formation_code'] = site_formation_code



                if ("formateur_id" in retVal.keys() and  retVal['formateur_id']):
                    val['formateur_id'] = retVal['formateur_id']

                    # On va aller chercher le nom et prenom du formateur
                    fomateur_data = MYSY_GV.dbname['ressource_humaine'].find_one({'_id':ObjectId(str(retVal['formateur_id'])),
                                                                                  'valide':'1',
                                                                                  'locked':'0',
                                                                                  'partner_recid':str(my_partner['recid'])})
                    if( fomateur_data is None):
                        val['formateur'] = " Invalide"
                    else:
                        nom = ""
                        prenom = ""
                        if( "nom" in fomateur_data.keys()):
                            nom = fomateur_data['nom']
                        if ("prenom" in fomateur_data.keys()):
                            prenom = fomateur_data['prenom']
                        val['formateur'] = str(nom)+" "+str(prenom)


                else:
                    val['formateur_id'] = ""

                nb_participant = "1"
                if ("nb_participant" in retVal.keys()):
                    nb_participant = retVal['nb_participant']

                val['nb_participant'] = nb_participant

                if ("presentiel" in retVal.keys()):
                    val['presentiel'] = retVal['presentiel']
                else:
                    val['presentiel'] = "0"

                if ("prix_session" in retVal.keys()):
                    val['prix_session'] = retVal['prix_session']
                else:
                    val['prix_session'] = "0"

                val['title'] = retVal['myclass_collection'][0]['title']

                if( "domaine" in retVal['myclass_collection'][0].keys() ):
                    val['domaine'] = retVal['myclass_collection'][0]['domaine']
                else:
                    val['domaine'] = ""


                val['class_external_code'] = retVal['myclass_collection'][0]['external_code']

                if (str(retVal['myclass_collection'][0]['duration_unit']) == "heure"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " h"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "jour"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " j"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "semaine"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " s"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "mois"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " m"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "annee"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " a"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "user_rythme"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " u"

                else:
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " ?"

                val['duration'] = retVal['myclass_collection'][0]['duration']
                val['duration_unit'] = retVal['myclass_collection'][0]['duration_unit']

                ## Recuperation du nombre d'inscrits
                Count_Inscrit = MYSY_GV.dbname['inscription'].count_documents(
                    {'session_id': str(retVal['_id']), 'class_internal_url': str(retVal['class_internal_url']),
                     'status': "1"})
                val['nb_inscrit'] = str(Count_Inscrit)

                ## Recuperation du nombre de preinscrits
                Count_Preinscrit = MYSY_GV.dbname['inscription'].count_documents(
                    {'session_id': str(retVal['_id']), 'class_internal_url': str(retVal['class_internal_url']),
                     'status': "0"})

                val['nb_preinscrit'] = str(Count_Preinscrit)

                taux_remplissage = "0"
                if (str(nb_participant) != "0"):
                    taux_remplissage = round(int(mycommon.tryInt(Count_Inscrit)) / int(mycommon.tryInt(nb_participant)),
                                            2)

                val['taux_remplissage'] = str(taux_remplissage)

                """
                29/05/2024 : On va aller recuperer le nombre de personnes inscrites sur des devis non envoyés et non validés.
                Ceci permet de connaitre le nombre potentiel d'inscription en attentes.

                Regles : Cela concerne uniquement les devis envoyés aux clients et prospects mais non validé

                """

                locl_find_qry = {
                    '$and': [{'partner_owner_recid': str(my_partner['recid']), 'valide': '1', 'locked': '0',
                              'order_header_type': 'devis', 'is_validated': '0',
                              'date_envoi_quotation': {"$exists": True}}, {}, ]}

                local_new_myquery_find_order = [{'$match': locl_find_qry},
                                                {'$sort': {'_id': -1}},
                                                {"$addFields": {"partner_order_header_Id": {"$toString": "$_id"}}},
                                                {'$lookup':
                                                    {
                                                        'from': 'partner_order_line',
                                                        'localField': "partner_order_header_Id",
                                                        'foreignField': 'order_header_id',
                                                        'pipeline': [
                                                            {'$match':
                                                                {'$and':
                                                                    [
                                                                        {'order_line_formation': str(
                                                                            retVal['class_internal_url'])},
                                                                        {'partner_owner_recid': str(
                                                                            my_partner['recid']),
                                                                         'order_line_session_id': str(retVal['_id'])},
                                                                        {'valide': '1'}]}}, ],
                                                        'as': 'partner_order_line_collection'
                                                    }
                                                },
                                                {
                                                    '$unwind': '$partner_order_line_collection'
                                                }
                                                ]
                total_qty = 0
                tab_quotation_ref = []

                for local_New_retVal in MYSY_GV.dbname['partner_order_header'].aggregate(local_new_myquery_find_order):
                    # print(" ### local_New_retVal = ", local_New_retVal)
                    if ("partner_order_line_collection" in local_New_retVal.keys()):
                        if ("order_line_qty" in local_New_retVal['partner_order_line_collection'].keys() and
                                len(str(local_New_retVal['date_envoi_quotation'])) > 5):
                            total_qty = total_qty + mycommon.tryFloat(
                                local_New_retVal['partner_order_line_collection']['order_line_qty'])
                            tab_quotation_ref.append(str(local_New_retVal['order_header_ref_interne']))

                # print(" ### total_qty = ", total_qty)
                # print(" ### tab_quotation_ref = ", tab_quotation_ref)

                val['qty_in_quotation'] = str(total_qty)
                val['qty_in_quotation_list_quotation'] = ', '.join(tab_quotation_ref)

                if (filt_session_start_date and filt_session_end_date):
                    # Si on a un filtre sur la date debut et de fin de session
                    if ((datetime.strptime(str(retVal['date_debut'][0:10]).strip(), '%d/%m/%Y') >= datetime.strptime(
                            str(filt_session_start_date).strip(), '%d/%m/%Y'))
                            and
                            (datetime.strptime(str(retVal['date_fin'][0:10]).strip(),
                                               '%d/%m/%Y') <= datetime.strptime(str(filt_session_end_date).strip(),
                                                                                '%d/%m/%Y'))
                    ):
                        RetObject.append(mycommon.JSONEncoder().encode(val))


                elif (filt_session_start_date):
                    # Si on a un filtre uniquement sur la date de debut de session
                    if ((datetime.strptime(str(retVal['date_debut'][0:10]).strip(), '%d/%m/%Y') >= datetime.strptime(
                            str(filt_session_start_date).strip(), '%d/%m/%Y'))):
                        RetObject.append(mycommon.JSONEncoder().encode(val))


                elif (filt_session_end_date):
                    # Si on a un filtre uniquement sur la date de fin session
                    if ((datetime.strptime(str(retVal['date_fin'][0:10]).strip(), '%d/%m/%Y') <= datetime.strptime(
                            str(filt_session_end_date).strip(), '%d/%m/%Y'))):
                        RetObject.append(mycommon.JSONEncoder().encode(val))


                else:
                    RetObject.append(mycommon.JSONEncoder().encode(val))

        return True, RetObject


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la liste des sessions de formation valides et actives."


"""
Recuperation de la liste sans filtre
"""
def GetAllValideSessionPartner_List_no_filter(diction):
    try:

        field_list = ['token','class_title', 'code_session']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Impossible de récupérer la liste des session de formation"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des session de formation"

        # Recuperation du recid du partner
        mydata = {}

        token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner


        coll_session = MYSY_GV.dbname['session_formation']

        query = [{'$match':{'partner_owner_recid': str(my_partner['recid'])}   },
                 {'$lookup':
                     {
                         'from': 'myclass',
                         'localField': 'class_internal_url',
                         'foreignField': 'internal_url',
                         'pipeline': [{'$match': {'partner_owner_recid': str(my_partner['recid'])}  },
                                      {'$project': {'title': 1, 'domaine': 1,
                                                    'duration': 1, 'duration_unit': 1, 'external_code':1}}],
                         'as': 'myclass_collection'
                     }
                 }
                 ]

        print("#### GetAllValideSessionPartner_List_filter_likequery = ", query)
        RetObject = []
        cpt = 1
        for retVal in MYSY_GV.dbname['session_formation'].aggregate(query):
            if( 'myclass_collection' in  retVal.keys() and len(retVal['myclass_collection']) > 0  ):
                val = {}
                val['id'] = str(cpt)
                cpt = cpt + 1
                val['_id'] = retVal['_id']
                val['class_internal_url'] = retVal['class_internal_url']
                val['code_session'] = retVal['code_session']

                if ("titre" in retVal.keys()):
                    val['titre'] = retVal['titre']
                else:
                    val['titre'] = ""

                if ("location_type" in retVal.keys()):
                    val['location_type'] = retVal['location_type']
                else:
                    val['location_type'] = ""

                if ("is_bpf" in retVal.keys()):
                    val['is_bpf'] = retVal['is_bpf']
                else:
                    val['is_bpf'] = ""


                if( "session_etape" in retVal.keys()):
                    val['session_etape'] = retVal['session_etape']
                else:
                    val['session_etape'] = ""

                if ("invoiced_statut" in retVal.keys()):
                    val['invoiced_statut'] = retVal['invoiced_statut']
                else:
                    val['invoiced_statut'] = "0"

                if( "session_status" in retVal.keys()):
                    val['session_status'] = retVal['session_status']
                else:
                    val['session_status'] = "0"

                val['date_debut'] = retVal['date_debut'][0:10]
                val['date_fin'] = retVal['date_fin'][0:10]
                val['date_debut_inscription'] = retVal['date_debut_inscription'][0:10]
                val['date_fin_inscription'] = retVal['date_fin_inscription'][0:10]

                if ("distantiel" in retVal.keys()):
                    val['distantiel'] = retVal['distantiel']
                else:
                    val['distantiel'] = "0"


                if ("formateur_id" in retVal.keys() and  retVal['formateur_id']):
                    val['formateur_id'] = retVal['formateur_id']

                    # On va aller chercher le nom et prenom du formateur
                    fomateur_data = MYSY_GV.dbname['ressource_humaine'].find_one(
                        {'_id': ObjectId(str(retVal['formateur_id'])),
                         'valide': '1',
                         'locked': '0',
                         'partner_recid': str(my_partner['recid'])})
                    if (fomateur_data is None):
                        val['formateur'] = " Invalide"
                    else:
                        nom = ""
                        prenom = ""
                        if ("nom" in fomateur_data.keys()):
                            nom = fomateur_data['nom']
                        if ("prenom" in fomateur_data.keys()):
                            prenom = fomateur_data['prenom']
                        val['formateur'] = str(nom) + " " + str(prenom)
                else:
                    val['formateur_id'] = ""


                nb_participant = "1"
                if ("nb_participant" in retVal.keys()):
                    nb_participant = retVal['nb_participant']

                val['nb_participant'] = nb_participant

                if ("presentiel" in retVal.keys()):
                    val['presentiel'] = retVal['presentiel']
                else:
                    val['presentiel'] = "0"

                if ("prix_session" in retVal.keys()):
                    val['prix_session'] = retVal['prix_session']
                else:
                    val['prix_session'] = "0"

                val['title'] = retVal['myclass_collection'][0]['title']

                if( "domaine" in retVal['myclass_collection'][0].keys() ):
                    val['domaine'] = retVal['myclass_collection'][0]['domaine']
                else:
                    val['domaine'] = ""


                val['class_external_code'] = retVal['myclass_collection'][0]['external_code']

                if (str(retVal['myclass_collection'][0]['duration_unit']) == "heure"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " h"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "jour"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " j"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "semaine"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " s"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "mois"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " m"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "annee"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " a"

                elif (str(retVal['myclass_collection'][0]['duration_unit']) == "user_rythme"):
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " u"

                else:
                    val['duration_concat'] = str(retVal['myclass_collection'][0]['duration']) + " ?"

                val['duration'] = retVal['myclass_collection'][0]['duration']
                val['duration_unit'] = retVal['myclass_collection'][0]['duration_unit']

                ## Recuperation du nombre d'inscrits
                Count_Inscrit = MYSY_GV.dbname['inscription'].count_documents(
                    {'session_id': str(retVal['_id']), 'class_internal_url': str(retVal['class_internal_url']),
                     'status': "1"})
                val['nb_inscrit'] = str(Count_Inscrit)

                ## Recuperation du nombre de preinscrits
                Count_Preinscrit = MYSY_GV.dbname['inscription'].count_documents(
                    {'session_id': str(retVal['_id']), 'class_internal_url': str(retVal['class_internal_url']),
                     'status': "0"})

                val['nb_preinscrit'] = str(Count_Preinscrit)

                taux_remplissage = "0"
                if (str(nb_participant) != "0"):
                    taux_remplissage = round(int(mycommon.tryInt(Count_Inscrit)) / int(mycommon.tryInt(nb_participant)),
                                            2)

                val['taux_remplissage'] = str(taux_remplissage)

                """
                29/05/2024 : On va aller recuperer le nombre de personnes inscrites sur des devis non envoyés et non validés.
                Ceci permet de connaitre le nombre potentiel d'inscription en attentes.

                Regles : Cela concerne uniquement les devis envoyés aux clients et prospects mais non validé

                """

                locl_find_qry = {
                    '$and': [{'partner_owner_recid': str(my_partner['recid']), 'valide': '1', 'locked': '0',
                              'order_header_type': 'devis', 'is_validated': '0',
                              'date_envoi_quotation': {"$exists": True}}, {}, ]}

                local_new_myquery_find_order = [{'$match': locl_find_qry},
                                                {'$sort': {'_id': -1}},
                                                {"$addFields": {"partner_order_header_Id": {"$toString": "$_id"}}},
                                                {'$lookup':
                                                    {
                                                        'from': 'partner_order_line',
                                                        'localField': "partner_order_header_Id",
                                                        'foreignField': 'order_header_id',
                                                        'pipeline': [
                                                            {'$match':
                                                                {'$and':
                                                                    [
                                                                        {'order_line_formation': str(
                                                                            retVal['class_internal_url'])},
                                                                        {'partner_owner_recid': str(
                                                                            my_partner['recid']),
                                                                         'order_line_session_id': str(retVal['_id'])},
                                                                        {'valide': '1'}]}}, ],
                                                        'as': 'partner_order_line_collection'
                                                    }
                                                },
                                                {
                                                    '$unwind': '$partner_order_line_collection'
                                                }
                                                ]
                total_qty = 0
                tab_quotation_ref = []

                for local_New_retVal in MYSY_GV.dbname['partner_order_header'].aggregate(local_new_myquery_find_order):
                    # print(" ### local_New_retVal = ", local_New_retVal)
                    if ("partner_order_line_collection" in local_New_retVal.keys()):
                        if ("order_line_qty" in local_New_retVal['partner_order_line_collection'].keys() and
                                len(str(local_New_retVal['date_envoi_quotation'])) > 5):
                            total_qty = total_qty + mycommon.tryFloat(
                                local_New_retVal['partner_order_line_collection']['order_line_qty'])
                            tab_quotation_ref.append(str(local_New_retVal['order_header_ref_interne']))

                # print(" ### total_qty = ", total_qty)
                # print(" ### tab_quotation_ref = ", tab_quotation_ref)

                val['qty_in_quotation'] = str(total_qty)
                val['qty_in_quotation_list_quotation'] = ', '.join(tab_quotation_ref)

                RetObject.append(mycommon.JSONEncoder().encode(val))

        return True, RetObject


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de récupérer la liste des sessions de formation valides et actives."



"""
Cette fonction crée les sessions de formation en mass
par exemple avec l'import d'un fichier csv
"""
def Add_Update_SessionFormation_mass(file=None, Folder=None, diction=None):
    try:
        '''
               # Verification que les champs reçus dans l'API sont bien dans la liste des champs autorisés
               # Cela evite le cas ou une entité tierce ajouter les valeurs inconnu dans l'API
               # Ici on doit mettre tous les champs possible (obligatoire ou non) de la BDD dans la liste
               # field_list.
               '''
        field_list = ['token', 'class_internal_url']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + "  Le champ  '" + val + "' n'existe pas, Creation session annulée")
                return False, " Le champ  '" + val + "' n'existe pas, Creation session annulée "

        '''
        Une fois qu'on a controlé que toutes les clés mise dans l'API sont correcte. etape precedente,
        On controle que les champs obligatoires sont presents dans la liste 
        '''
        field_list_obligatoire = ['token', 'class_internal_url']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        # Verifier la validité du token
        retval = mycommon.check_partner_token_validity("", my_token)
        if retval is False:
            return "Err_Connexion", " La session de connexion n'est pas valide"


        class_internal_url = ""
        if ("class_internal_url" in diction.keys()):
            if diction['class_internal_url']:
                class_internal_url = diction['class_internal_url']

        if (len(str(class_internal_url).strip()) <= 0):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " Le code de la formation est vide : Impossible d'importer la liste des sessions ")
            return False, "Le code de la formation est vide. Impossible d'importer la liste des sessions"


        partner_recid = mycommon.get_parnter_recid_from_token(my_token)
        if (partner_recid is False):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " - partner_recid KO : Impossible d'importer la liste des sessions ")
            return False, "les information de connexion sont incorrectes. Impossible d'importer la liste des sessions"

        status, saved_file = mycommon.Upload_Save_CSV_File(file, Folder)
        if (status == False):
            return False, "Impossible d'importer la liste des sessions, le nom du fichier est incorrect "

        # " Lecture du fichier "
        # print(" Lecture du fichier : "+saved_file)
        nb_line = 0

        """"
        update du 31/08/23 : Controle de l'integrité du fichier avant import
        """
        local_controle_status, local_controle_message = Controle_Add_Update_SessionFormation_mass(saved_file,
                                                                                                           Folder,
                                                                                                           diction)

        if (local_controle_status is False):
            return local_controle_status, local_controle_message

        print(" #### local_controle_message = ", local_controle_message)


        df = pd.read_csv(saved_file, encoding='utf8', on_bad_lines='skip', sep=';', encoding_errors='ignore')
        df = df.fillna('')
        # Dictionnaire des champs utilisables
        '''
        # Verification que les noms des colonne sont bien corrects"
        '''
        field_list = ['date_debut', 'date_fin', 'nb_participant', 'adresse', 'code_postal', 'ville', 'pays',
                      'session_status', 'date_debut_inscription', 'date_fin_inscription', 'attestation', 'formateur',
                      'code_session', "distanciel", "presentiel", "prix_session", 'contenu_ftion', 'lms_class_code',
                      'session_ondemande', 'session_etape', 'formation_code_externe', 'formateur_email', 'titre', 'location_type', 'is_bpf']

        # Controle du nombre de lignes dans le fichier.
        total_rows = len(df)
        if (total_rows > MYSY_GV.MAX_PARTICIPANT_BY_CSV):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " Le fichier comporte plus de " + str(
                    MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes.")
            return False, " Le fichier comporte plus de " + str(MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes."

        # print(df.columns)
        for val in df.columns:
            if str(val).lower() not in field_list:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " : entete du fichier csv. '" + val + "' n'est pas acceptée")
                return False, " Entete du fichier csv. La Colonne '" + val + "' n'est pas acceptée"


        # Verification des champs obligatoires dans le fichier
        field_list_obligatoire_file = ['date_debut', 'date_fin', 'session_status',  'code_session', 'formation_code_externe']

        for val in field_list_obligatoire_file:
            if val not in df.columns:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " : Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire")
                return False, " Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire "


        # Recuperation des info de la formation.
        formation_data = MYSY_GV.dbname['myclass'].find_one({'internal_url': str(class_internal_url), 'valide':'1', 'locked':'0'})
        if (formation_data is None):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " La session de formation n'existe pas : Impossible d'importer la liste des participants ")
            return False, "la session de formation n'existe pas. Impossible d'importer la liste des participants"

        x = range(0, total_rows)
        ignored_line = ""
        nb_inserted_line = 0


        for n in x:
            mydata = {}

            nb_inserted_line = nb_inserted_line + 1
            # Si une ligne n'a aucune information obligatoire, alors on ignore la ligne
            if (str(df['code_session'].values[n]) == "nan" or str(df['session_status'].values[n]) == "nan" or
            str(df['date_debut'].values[n]) == "nan" or str(df['date_fin'].values[n]) == "nan" ):
                mycommon.myprint(str(
                    inspect.stack()[0][
                        3]) + " - La ligne " + str(n + 2) + " a été ignorée")
                ignored_line = str(n + 2) + " , " + str(ignored_line)

                nb_inserted_line = nb_inserted_line - 1
                continue

            external_code = ""
            if ("formation_code_externe" in df.keys()):
                if (str(df['formation_code_externe'].values[n])):
                    external_code = str(df['formation_code_externe'].values[n]).strip()

                    # On verifie l'existance de l'external code pour ce une des formations de ce partner
                    count_class = MYSY_GV.dbname['myclass'].count_documents(
                        {'external_code': str(external_code), 'valide': '1',
                         'locked': '0', 'partner_owner_recid': str(partner_recid)})

                    if (count_class < 0):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Ligne " + str( n + 2) + " : Le code_externe n'est pas valide.")
                        return False, " Ligne " + str( n + 2) + " : Le code_externe n'est pas valide."

                    if (count_class > 1):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Erreur : Ligne " + str(n + 2) + " : Le code_externe corresponds à plus d'une formation.")
                        return False, " Erreur : Ligne " + str(n + 2) + " : Le code_externe corresponds à plus d'une formation."

                    class_date = MYSY_GV.dbname['myclass'].find_one(
                        {'external_code': str(external_code), 'valide': '1',
                         'locked': '0', 'partner_owner_recid': str(partner_recid)})

                    if( class_date['external_code'] != formation_data['external_code']):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Erreur : Ligne " + str( n + 2) + " : Le code externe dans le fichier excel ne correpond pas au code externe de la formation sur la quelle vous etes")
                        return False, "  Erreur : Ligne " + str( n + 2) + " : Le code externe dans le fichier excel ne correpond pas au code externe de la formation sur la quelle vous etes"

            mydata['date_debut'] = str(df['date_debut'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_debut'])
            if(local_status is False ):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne "+str(n+2)+"."
                              "La date debut n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne "+str(n+2)+". La date debut n'est pas au format jj/mm/aaaa "

            mydata['date_fin'] = str(df['date_fin'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_fin'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(n+2) + "."
                                                                                                                     "La date date_fin n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n+1) + ". La date fin n'est pas au format jj/mm/aaaa "


            ## Verification de la cohérence des dates. Date_du doit <= Date_au
            if (datetime.strptime(str(mydata['date_debut']).strip(), '%d/%m/%Y') > datetime.strptime(
                    str(mydata['date_fin']).strip(), '%d/%m/%Y')):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer la session de formation : La date debut " + str(mydata['date_debut']) +
                    " est postérieure à la date de fin " + str(mydata['date_fin']) + " pour la ligne  "+str(n+2)+" ")

                return False, " Impossible de créer la session de formation : La date debut " + str(mydata['date_debut'])[0:10] +\
                       " est postérieure à la date de fin " + str(mydata['date_fin'])[0:10] + " pour la ligne  "+str(n+2)+" "


            local_nb_participants = "1"
            if ("nb_participant" in df.keys()):
                if (str(df['nb_participant'].values[n])):
                    local_nb_participants = str(df['nb_participant'].values[n]).strip()

            local_status, new_participants = mycommon.IsInt(local_nb_participants)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'nb_participant' de la ligne " + str(n+2) + " est incorrecte.")
                return False, " Le champ nb_participant de la ligne " + str(n+2) + " est incorrecte. "

            mydata['nb_participant'] = str(new_participants)

            prix_session = "0"
            if ("prix_session" in df.keys()):
                if (str(df['prix_session'].values[n])):
                    prix_session = str(df['prix_session'].values[n]).strip()

            local_status, new_prix_session = mycommon.IsFloat(prix_session)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'prix_session' de la ligne " + str(n+2) + " est incorrecte.")
                return False, " Le champ prix_session de la ligne " + str(n+2) + " est incorrecte. "

            mydata['prix_session'] = str(new_prix_session)

            local_code_session = ""
            if ("code_session" in df.keys()):
                if (str(df['code_session'].values[n])):
                    local_code_session = str(df['code_session'].values[n]).strip()

            if (len(str(local_code_session).strip()) < 2):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'code_session' de la ligne " + str(
                        n + 2) + " doit faire plus de deux caractères.")
                return False, " Le champ code_session de la ligne " + str(
                    n + 2) + " doit faire plus de deux caractères. "

            mydata['code_session'] = local_code_session

            local_adresse = ""
            if ("adresse" in df.keys()):
                if (str(df['adresse'].values[n])):
                    local_adresse = str(df['adresse'].values[n]).strip()
            mydata['adresse'] = local_adresse

            """ lms_class_code = ""
            if ("lms_class_code" in df.keys()):
                if (str(df['lms_class_code'].values[n])):
                    lms_class_code = str(df['lms_class_code'].values[n]).strip()
            mydata['lms_class_code'] = lms_class_code """


            session_ondemande = "0"
            if ("session_ondemande" in df.keys()):
                if (str(df['session_ondemande'].values[n])):
                    session_ondemande =  str(mycommon.tryInt(str(df['session_ondemande'].values[n]).strip()))

                    if( session_ondemande != "1" and session_ondemande != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'session_ondemande' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'session_ondemande' de la ligne " + str(n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"


            mydata['session_ondemande'] = session_ondemande



            local_code_postal = ""
            if ("code_postal" in df.keys()):
                if (str(df['code_postal'].values[n])):
                    local_code_postal = str(df['code_postal'].values[n]).strip()
                    if ("." in str(local_code_postal)):
                        # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                        local_code_postal = str(local_code_postal).split(".")[0]
                    elif ("." in str(local_code_postal)):
                        # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                        local_code_postal = str(local_code_postal).split(",")[0]
                    else:
                        local_code_postal = str(local_code_postal)


            mydata['code_postal'] = local_code_postal

            distanciel = ""
            if ("distanciel" in df.keys()):
                if (str(df['distanciel'].values[n])):
                    distanciel = str(df['distanciel'].values[n]).strip()
                    distanciel = str(mycommon.tryInt(distanciel))
                    if (distanciel != "1" and distanciel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'distanciel' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'distanciel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"



            mydata['distantiel'] = distanciel

            presentiel = ""
            if ("presentiel" in df.keys()):
                if (str(df['presentiel'].values[n])):
                    presentiel = str(df['presentiel'].values[n]).strip()
                    presentiel = str(mycommon.tryInt(presentiel))

                    if (presentiel != "1" and presentiel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'presentiel' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'presentiel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"

            mydata['presentiel'] = presentiel

            #mydata['code_postal'] = str(df['code_postal'].values[n]).strip()

            local_ville = ""
            if ("ville" in df.keys()):
                if (str(df['ville'].values[n])):
                    local_ville = str(df['ville'].values[n]).strip()
            mydata['ville'] = local_ville

            local_pays = ""
            if ("pays" in df.keys()):
                if (str(df['pays'].values[n])):
                    local_pays = str(df['pays'].values[n]).strip()
            mydata['pays'] = local_pays

            contenu_ftion = ""
            if ("contenu_ftion" in df.keys()):
                if (str(df['contenu_ftion'].values[n])):
                    contenu_ftion = str(df['contenu_ftion'].values[n]).strip()
            mydata['contenu_ftion'] = contenu_ftion

            #mydata['ville'] = str(df['ville'].values[n]).strip()

            local_attestation_certif = ""
            if ("attestation" in df.keys()):
                if (str(df['attestation'].values[n])):
                    local_attestation_certif = str(df['attestation'].values[n]).strip()
            mydata['attestation_certif'] = local_attestation_certif

            #mydata['attestation_certif'] = str(df['attestation'].values[n]).strip()


            session_status = ""
            if ("session_status" in df.keys()):
                if (str(df['session_status'].values[n])):
                    session_status = str(df['session_status'].values[n]).strip()
            session_status = str(mycommon.tryInt(session_status))
            mydata['session_status'] = session_status


            session_etape = ""
            if ("session_etape" in df.keys()):
                if (str(df['session_etape'].values[n])):
                    session_etape = str(df['session_etape'].values[n]).strip()

            mydata['session_etape'] = session_etape

            """ 
            Update du 11/08/23 :
            - le status va etre geré en 0 (desactivé) et 1 (activé)
            - mise en commentaire du code ci-dessous
            
            local_session = str(df['session_status'].values[n]).strip()

            if( str(local_session) != "0" and  str(local_session) != "1" and  str(local_session) != "2"):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2")
                return False, " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2"

            if( str(local_session) == "1"):
                mydata['session_status'] = str("true")
            """

            mydata['date_debut_inscription'] = str(df['date_debut_inscription'].values[n]).strip().split(" ")[0]
            if( str(mydata['date_debut_inscription']).strip() != ""):
                local_status = mycommon.CheckisDate(mydata['date_debut_inscription'])
                if (local_status is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(n+2) + "."
                                                                                                                         "La  date_debut_inscription n'est pas au format jj/mm/aaaa ")

                    return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n+2) + ". La date_debut_inscription n'est pas au format jj/mm/aaaa "


            mydata['date_fin_inscription'] = str(df['date_fin_inscription'].values[n]).strip().split(" ")[0]
            if (str(mydata['date_fin_inscription']).strip() != ""):
                local_status = mycommon.CheckisDate(mydata['date_fin_inscription'])
                if (local_status is False):
                    mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(n+2) + "."
                                                                                                                     "La date_fin_inscription n'est pas au format jj/mm/aaaa ")

                    return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n+1) + ". La date_fin_inscription n'est pas au format jj/mm/aaaa "

            ## Verification de la cohérence des dates. date_debut_inscription doit <= date_fin_inscription
            if( str(mydata['date_debut_inscription']).strip() != "" and str(mydata['date_fin_inscription']).strip() != "" ):
                if (datetime.strptime(str(mydata['date_debut_inscription']).strip(), '%d/%m/%Y') > datetime.strptime(
                        str(mydata['date_fin_inscription']).strip(), '%d/%m/%Y')):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " - Impossible de créer la session de formation : La date debut des inscriptions " + str(
                            mydata['date_debut_inscription']) +
                        " est postérieure à la date de fin des inscriptions " + str(mydata['date_fin_inscription']) + " pour la ligne  "+str(n+2)+" ")

                    return False, " Impossible de créer la session de formation : La date de fin des inscriptions est antérieure à la date de début des inscriptions : Ligne  "+str(n+2)+" "

            mydata['token'] = str(my_token)
            mydata['class_internal_url'] = str(class_internal_url)

            #print(" ### mydata ", mydata)

            diction_for_session_id = {}
            diction_for_session_id['date_du'] = str(mydata['date_debut']).split(" ")[0]
            diction_for_session_id['date_au'] = str(mydata['date_fin']).split(" ")[0]
            diction_for_session_id['code_postal'] = str(mydata['code_postal'] )
            diction_for_session_id['adresse'] = str(mydata['adresse'])

            """
                       Verifier si la session existe deja en base, si c'est le cas récupérer le '_id'
                       la clé pour verifier l'existance d'une session est : 
                       - code_session
                       - class_internal_url
                       - partner_owner_recid
                       """

            existing_session = MYSY_GV.dbname['session_formation'].find_one(
                {'code_session': str(local_code_session), 'class_internal_url': str(class_internal_url),
                 'partner_owner_recid': str(partner_recid), 'valide': '1'})

            if (existing_session is not None):
                if ('_id' in existing_session.keys()):
                    mydata['session_id'] = existing_session['_id']

            clean_dict = {k: mydata[k] for k in mydata if (str(mydata[k]) != "nan")}

            #print("####  Add_Update_SessionFormation_mass : clean_dict ", clean_dict)
            status, retval = Add_Update_SessionFormation(clean_dict)

            if (status is False):
                return status, retval


        print(str(total_rows) + " sessions ont été inserées")

        message_ignored_line = ""
        if (ignored_line):
            message_ignored_line = "  ATTENTION -  Les lignes [" + str(
                ignored_line) + "] ont été ignorées. car les toutes informations obligatoires ne sont pas fournies"

        return True, str(nb_inserted_line) + " sessions ont été inserées / Mises à jour. " + str(message_ignored_line)


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible d'importer les sessions de formation  en masse "

"""
Cette fonction permet de faire des controles du fichier excel avant l'import.
cela permet d'eviter des imports partiels.
c'est soit le fichier est TOUT bon ou pas.
"""
def Controle_Add_Update_SessionFormation_mass(saved_file=None, Folder=None, diction=None):
    try:
        '''
               # Verification que les champs reçus dans l'API sont bien dans la liste des champs autorisés
               # Cela evite le cas ou une entité tierce ajouter les valeurs inconnu dans l'API
               # Ici on doit mettre tous les champs possible (obligatoire ou non) de la BDD dans la liste
               # field_list.
               '''
        field_list = ['token', 'class_internal_url']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + "  Le champ  '" + val + "' n'existe pas, Creation session annulée")
                return False, " Le champ  '" + val + "' n'existe pas, Creation session annulée "

        '''
        Une fois qu'on a controlé que toutes les clés mise dans l'API sont correcte. etape precedente,
        On controle que les champs obligatoires sont presents dans la liste 
        '''
        field_list_obligatoire = ['token', 'class_internal_url']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        # Verifier la validité du token
        retval = mycommon.check_partner_token_validity("", my_token)
        if retval is False:
            return "Err_Connexion", " La session de connexion n'est pas valide"

        class_internal_url = ""
        if ("class_internal_url" in diction.keys()):
            if diction['class_internal_url']:
                class_internal_url = diction['class_internal_url']

        if (len(str(class_internal_url).strip()) <= 0):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " Le code de la formation est vide : Impossible d'importer la liste des sessions ")
            return False, "Le code de la formation est vide. Impossible d'importer la liste des sessions"


        partner_recid = mycommon.get_parnter_recid_from_token(my_token)
        if (partner_recid is False):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " - partner_recid KO : Impossible d'importer la liste des sessions ")
            return False, "les information de connexion sont incorrectes. Impossible d'importer la liste des sessions"

        nb_line = 0

        df = pd.read_csv(saved_file, encoding='utf8', on_bad_lines='skip', sep=';', encoding_errors='ignore')
        df = df.fillna('')
        # Dictionnaire des champs utilisables
        '''
        # Verification que les noms des colonne sont bien corrects"
        '''
        field_list = ['date_debut', 'date_fin', 'nb_participant', 'adresse', 'code_postal', 'ville', 'pays',
                      'session_status', 'date_debut_inscription', 'date_fin_inscription', 'attestation',
                      'code_session', "distanciel", "presentiel", "prix_session", 'contenu_ftion', 'lms_class_code',
                      'session_ondemande', 'session_etape', 'formation_code_externe','formateur_email', 'titre', 'location_type', 'is_bpf']

        # Controle du nombre de lignes dans le fichier.
        total_rows = len(df)
        if (total_rows > MYSY_GV.MAX_PARTICIPANT_BY_CSV):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " Le fichier comporte plus de " + str(
                    MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes.")
            return False, " Le fichier comporte plus de " + str(MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes."

        # print(df.columns)
        for val in df.columns:
            if str(val).lower() not in field_list:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " : entete du fichier csv. '" + val + "' n'est pas acceptée")
                return False, " Entete du fichier csv. La Colonne '" + val + "' n'est pas acceptée"

        # Verification des champs obligatoires dans le fichier
        field_list_obligatoire_file = ['date_debut', 'date_fin', 'session_status', 'code_session',
                                       'formation_code_externe']

        for val in field_list_obligatoire_file:
            if val not in df.columns:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " : Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire")
                return False, " Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire "

        # Recuperation des info de la formation.
        formation_data = MYSY_GV.dbname['myclass'].find_one(
            {'internal_url': str(class_internal_url), 'valide': '1', 'locked': '0'})
        if (formation_data is None):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " La session de formation n'existe pas : Impossible d'importer la liste des participants ")
            return False, "la session de formation n'existe pas. Impossible d'importer la liste des participants"

        x = range(0, total_rows)
        ignored_line = ""
        nb_inserted_line = 0

        for n in x:
            mydata = {}

            nb_inserted_line = nb_inserted_line + 1
            # Si une ligne n'a aucune information obligatoire, alors on ignore la ligne
            if (str(df['code_session'].values[n]) == "nan" or str(df['session_status'].values[n]) == "nan" or
                    str(df['date_debut'].values[n]) == "nan" or str(df['date_fin'].values[n]) == "nan"):
                mycommon.myprint(str(
                    inspect.stack()[0][
                        3]) + " - La ligne " + str(n + 2) + " a été ignorée")
                ignored_line = str(n + 2) + " , " + str(ignored_line)

                nb_inserted_line = nb_inserted_line - 1
                continue

            external_code = ""
            if ("formation_code_externe" in df.keys()):
                if (str(df['formation_code_externe'].values[n])):
                    external_code = str(df['formation_code_externe'].values[n]).strip()

                    # On verifie l'existance de l'external code pour ce une des formations de ce partner
                    count_class = MYSY_GV.dbname['myclass'].count_documents(
                        {'external_code': str(external_code), 'valide': '1',
                         'locked': '0', 'partner_owner_recid': str(partner_recid)})

                    if (count_class < 0):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Ligne " + str(
                                n + 2) + " : Le code_externe n'est pas valide.")
                        return False, " Ligne " + str(n + 2) + " : Le code_externe n'est pas valide."

                    if (count_class > 1):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Erreur : Ligne " + str(
                                n + 2) + " : Le code_externe corresponds à plus d'une formation.")
                        return False, " Erreur : Ligne " + str(
                            n + 2) + " : Le code_externe corresponds à plus d'une formation."

                    class_date = MYSY_GV.dbname['myclass'].find_one(
                        {'external_code': str(external_code), 'valide': '1',
                         'locked': '0', 'partner_owner_recid': str(partner_recid)})

                    if (class_date['external_code'] != formation_data['external_code']):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Erreur : Ligne " + str(
                                n + 2) + " : Le code externe dans le fichier excel ne correpond pas au code externe de la formation sur la quelle vous etes")
                        return False, "  Erreur : Ligne " + str(
                            n + 2) + " : Le code externe dans le fichier excel ne correpond pas au code externe de la formation sur la quelle vous etes"

            mydata['date_debut'] = str(df['date_debut'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_debut'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date debut n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 2) + ". La date debut n'est pas au format jj/mm/aaaa "

            mydata['date_fin'] = str(df['date_fin'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_fin'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date date_fin n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 1) + ". La date fin n'est pas au format jj/mm/aaaa "

            # Verifier que l'adresse email du formateur est valide
            formateur_email = ""
            formateur_id = ""
            if ("formateur_email" in df.keys()):
                if (str(df['formateur_email'].values[n]) and str(df['formateur_email'].values[n]) != ""):

                    formateur_email = str(df['formateur_email'].values[n]).strip()
                    if (mycommon.isEmailValide(formateur_email) is False):
                        mycommon.myprint(str(
                            inspect.stack()[0][3]) + " - L'email du formateur  '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide.")
                        return False, " L'email du formateur '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide."

                    is_formateur_email_ok = MYSY_GV.dbname['ressource_humaine'].count_documents(
                        {'email': formateur_email,
                         'valide': '1',
                         'locked': '0',
                         'partner_recid': str(
                             partner_recid)})
                    if (is_formateur_email_ok <= 0):
                        mycommon.myprint(str(
                            inspect.stack()[0][3]) + " - L'email du formateur  '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide (2).")
                        return False, " L'email du formateur '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide (2)."

                    formateur_data = MYSY_GV.dbname['ressource_humaine'].find_one({'email': formateur_email,
                                                                                   'valide': '1',
                                                                                   'locked': '0',
                                                                                   'partner_recid': str(
                                                                                       partner_recid)})

                    formateur_id = str(formateur_data['_id'])

            mydata['formateur_id'] = formateur_id

            if ("titre" in df.keys()):
                mydata['titre'] =  str(df['titre'].values[n]).strip()

            if ("location_type" in df.keys()):
                mydata['location_type'] = str(df['location_type'].values[n]).strip().lower()
                if (str(df['location_type'].values[n]).strip().lower() not in MYSY_GV.TRAINING_LOCATION_TYPE):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " - Le champ 'location_type' est incorrecte.")
                    return False, "Le champ 'location_type' est incorrect."

            if ("is_bpf" in df.keys()):
                if (str(df['is_bpf'].values[n]).strip() not in ['0', '1']):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " - Le champ 'bpf' est incorrect.  Les valeurs acceptées sont : '1' ou '0' ")
                    return False, "Le champ 'bpf' est incorrect.  Les valeurs acceptées sont : '1' ou '0' "

                mydata['is_bpf'] = str(df['is_bpf'].values[n]).strip()


            ## Verification de la cohérence des dates. Date_du doit <= Date_au
            if (datetime.strptime(str(mydata['date_debut']).strip(), '%d/%m/%Y') > datetime.strptime(
                    str(mydata['date_fin']).strip(), '%d/%m/%Y')):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer la session de formation : La date debut " + str(
                        mydata['date_debut']) +
                    " est postérieure à la date de fin " + str(mydata['date_fin']) + " pour la ligne  " + str(
                        n + 2) + " ")

                return False, " Impossible de créer la session de formation : La date debut " + str(
                    mydata['date_debut'])[0:10] + \
                       " est postérieure à la date de fin " + str(mydata['date_fin'])[0:10] + " pour la ligne  " + str(
                    n + 2) + " "

            local_nb_participants = "1"
            if ("nb_participant" in df.keys()):
                if (str(df['nb_participant'].values[n])):
                    local_nb_participants = str(df['nb_participant'].values[n]).strip()

            local_status, new_participants = mycommon.IsInt(local_nb_participants)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'nb_participant' de la ligne " + str(
                        n + 2) + " est incorrecte.")
                return False, " Le champ nb_participant de la ligne " + str(n + 2) + " est incorrecte. Un nombre Entier Positif doit être fourni "

            mydata['nb_participant'] = str(new_participants)

            prix_session = "0"
            if ("prix_session" in df.keys()):
                if (str(df['prix_session'].values[n])):
                    prix_session = str(df['prix_session'].values[n]).strip()

            local_status, new_prix_session = mycommon.IsFloat(prix_session)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'prix_session' de la ligne " + str(
                        n + 2) + " est incorrecte.")
                return False, " Le champ prix_session de la ligne " + str(n + 2) + " est incorrecte. "

            mydata['prix_session'] = str(new_prix_session)

            local_code_session = ""
            if ("code_session" in df.keys()):
                if (str(df['code_session'].values[n])):
                    local_code_session = str(df['code_session'].values[n]).strip()
                    
            if(len(str(local_code_session).strip()) < 2 ):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'code_session' de la ligne " + str(
                        n + 2) + " doit faire plus de deux caractères.")
                return False, " Le champ code_session de la ligne " + str(n + 2) + " doit faire plus de deux caractères. "
                
            mydata['code_session'] = local_code_session

            local_adresse = ""
            if ("adresse" in df.keys()):
                if (str(df['adresse'].values[n])):
                    local_adresse = str(df['adresse'].values[n]).strip()
            mydata['adresse'] = local_adresse


            session_ondemande = "0"
            if ("session_ondemande" in df.keys()):
                if (str(df['session_ondemande'].values[n])):
                    session_ondemande = str(df['session_ondemande'].values[n]).strip()
                    session_ondemande = str(mycommon.tryInt(str(session_ondemande)))

                    if (session_ondemande != "1" and session_ondemande != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'session_ondemande' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'session_ondemande' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"

            mydata['session_ondemande'] = session_ondemande

            local_code_postal = ""
            if ("code_postal" in df.keys()):
                if (str(df['code_postal'].values[n])):
                    local_code_postal = str(df['code_postal'].values[n]).strip()

                    if ("." in str(local_code_postal)):
                        # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                        local_code_postal = str(local_code_postal).split(".")[0]
                    elif ("." in str(local_code_postal)):
                        # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                        local_code_postal = str(local_code_postal).split(",")[0]
                    else:
                        local_code_postal = str(local_code_postal)


            mydata['code_postal'] = local_code_postal

            distanciel = ""
            if ("distanciel" in df.keys()):
                if (str(df['distanciel'].values[n])):
                    distanciel = str(df['distanciel'].values[n]).strip()
                    distanciel = str(mycommon.tryInt(distanciel))

                    if (distanciel != "1" and distanciel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'distanciel' de la ligne " + str(
                                n + 2) + " est incorrecte. La valeur fournie est "+str(distanciel) )
                        return False, " Le champ 'distanciel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0'. La valeur fournie est "+str(distanciel)

            mydata['distantiel'] = distanciel

            presentiel = ""
            if ("presentiel" in df.keys()):
                if (str(df['presentiel'].values[n])):
                    presentiel = str(df['presentiel'].values[n]).strip()
                    presentiel = str(mycommon.tryInt(presentiel))

                    if (presentiel != "1" and presentiel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'presentiel' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'presentiel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"

            mydata['presentiel'] = presentiel

            # mydata['code_postal'] = str(df['code_postal'].values[n]).strip()

            local_ville = ""
            if ("ville" in df.keys()):
                if (str(df['ville'].values[n])):
                    local_ville = str(df['ville'].values[n]).strip()
            mydata['ville'] = local_ville

            local_pays = ""
            if ("pays" in df.keys()):
                if (str(df['pays'].values[n])):
                    local_pays = str(df['pays'].values[n]).strip()
            mydata['pays'] = local_pays

            contenu_ftion = ""
            if ("contenu_ftion" in df.keys()):
                if (str(df['contenu_ftion'].values[n])):
                    contenu_ftion = str(df['contenu_ftion'].values[n]).strip()
            mydata['contenu_ftion'] = contenu_ftion

            # mydata['ville'] = str(df['ville'].values[n]).strip()

            local_attestation_certif = ""
            if ("attestation" in df.keys()):
                if (str(df['attestation'].values[n])):
                    local_attestation_certif = str(df['attestation'].values[n]).strip()
            mydata['attestation_certif'] = local_attestation_certif

            # mydata['attestation_certif'] = str(df['attestation'].values[n]).strip()


            session_status = ""
            if ("session_status" in df.keys()):
                if (str(df['session_status'].values[n])):
                    session_status = str(df['session_status'].values[n]).strip()
            mydata['session_status'] = session_status

            session_etape = ""
            if ("session_etape" in df.keys()):
                if (str(df['session_etape'].values[n])):
                    session_etape = str(df['session_etape'].values[n]).strip()
            mydata['session_etape'] = session_etape

            """ 
            Update du 11/08/23 :
            - le status va etre geré en 0 (desactivé) et 1 (activé)
            - mise en commentaire du code ci-dessous

            local_session = str(df['session_status'].values[n]).strip()

            if( str(local_session) != "0" and  str(local_session) != "1" and  str(local_session) != "2"):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2")
                return False, " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2"

            if( str(local_session) == "1"):
                mydata['session_status'] = str("true")
            """

            mydata['date_debut_inscription'] = str(df['date_debut_inscription'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_debut_inscription'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La  date_debut_inscription n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 2) + ". La date_debut_inscription n'est pas au format jj/mm/aaaa "

            mydata['date_fin_inscription'] = str(df['date_fin_inscription'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_fin_inscription'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date_fin_inscription n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 1) + ". La date_fin_inscription n'est pas au format jj/mm/aaaa "

            ## Verification de la cohérence des dates. Date_du doit <= Date_au
            if (datetime.strptime(str(mydata['date_debut_inscription']).strip(), '%d/%m/%Y') > datetime.strptime(
                    str(mydata['date_fin_inscription']).strip(), '%d/%m/%Y')):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer la session de formation : La date debut des inscriptions " + str(
                        mydata['date_debut_inscription'])[0:10] +
                    " est postérieure à la date de fin des inscriptions " + str(
                        mydata['date_fin_inscription'])[0:10] + " pour la ligne  " + str(n + 2) + " ")

                return False, " Impossible de créer la session de formation : La date de fin des inscriptions est antérieure à la date de début des inscriptions : Ligne  " + str(
                    n + 2) + " "

            mydata['token'] = str(my_token)
            mydata['class_internal_url'] = str(class_internal_url)

            # print(" ### mydata ", mydata)

            diction_for_session_id = {}
            diction_for_session_id['date_du'] = str(mydata['date_debut']).split(" ")[0]
            diction_for_session_id['date_au'] = str(mydata['date_fin']).split(" ")[0]
            diction_for_session_id['code_postal'] = str(mydata['code_postal'])
            diction_for_session_id['adresse'] = str(mydata['adresse'])





        return True, str(total_rows)+" sessions dans le fichier"


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de controler le fichier des sessions de formation  en masse "


"""
import des sessions de formation en masse
pour plusieurs formation.

Cela veut dire qu'on fourni 'external_code' pour chaque ligne du fichier excel, mais on ne fourni pas de 'internal_url'
"""


def Add_Update_SessionFormation_mass_for_many_class(file=None, Folder=None, diction=None):
    try:
        '''
               # Verification que les champs reçus dans l'API sont bien dans la liste des champs autorisés
               # Cela evite le cas ou une entité tierce ajouter les valeurs inconnu dans l'API
               # Ici on doit mettre tous les champs possible (obligatoire ou non) de la BDD dans la liste
               # field_list.
               '''
        field_list = ['token', ]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + "  Le champ  '" + val + "' n'existe pas, Creation session annulée")
                return False, " Le champ  '" + val + "' n'existe pas, Creation session annulée "

        '''
        Une fois qu'on a controlé que toutes les clés mise dans l'API sont correcte. etape precedente,
        On controle que les champs obligatoires sont presents dans la liste 
        '''
        field_list_obligatoire = ['token', ]

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        # Verifier la validité du token
        retval = mycommon.check_partner_token_validity("", my_token)
        if retval is False:
            return "Err_Connexion", " La session de connexion n'est pas valide"

        # Verifier la validité du token
        retval = mycommon.check_partner_token_validity("", my_token)

        if retval is False:
            return "Err_Connexion", " La session de connexion n'est pas valide"

        partner_recid = mycommon.get_parnter_recid_from_token(my_token)
        if (partner_recid is False):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " - partner_recid KO : Impossible d'importer la liste des sessions ")
            return False, "les information de connexion sont incorrectes. Impossible d'importer la liste des sessions"

        status, saved_file = mycommon.Upload_Save_CSV_File(file, Folder)
        if (status == False):
            return False, "Impossible d'importer la liste des sessions, le nom du fichier est incorrect "

        # " Lecture du fichier "
        # print(" Lecture du fichier : "+saved_file)
        nb_line = 0

        """"
                update du 31/08/23 : Controle de l'integrité du fichier avant import
                """
        local_controle_status, local_controle_message = Controle_Add_Update_SessionFormation_mass_for_many_class(saved_file,
                                                                                                  Folder,
                                                                                                  diction)


        if (local_controle_status is False):
            return local_controle_status, local_controle_message

        print(" #### local_controle_message = ", local_controle_message)


        df = pd.read_csv(saved_file, encoding='utf8', on_bad_lines='skip', sep=';', encoding_errors='ignore')
        df = df.fillna('')
        # Dictionnaire des champs utilisables
        '''
        # Verification que les noms des colonne sont bien corrects"
        '''
        field_list = ['date_debut', 'date_fin', 'nb_participant', 'adresse', 'code_postal', 'ville', 'pays',
                      'session_status', 'date_debut_inscription', 'date_fin_inscription', 'attestation',
                      'code_session', "distanciel", "presentiel", "prix_session", 'contenu_ftion', 'lms_class_code',
                      'session_ondemande', 'session_etape', 'formation_code_externe', 'formateur_email', 'titre', 'location_type', 'is_bpf']

        # Controle du nombre de lignes dans le fichier.
        total_rows = len(df)
        if (total_rows > MYSY_GV.MAX_PARTICIPANT_BY_CSV):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " Le fichier comporte plus de " + str(
                    MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes.")
            return False, " Le fichier comporte plus de " + str(MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes."

        # print(df.columns)
        for val in df.columns:
            if str(val).lower() not in field_list:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " : entete du fichier csv. '" + val + "' n'est pas acceptée")
                return False, " Entete du fichier csv. La Colonne '" + val + "' n'est pas acceptée"

        # Verification des champs obligatoires dans le fichier
        field_list_obligatoire_file = ['date_debut', 'date_fin', 'session_status', 'code_session', 'formation_code_externe']

        for val in field_list_obligatoire_file:
            if val not in df.columns:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " : Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire")
                return False, " Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire "



        x = range(0, total_rows)
        ignored_line = ""
        nb_inserted_line = 0


        for n in x:
            mydata = {}

            nb_inserted_line = nb_inserted_line + 1
            # Si une ligne n'a aucune information obligatoire, alors on ignore la ligne
            if (str(df['code_session'].values[n]) == "nan" or str(df['session_status'].values[n]) == "nan" or
                    str(df['date_debut'].values[n]) == "nan" or str(df['date_fin'].values[n]) == "nan"):
                mycommon.myprint(str(
                    inspect.stack()[0][
                        3]) + " - La ligne " + str(n + 2) + " a été ignorée")
                ignored_line = str(n + 2) + " , " + str(ignored_line)

                nb_inserted_line = nb_inserted_line - 1
                continue

            mydata['date_debut'] = str(df['date_debut'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_debut'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date debut n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 2) + ". La date debut n'est pas au format jj/mm/aaaa "

            mydata['date_fin'] = str(df['date_fin'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_fin'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date date_fin n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 1) + ". La date fin n'est pas au format jj/mm/aaaa "

            # Verifier que l'adresse email du formateur est valide
            formateur_email = ""
            formateur_id = ""
            if ("formateur_email" in df.keys()):
                if (str(df['formateur_email'].values[n]) and str(df['formateur_email'].values[n]) != ""):

                    formateur_email = str(df['formateur_email'].values[n]).strip()
                    if (mycommon.isEmailValide(formateur_email) is False):
                        mycommon.myprint(str(
                            inspect.stack()[0][3]) + " - L'email du formateur  '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide.")
                        return False, " L'email du formateur '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide."

                    is_formateur_email_ok = MYSY_GV.dbname['ressource_humaine'].count_documents(
                        {'email': formateur_email,
                         'valide': '1',
                         'locked': '0',
                         'partner_recid': str(
                             partner_recid)})
                    if (is_formateur_email_ok <= 0):
                        mycommon.myprint(str(
                            inspect.stack()[0][3]) + " - L'email du formateur  '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide (2).")
                        return False, " L'email du formateur '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide (2)."

                    formateur_data = MYSY_GV.dbname['ressource_humaine'].find_one({'email': formateur_email,
                                                                                   'valide': '1',
                                                                                   'locked': '0',
                                                                                   'partner_recid': str(
                                                                                       partner_recid)})

                    formateur_id = str(formateur_data['_id'])

            mydata['formateur_id'] = formateur_id

            if ("titre" in df.keys()):
                mydata['titre'] = str(df['titre'].values[n]).strip()

            if ("location_type" in df.keys()):
                mydata['location_type'] = str(df['location_type'].values[n]).strip().lower()
                if (str(df['location_type'].values[n]).strip().lower() not in MYSY_GV.TRAINING_LOCATION_TYPE):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " - Le champ 'location_type' est incorrecte.")
                    return False, "Le champ 'location_type' est incorrect."

            if ("is_bpf" in df.keys()):
                if (str(df['is_bpf'].values[n]).strip() not in ['0', '1']):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " - Le champ 'bpf' est incorrect.  Les valeurs acceptées sont : '1' ou '0' ")
                    return False, "Le champ 'bpf' est incorrect.  Les valeurs acceptées sont : '1' ou '0' "

                mydata['is_bpf'] =  str(df['is_bpf'].values[n]).strip()




            ## Verification de la cohérence des dates. Date_du doit <= Date_au
            if (datetime.strptime(str(mydata['date_debut']).strip(), '%d/%m/%Y') > datetime.strptime(
                    str(mydata['date_fin']).strip(), '%d/%m/%Y')):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer la session de formation : La date debut " + str(
                        mydata['date_debut']) +
                    " est postérieure à la date de fin " + str(mydata['date_fin']) + " pour la ligne  " + str(
                        n + 2) + " ")

                return False, " Impossible de créer la session de formation : La date debut " + str(
                    mydata['date_debut'])[0:10] + \
                       " est postérieure à la date de fin " + str(mydata['date_fin'])[0:10] + " pour la ligne  " + str(
                    n + 2) + " "

            local_nb_participants = "1"
            if ("nb_participant" in df.keys()):
                if (str(df['nb_participant'].values[n])):
                    local_nb_participants = str(df['nb_participant'].values[n]).strip()

            local_status, new_participants = mycommon.IsInt(local_nb_participants)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'nb_participant' de la ligne " + str(
                        n + 2) + " est incorrecte.")
                return False, " Le champ nb_participant de la ligne " + str(n + 2) + " est incorrecte. "

            mydata['nb_participant'] = str(new_participants)

            prix_session = "0"
            if ("prix_session" in df.keys()):
                if (str(df['prix_session'].values[n])):
                    prix_session = str(df['prix_session'].values[n]).strip()

            local_status, new_prix_session = mycommon.IsFloat(prix_session)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'prix_session' de la ligne " + str(
                        n + 2) + " est incorrecte.")
                return False, " Le champ prix_session de la ligne " + str(n + 2) + " est incorrecte. "

            mydata['prix_session'] = str(new_prix_session)

            class_internal_url = ""

            external_code = ""
            if ("formation_code_externe" in df.keys()):
                if (str(df['formation_code_externe'].values[n])):
                    external_code = str(df['formation_code_externe'].values[n]).strip()

                    #On verifie l'existance de l'external code pour ce une des formations de ce partner
                    count_class =  MYSY_GV.dbname['myclass'].count_documents({'external_code':str(external_code), 'valide':'1',
                                                                               'locked':'0', 'partner_owner_recid':str(partner_recid)})


                    if( count_class != 1 ):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'code_externe' de la ligne " + str(
                                n + 2) + " ne correspond pas à une formation valide.")
                        return False, " Le champ 'code_externe' de la ligne " + str(
                            n + 2) + " ne correspond pas à une formation valide."

                    class_from_external_code = MYSY_GV.dbname['myclass'].find({'external_code':str(external_code), 'valide':'1',
                                                                               'locked':'0', 'partner_owner_recid':str(partner_recid)})

                    if(class_from_external_code is None ):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'code_exyer' de la ligne " + str(
                                n + 2) + " ne correspond pas à une formation valide (2).")
                        return False, " Le champ 'code_exyer' de la ligne " + str( n + 2) + " ne correspond pas à une formation valide (2)."


                    class_internal_url = str(class_from_external_code[0]['internal_url'])


            local_code_session = ""
            if ("code_session" in df.keys()):
                if (str(df['code_session'].values[n])):
                    local_code_session = str(df['code_session'].values[n]).strip()

            if (len(str(local_code_session).strip()) < 2):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'code_session' de la ligne " + str(
                        n + 2) + " doit faire plus de deux caractères.")
                return False, " Le champ code_session de la ligne " + str(
                    n + 2) + " doit faire plus de deux caractères. "
            mydata['code_session'] = local_code_session

            local_adresse = ""
            if ("adresse" in df.keys()):
                if (str(df['adresse'].values[n])):
                    local_adresse = str(df['adresse'].values[n]).strip()
            mydata['adresse'] = local_adresse




            session_ondemande = ""
            if ("session_ondemande" in df.keys()):
                if (str(df['session_ondemande'].values[n])):
                    session_ondemande = str(df['session_ondemande'].values[n]).strip()

                    if (session_ondemande != "1" and session_ondemande != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'session_ondemande' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'session_ondemande' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"

            mydata['session_ondemande'] = session_ondemande

            local_code_postal = ""
            if ("code_postal" in df.keys()):
                if (str(df['code_postal'].values[n])):
                    local_code_postal = str(df['code_postal'].values[n]).strip()

            if ("." in str(local_code_postal)):
                # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                local_code_postal = str(local_code_postal).split(".")[0]
            elif ("." in str(local_code_postal)):
                # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                local_code_postal = str(local_code_postal).split(",")[0]
            else:
                local_code_postal = str(local_code_postal)

            mydata['code_postal'] = local_code_postal

            distanciel = ""
            if ("distanciel" in df.keys()):
                if (str(df['distanciel'].values[n])):
                    distanciel = str(df['distanciel'].values[n]).strip()
                    distanciel = str(mycommon.tryInt(distanciel))
                    if (distanciel != "1" and distanciel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'distanciel' de la ligne " + str(
                                n + 2) + " est incorrecte. Valeur reçue : "+str(distanciel))
                        return False, " Le champ 'distanciel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide. Valeur reçue : "+str(distanciel)

            mydata['distantiel'] = distanciel

            presentiel = ""
            if ("presentiel" in df.keys()):
                if (str(df['presentiel'].values[n])):
                    presentiel = str(df['presentiel'].values[n]).strip()
                    presentiel = str(mycommon.tryInt(presentiel))
                    if (presentiel != "1" and presentiel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'presentiel' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'presentiel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"

            mydata['presentiel'] = presentiel

            # mydata['code_postal'] = str(df['code_postal'].values[n]).strip()

            local_ville = ""
            if ("ville" in df.keys()):
                if (str(df['ville'].values[n])):
                    local_ville = str(df['ville'].values[n]).strip()
            mydata['ville'] = local_ville

            pays = ""
            if ("pays" in df.keys()):
                if (str(df['pays'].values[n])):
                    pays = str(df['pays'].values[n]).strip()
            mydata['pays'] = pays

            contenu_ftion = ""
            if ("contenu_ftion" in df.keys()):
                if (str(df['contenu_ftion'].values[n])):
                    contenu_ftion = str(df['contenu_ftion'].values[n]).strip()
            mydata['contenu_ftion'] = contenu_ftion

            # mydata['ville'] = str(df['ville'].values[n]).strip()
            """ 
            local_attestation_certif = ""
            if ("attestation" in df.keys()):
                if (str(df['attestation'].values[n])):
                    local_attestation_certif = str(df['attestation'].values[n]).strip()
            mydata['attestation_certif'] = local_attestation_certif
            
            # mydata['attestation_certif'] = str(df['attestation'].values[n]).strip()
            """

            session_status = ""
            if ("session_status" in df.keys()):
                if (str(df['session_status'].values[n])):
                    session_status = str(df['session_status'].values[n]).strip()

            session_status = str(mycommon.tryInt(session_status))
            mydata['session_status'] = session_status

            session_etape = ""
            if ("session_etape" in df.keys()):
                if (str(df['session_etape'].values[n])):
                    session_etape = str(df['session_etape'].values[n]).strip()


            mydata['session_etape'] = session_etape

            """ 
            Update du 11/08/23 :
            - le status va etre geré en 0 (desactivé) et 1 (activé)
            - mise en commentaire du code ci-dessous

            local_session = str(df['session_status'].values[n]).strip()

            if( str(local_session) != "0" and  str(local_session) != "1" and  str(local_session) != "2"):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2")
                return False, " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2"

            if( str(local_session) == "1"):
                mydata['session_status'] = str("true")
            """

            mydata['date_debut_inscription'] = str(df['date_debut_inscription'].values[n]).strip().split(" ")[0]
            if( str(mydata['date_debut_inscription']).strip() != ""):
                local_status = mycommon.CheckisDate(mydata['date_debut_inscription'])
                if (local_status is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                            n + 2) + "."
                                     "La  date_debut_inscription n'est pas au format jj/mm/aaaa ")

                    return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + ". La date_debut_inscription n'est pas au format jj/mm/aaaa "

            mydata['date_fin_inscription'] = str(df['date_fin_inscription'].values[n]).strip().split(" ")[0]
            if (str(mydata['date_fin_inscription']).strip() != ""):
                local_status = mycommon.CheckisDate(mydata['date_fin_inscription'])
                if (local_status is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                            n + 2) + "."
                                     "La date_fin_inscription n'est pas au format jj/mm/aaaa ")

                    return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 1) + ". La date_fin_inscription n'est pas au format jj/mm/aaaa "

            ## Verification de la cohérence des dates. Date_du doit <= Date_au
            if (str(mydata['date_debut_inscription']).strip() != "" and str(mydata['date_fin_inscription']).strip() != ""):
                if (datetime.strptime(str(mydata['date_debut_inscription']).strip(), '%d/%m/%Y') > datetime.strptime(
                        str(mydata['date_fin_inscription']).strip(), '%d/%m/%Y')):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " - Impossible de créer la session de formation : La date debut des inscriptions " + str(
                            mydata['date_debut_inscription']) +
                        " est postérieure à la date de fin des inscriptions " + str(
                            mydata['date_fin_inscription']) + " pour la ligne  " + str(n + 2) + " ")

                    return False, " Impossible de créer la session de formation : La date de fin des inscriptions est antérieure à la date de début des inscriptions : Ligne  " + str(
                    n + 2) + " "

            mydata['token'] = str(my_token)
            mydata['class_internal_url'] = str(class_internal_url)

            #print(" ### mydata ", mydata)

            diction_for_session_id = {}
            diction_for_session_id['date_du'] = str(mydata['date_debut']).split(" ")[0]
            diction_for_session_id['date_au'] = str(mydata['date_fin']).split(" ")[0]
            diction_for_session_id['code_postal'] = str(mydata['code_postal'])
            diction_for_session_id['adresse'] = str(mydata['adresse'])

            """
            Verifier si la session existe deja en base, si c'est le cas récupérer le '_id'
            la clé pour verifier l'existance d'une session est : 
            - code_session
            - class_internal_url
            - partner_owner_recid
            """

            existing_session = MYSY_GV.dbname['session_formation'].find_one({'code_session':str(local_code_session), 'class_internal_url':str(class_internal_url),
                                                                             'partner_owner_recid':str(partner_recid), 'valide':'1'})

            if( existing_session is not None):
                if( '_id' in existing_session.keys()):
                    mydata['session_id'] = existing_session['_id']

            clean_dict = {k: mydata[k] for k in mydata if (str(mydata[k]) != "nan")}

            print("####  Add_Update_SessionFormation_mass_for_many_class : clean_dict ", clean_dict)
            status, retval = Add_Update_SessionFormation(clean_dict)

            if (status is False):
                return status, retval

        print(str(total_rows) + " sessions ont été inserées")

        message_ignored_line = ""
        if (ignored_line):
            message_ignored_line = "  ATTENTION -  Les lignes [" + str(
                ignored_line) + "] ont été ignorées. car les toutes informations obligatoires ne sont pas fournies"

        return True, str(nb_inserted_line) + " sessions ont été inserées / Mises à jour. " + str(message_ignored_line)


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible d'importer les sessions de formation  en masse "



"""
Controle fichier avant import
"""
def Controle_Add_Update_SessionFormation_mass_for_many_class(saved_file=None, Folder=None, diction=None):
    try:
        '''
               # Verification que les champs reçus dans l'API sont bien dans la liste des champs autorisés
               # Cela evite le cas ou une entité tierce ajouter les valeurs inconnu dans l'API
               # Ici on doit mettre tous les champs possible (obligatoire ou non) de la BDD dans la liste
               # field_list.
               '''
        field_list = ['token']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + "  Le champ  '" + val + "' n'existe pas, Creation session annulée")
                return False, " Le champ  '" + val + "' n'existe pas, Creation session annulée "

        '''
        Une fois qu'on a controlé que toutes les clés mise dans l'API sont correcte. etape precedente,
        On controle que les champs obligatoires sont presents dans la liste 
        '''
        field_list_obligatoire = ['token', ]

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        # Verifier la validité du token
        retval = mycommon.check_partner_token_validity("", my_token)
        if retval is False:
            return "Err_Connexion", " La session de connexion n'est pas valide"


        partner_recid = mycommon.get_parnter_recid_from_token(my_token)
        if (partner_recid is False):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " - partner_recid KO : Impossible d'importer la liste des sessions ")
            return False, "les information de connexion sont incorrectes. Impossible d'importer la liste des sessions"

        nb_line = 0

        df = pd.read_csv(saved_file, encoding='utf8', on_bad_lines='skip', sep=';', encoding_errors='ignore')
        df = df.fillna('')
        # Dictionnaire des champs utilisables
        '''
        # Verification que les noms des colonne sont bien corrects"
        '''
        field_list = ['date_debut', 'date_fin', 'nb_participant', 'adresse', 'code_postal', 'ville', 'pays',
                      'session_status', 'date_debut_inscription', 'date_fin_inscription', 'attestation',
                      'code_session', "distanciel", "presentiel", "prix_session", 'contenu_ftion', 'lms_class_code',
                      'session_ondemande', 'session_etape', 'formation_code_externe', 'formateur_email', 'titre', 'location_type', 'is_bpf']

        # Controle du nombre de lignes dans le fichier.
        total_rows = len(df)
        if (total_rows > MYSY_GV.MAX_PARTICIPANT_BY_CSV):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " Le fichier comporte plus de " + str(
                    MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes.")
            return False, " Le fichier comporte plus de " + str(MYSY_GV.MAX_PARTICIPANT_BY_CSV) + " lignes."

        # print(df.columns)
        for val in df.columns:
            if str(val).lower() not in field_list:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " : entete du fichier csv. '" + val + "' n'est pas acceptée")
                return False, " Entete du fichier csv. La Colonne '" + val + "' n'est pas acceptée"

        # Verification des champs obligatoires dans le fichier
        field_list_obligatoire_file = ['date_debut', 'date_fin', 'session_status', 'code_session',
                                       'formation_code_externe']

        for val in field_list_obligatoire_file:
            if val not in df.columns:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " : Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire")
                return False, " Le champ '" + val + "' n'est pas présent dans le fichier. Il est obligatoire "


        x = range(0, total_rows)
        ignored_line = ""
        nb_inserted_line = 0

        for n in x:
            mydata = {}

            nb_inserted_line = nb_inserted_line + 1
            # Si une ligne n'a aucune information obligatoire, alors on ignore la ligne
            if (str(df['code_session'].values[n]) == "nan" or str(df['session_status'].values[n]) == "nan" or
                    str(df['date_debut'].values[n]) == "nan" or str(df['date_fin'].values[n]) == "nan"):
                mycommon.myprint(str(
                    inspect.stack()[0][
                        3]) + " - La ligne " + str(n + 2) + " a été ignorée")
                ignored_line = str(n + 2) + " , " + str(ignored_line)

                nb_inserted_line = nb_inserted_line - 1
                continue

            external_code = ""
            if ("formation_code_externe" in df.keys()):
                if (str(df['formation_code_externe'].values[n])):
                    external_code = str(df['formation_code_externe'].values[n]).strip()

                    # On verifie l'existance de l'external code pour ce une des formations de ce partner
                    count_class = MYSY_GV.dbname['myclass'].count_documents(
                        {'external_code': str(external_code), 'valide': '1',
                         'locked': '0', 'partner_owner_recid': str(partner_recid)})

                    if (count_class < 0):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Ligne " + str(
                                n + 2) + " : Le code_externe n'est pas valide.")
                        return False, " Ligne " + str(n + 2) + " : Le code_externe n'est pas valide."

                    if (count_class > 1):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Erreur : Ligne " + str(
                                n + 2) + " : Le code_externe corresponds à plus d'une formation.")
                        return False, " Erreur : Ligne " + str(
                            n + 2) + " : Le code_externe corresponds à plus d'une formation."

                    class_date = MYSY_GV.dbname['myclass'].find_one(
                        {'external_code': str(external_code), 'valide': '1',
                         'locked': '0', 'partner_owner_recid': str(partner_recid)})

                else:
                    mycommon.myprint(
                        str(inspect.stack()[0][3]) + " Erreur : Ligne " + str(
                            n + 2) + " : Le code_externe de la formation  n'est pas invalide.")
                    return False, " Erreur : Ligne " + str(
                        n + 2) + " :  Le code_externe de la formation  n'est pas invalide."


            mydata['date_debut'] = str(df['date_debut'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_debut'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date debut n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 2) + ". La date debut n'est pas au format jj/mm/aaaa "

            mydata['date_fin'] = str(df['date_fin'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_fin'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date date_fin n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 1) + ". La date fin n'est pas au format jj/mm/aaaa "

            ## Verification de la cohérence des dates. Date_du doit <= Date_au
            if (datetime.strptime(str(mydata['date_debut']).strip(), '%d/%m/%Y') > datetime.strptime(
                    str(mydata['date_fin']).strip(), '%d/%m/%Y')):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer la session de formation : La date debut " + str(
                        mydata['date_debut']) +
                    " est postérieure à la date de fin " + str(mydata['date_fin']) + " pour la ligne  " + str(
                        n + 2) + " ")

                return False, " Impossible de créer la session de formation : La date debut " + str(
                    mydata['date_debut']) + \
                       " est postérieure à la date de fin " + str(mydata['date_fin'])[0:10] + " pour la ligne  " + str(
                    n + 2) + " "

            # Verifier que l'adresse email du formateur est valide
            formateur_email = ""
            formateur_id = ""
            if ("formateur_email" in df.keys()):
                if (str(df['formateur_email'].values[n]) and str(df['formateur_email'].values[n]) != ""):

                    formateur_email = str(df['formateur_email'].values[n]).strip()
                    if (mycommon.isEmailValide(formateur_email) is False):
                        mycommon.myprint(str(
                            inspect.stack()[0][3]) + " - L'email du formateur  '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide.")
                        return False, " L'email du formateur '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide."

                    is_formateur_email_ok = MYSY_GV.dbname['ressource_humaine'].count_documents(
                        {'email': formateur_email,
                         'valide': '1',
                         'locked': '0',
                         'partner_recid': str(
                             partner_recid)})
                    if (is_formateur_email_ok <= 0):
                        mycommon.myprint(str(
                            inspect.stack()[0][3]) + " - L'email du formateur  '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide (2).")
                        return False, " L'email du formateur '" + str(
                            formateur_email) + "' pour la formation à la ligne " + str(
                            n + 2) + " n'est pas valide (2)."

                    formateur_data = MYSY_GV.dbname['ressource_humaine'].find_one({'email': formateur_email,
                                                                                   'valide': '1',
                                                                                   'locked': '0',
                                                                                   'partner_recid': str(
                                                                                       partner_recid)})

                    formateur_id = str(formateur_data['_id'])

            mydata['formateur_id'] = formateur_id

            if ("titre" in df.keys()):
                mydata['titre'] = str(df['titre'].values[n]).strip()

            if ("location_type" in df.keys()):
                mydata['location_type'] = str(df['location_type'].values[n]).strip().lower()
                if (str(df['location_type'].values[n]).strip().lower() not in MYSY_GV.TRAINING_LOCATION_TYPE):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " - Le champ 'location_type' est incorrecte. Les valeurs acceptés sont : "+str(MYSY_GV.TRAINING_LOCATION_TYPE))
                    return False, "Le champ 'location_type' est incorrect. . Les valeurs acceptés sont : "+str(MYSY_GV.TRAINING_LOCATION_TYPE)+ " "

            if ("is_bpf" in df.keys()):
                if (str(df['is_bpf'].values[n]).strip() not in ['0', '1']):
                    mycommon.myprint(str(inspect.stack()[0][
                                             3]) + " - Le champ 'bpf' est incorrect.  Les valeurs acceptées sont : '1' ou '0' ")
                    return False, "Le champ 'bpf' est incorrect.  Les valeurs acceptées sont : '1' ou '0' "

                mydata['is_bpf'] = str(df['is_bpf'].values[n]).strip()

            local_nb_participants = "1"
            if ("nb_participant" in df.keys()):
                if (str(df['nb_participant'].values[n])):
                    local_nb_participants = str(df['nb_participant'].values[n]).strip()

            local_status, new_participants = mycommon.IsInt(local_nb_participants)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'nb_participant' de la ligne " + str(
                        n + 2) + " est incorrecte.")
                return False, " Le champ nb_participant de la ligne " + str(n + 2) + " est incorrecte. Un nombre Entier Positif doit être fourni "

            mydata['nb_participant'] = str(new_participants)

            prix_session = "0"
            if ("prix_session" in df.keys()):
                if (str(df['prix_session'].values[n])):
                    prix_session = str(df['prix_session'].values[n]).strip()

            local_status, new_prix_session = mycommon.IsFloat(prix_session)
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'prix_session' de la ligne " + str(
                        n + 2) + " est incorrecte.")
                return False, " Le champ prix_session de la ligne " + str(n + 2) + " est incorrecte. "

            mydata['prix_session'] = str(new_prix_session)

            local_code_session = ""
            if ("code_session" in df.keys()):
                if (str(df['code_session'].values[n])):
                    local_code_session = str(df['code_session'].values[n]).strip()

            if (len(str(local_code_session).strip()) < 2):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'code_session' de la ligne " + str(
                        n + 2) + " doit faire plus de deux caractères.")
                return False, " Le champ code_session de la ligne " + str(
                    n + 2) + " doit faire plus de deux caractères. "

            mydata['code_session'] = local_code_session

            local_adresse = ""
            if ("adresse" in df.keys()):
                if (str(df['adresse'].values[n])):
                    local_adresse = str(df['adresse'].values[n]).strip()
            mydata['adresse'] = local_adresse


            session_ondemande = "0"
            if ("session_ondemande" in df.keys()):
                if (str(df['session_ondemande'].values[n])):
                    session_ondemande = str(df['session_ondemande'].values[n]).strip()
                    session_ondemande = str(mycommon.tryInt(str(session_ondemande)))

                    if (session_ondemande != "1" and session_ondemande != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'session_ondemande' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'session_ondemande' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"

            mydata['session_ondemande'] = session_ondemande

            local_code_postal = ""
            if ("code_postal" in df.keys()):
                if (str(df['code_postal'].values[n])):
                    local_code_postal = str(df['code_postal'].values[n]).strip()

                    if ("." in str(local_code_postal)):
                        # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                        local_code_postal = str(local_code_postal).split(".")[0]
                    elif ("." in str(local_code_postal)):
                        # /!\ : l'utilisateur envoie un code poster au format entier, plutot que string.
                        local_code_postal = str(local_code_postal).split(",")[0]
                    else:
                        local_code_postal = str(local_code_postal)


            mydata['code_postal'] = local_code_postal

            distanciel = ""
            if ("distanciel" in df.keys()):
                if (str(df['distanciel'].values[n])):
                    distanciel = str(df['distanciel'].values[n]).strip()
                    distanciel = str(mycommon.tryInt(distanciel))

                    if (distanciel != "1" and distanciel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'distanciel' de la ligne " + str(
                                n + 2) + " est incorrecte. La valeur fournie est "+str(distanciel) )
                        return False, " Le champ 'distanciel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0'. La valeur fournie est "+str(distanciel)

            mydata['distantiel'] = distanciel

            presentiel = ""
            if ("presentiel" in df.keys()):
                if (str(df['presentiel'].values[n])):
                    presentiel = str(df['presentiel'].values[n]).strip()
                    presentiel = str(mycommon.tryInt(presentiel))

                    if (presentiel != "1" and presentiel != "0"):
                        mycommon.myprint(
                            str(inspect.stack()[0][3]) + " Le champ 'presentiel' de la ligne " + str(
                                n + 2) + " est incorrecte.")
                        return False, " Le champ 'presentiel' de la ligne " + str(
                            n + 2) + " est incorrecte. Les valeurs acceptées sont '1' ou '0' ou vide"

            mydata['presentiel'] = presentiel

            # mydata['code_postal'] = str(df['code_postal'].values[n]).strip()

            local_ville = ""
            if ("ville" in df.keys()):
                if (str(df['ville'].values[n])):
                    local_ville = str(df['ville'].values[n]).strip()
            mydata['ville'] = local_ville

            local_pays = ""
            if ("pays" in df.keys()):
                if (str(df['pays'].values[n])):
                    local_pays = str(df['pays'].values[n]).strip()
            mydata['pays'] = local_pays

            contenu_ftion = ""
            if ("contenu_ftion" in df.keys()):
                if (str(df['contenu_ftion'].values[n])):
                    contenu_ftion = str(df['contenu_ftion'].values[n]).strip()
            mydata['contenu_ftion'] = contenu_ftion

            # mydata['ville'] = str(df['ville'].values[n]).strip()

            local_attestation_certif = ""
            if ("attestation" in df.keys()):
                if (str(df['attestation'].values[n])):
                    local_attestation_certif = str(df['attestation'].values[n]).strip()
            mydata['attestation_certif'] = local_attestation_certif

            # mydata['attestation_certif'] = str(df['attestation'].values[n]).strip()


            session_status = ""
            if ("session_status" in df.keys()):
                if (str(df['session_status'].values[n])):
                    session_status = str(df['session_status'].values[n]).strip()
            mydata['session_status'] = session_status

            session_etape = ""
            if ("session_etape" in df.keys()):
                if (str(df['session_etape'].values[n])):
                    session_etape = str(df['session_etape'].values[n]).strip()
            mydata['session_etape'] = session_etape

            """ 
            Update du 11/08/23 :
            - le status va etre geré en 0 (desactivé) et 1 (activé)
            - mise en commentaire du code ci-dessous

            local_session = str(df['session_status'].values[n]).strip()

            if( str(local_session) != "0" and  str(local_session) != "1" and  str(local_session) != "2"):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2")
                return False, " Le champ 'session_status' de la ligne " + str(n+2) + " est incorrecte. Les valeurs acceptées sont 0,1,2"

            if( str(local_session) == "1"):
                mydata['session_status'] = str("true")
            """

            mydata['date_debut_inscription'] = str(df['date_debut_inscription'].values[n]).strip().split(" ")[0]
            local_status = mycommon.CheckisDate(mydata['date_debut_inscription'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La  date_debut_inscription n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 2) + ". La date_debut_inscription n'est pas au format jj/mm/aaaa "

            mydata['date_fin_inscription'] = str(df['date_fin_inscription'].values[n]).strip().split(" ")[0]

            local_status = mycommon.CheckisDate(mydata['date_fin_inscription'])
            if (local_status is False):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                        n + 2) + "."
                                 "La date_fin_inscription n'est pas au format jj/mm/aaaa ")

                return False, " - Impossible de créer/mettre à jour la session de formation à ligne " + str(
                    n + 1) + ". La date_fin_inscription n'est pas au format jj/mm/aaaa "

            ## Verification de la cohérence des dates. Date_du doit <= Date_au
            if (datetime.strptime(str(mydata['date_debut_inscription']).strip(), '%d/%m/%Y') > datetime.strptime(
                    str(mydata['date_fin_inscription']).strip(), '%d/%m/%Y')):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " - Impossible de créer la session de formation : La date debut des inscriptions " + str(
                        mydata['date_debut_inscription']) +
                    " est postérieure à la date de fin des inscriptions " + str(
                        mydata['date_fin_inscription']) + " pour la ligne  " + str(n + 2) + " ")

                return False, " Impossible de créer la session de formation : La date de fin des inscriptions est antérieure à la date de début des inscriptions : Ligne  " + str(
                    n + 2) + " "

            mydata['token'] = str(my_token)

            # print(" ### mydata ", mydata)

            diction_for_session_id = {}
            diction_for_session_id['date_du'] = str(mydata['date_debut']).split(" ")[0]
            diction_for_session_id['date_au'] = str(mydata['date_fin']).split(" ")[0]
            diction_for_session_id['code_postal'] = str(mydata['code_postal'])
            diction_for_session_id['adresse'] = str(mydata['adresse'])


        return True, str(total_rows)+" sessions dans le fichier"


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de controler le fichier des sessions de formation  en masse "


"""
Cette fonction supprime une session de formation.
/!\ : il faut s'assurer qu'il n'y a aucune inscription avant de supprimer valide.
Et la session est supprimé, alors supprimer toutes inscriptions (annulée) associés
Ensuite supprimer les affectations d'enseignants et de materiels associée

12/01/2024 : 
- Supprimer les sequences de cette session
- Dealloué les agenda 
"""
def Delete_SessionFormation(diction):
    try:

        field_list = ['token',  'session_id', ]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, "de récupérer la liste des stagiaires . Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', 'session_id', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des stagiaires, Toutes les informations necessaires n'ont pas été fournies"

        # Recuperation du recid du partner
        mydata = {}
        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']


        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        partner_recid = str(my_partner['recid'])
        session_id = ""
        if ("session_id" in diction.keys()):
            if diction['session_id']:
                session_id = diction['session_id']



        # Verifier qu'il n'y a pas d'incription valide
        inscription_count_qry = {'session_id':str(session_id), 'status':{'$in':['0', '1', '2']} , 'partner_owner_recid':str(partner_recid)}
        print(" ####  inscription_count_qry aa =", inscription_count_qry)
        inscription_count = MYSY_GV.dbname['inscription'].count_documents(inscription_count_qry)
        if( inscription_count > 0 ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de supprimer la session. Vous avez "+str(inscription_count)+" inscription(s) associée(s) ")
            return False, "Impossible de supprimer la session. Vous avez "+str(inscription_count)+" inscription(s) associée(s)"

        # Suppression des insciption eventuellement annulées
        MYSY_GV.dbname['inscription'].delete_many({'session_id':str(session_id)})

        # Suppression des affectation de ressources associées
        delete_affectation_qry = {'related_target_collection':'session_formation', 'related_target_collection_id':str(session_id),
                                  'partner_owner_recid': str(partner_recid)
                                  }

        # Pour les Ressource Humaine
        MYSY_GV.dbname['ressource_humaine_affectation'].delete_many(delete_affectation_qry)

        # Pour le materiel
        MYSY_GV.dbname['ressource_materielle_affectation'].delete_many(delete_affectation_qry)

        # 12/01/2024 : Supprimer les sequences
        for sequence in MYSY_GV.dbname['session_formation_sequence'].find({'session_id':str(diction['session_id']),
                                                                           'partner_owner_recid':str(my_partner['recid'])},
                                                                          {'_id':1}):
            local_status, local_retval = Session_Formation_Sequence.Delete_Given_Session_Sequence({'token':str(diction['token']), '_id':str(sequence['_id']) })
            if( local_status is False):
                mycommon.myprint(str(inspect.stack()[0][3]) + " WARNING : Impossible de supprimer la sequence_id "+str(sequence['_id']))



        # Suppression de la session
        deleted_session_qry = {'_id':ObjectId(str(session_id)), 'partner_owner_recid':str(partner_recid)}
        deleted_data = MYSY_GV.dbname['session_formation'].delete_many(deleted_session_qry)

        mycommon.myprint(str(inspect.stack()[0][3]) + str(deleted_data.deleted_count)+" Document supprimé. La session_id " + str(
            session_id) + " a été correctement supprimée ")


        return True, "La session a été correctement supprimée"

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de supprimer la session de formation"


"""
Cette fonction prends une liste '_id' de session et effectue la suppresion
si les conditions sont remplie.

list_session_id = ['id1', 'id2', 'id3,]
"""
def Delete_List_SessionFormation(diction):
    try:

        field_list = ['token',  'list_session_id', ]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, "de récupérer la liste des stagiaires . Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', 'list_session_id', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Impossible de récupérer la liste des stagiaires, Toutes les informations necessaires n'ont pas été fournies"

        # Recuperation du recid du partner
        mydata = {}
        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']


        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        partner_recid = my_partner['recid']

        list_session_id = []
        if ("list_session_id" in diction.keys()):
            if diction['list_session_id']:
                list_session_id = str(diction['list_session_id']).replace(",", ";").split(";")



        # Verifier qu'il n'y a pas d'incription valide
        for session_id in list_session_id :
            inscription_count_qry = {'session_id':str(session_id), 'status':{'$in':['0', '1', '2']} , 'partner_owner_recid':str(partner_recid)}
            #print(" ####  inscription_count_qry aa =", inscription_count_qry)
            inscription_count = MYSY_GV.dbname['inscription'].count_documents(inscription_count_qry)
            if( inscription_count > 0 ):
                session_formation_data = MYSY_GV.dbname['session_formation'].find_one({'_id':ObjectId(str(session_id)), 'partner_owner_recid':str(partner_recid)})
                mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de supprimer la session "+str(session_formation_data['code_session'])+". Vous avez "+str(inscription_count)+" inscriptions associées.Suppression en masse annulée ")
                return False, "Impossible de supprimer la session "+str(session_formation_data['code_session'])+". Vous avez "+str(inscription_count)+" inscriptions associées. Suppression en masse annulée"

        # Suppression des insciption eventuellement annulées
        MYSY_GV.dbname['inscription'].delete_many({'session_id':{'$in':list_session_id}})

        # Suppression des affectation de ressources associées
        delete_affectation_qry = {'related_target_collection':'session_formation', 'related_target_collection_id':{'$in':list_session_id},
                                  'partner_owner_recid': str(partner_recid)
                                  }

        # Pour les Ressource Humaine
        MYSY_GV.dbname['ressource_humaine_affectation'].delete_many(delete_affectation_qry)

        # Pour le materiel
        MYSY_GV.dbname['ressource_materielle_affectation'].delete_many(delete_affectation_qry)


        # Suppression des sessions
        cpt = 0
        for session_id in list_session_id:
            deleted_session_qry = {'_id':ObjectId(str(session_id)), 'partner_owner_recid':str(partner_recid)}
            deleted_data = MYSY_GV.dbname['session_formation'].delete_many(deleted_session_qry)

            mycommon.myprint(str(inspect.stack()[0][3]) + str(deleted_data.deleted_count)+" Document supprimé. La session_id " + str(
            session_id) + " a été correctement supprimée ")
            cpt = cpt + 1

        if( cpt > 1 ):
            return True, "("+str(cpt)+") sessions ont été correctement supprimées"
        else:
            return True, "La session a été correctement supprimée"


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de supprimer la liste des sessions de formation"


"""
Cette fonction verifier des données correspondent à une session : 
- internal_url
         - date_debut
         - date_fin
         - si presentiel : la ville
         - si distance, juste le a distance 

"""
def Is_Corresponding_SessionFormation(diction):
    try:

        field_list = ['token', 'class_url', 'date_debut','date_fin', 'distantiel', 'ville' ]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, " Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', 'class_url', 'date_debut','date_fin', 'distantiel', 'ville' ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " Toutes les informations necessaires n'ont pas été fournies"

        # Recuperation du recid du partner
        mydata = {}
        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']

        # Verifier la validité du token
        retval = mycommon.check_partner_token_validity("", mytoken)
        if retval is False:
            return "Err_Connexion", " La session de connexion n'est pas valide"

        partner_recid = mycommon.get_parnter_recid_from_token(mytoken)
        if (partner_recid is False):
            mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de récupérer la liste des stagiaires, ")
            return False, " Les informations d'identification sont incorrectes "


        class_url = ""
        if ("class_url" in diction.keys()):
            if diction['class_url']:
                class_url = diction['class_url']

        """
        Recuperation de l'internal url depuis l'url de la formation 
        """
        myclass_data = MYSY_GV.dbname['myclass'].find_one({'valide':"1", 'locked':'0',
                                                           'url':str(class_url)})


        if( myclass_data is None or "internal_url" not in myclass_data.keys()):
            mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de récupérer les données de la formation, ")
            return False, " Impossible de récupérer les données de la formation, "

        class_internal_url = str(myclass_data['internal_url'])

        coll_session = MYSY_GV.dbname['session_formation']

        myquery = [{'$match':{ 'class_internal_url':class_internal_url, 'partner_owner_recid':str(partner_recid)}},
            {'$lookup':
                {
                    'from': 'myclass',
                    'localField': 'class_internal_url',
                    'foreignField': 'internal_url',
                    'pipeline': [{'$project': {'title': 1, 'lms_class_code':1}}],
                    'as': 'myclass'
                }
            }
        ]

        print(" ##### myquery GetSessionFormation = "+str(myquery))
        RetObject = []

        nb_val = 0
        for retval in coll_session.aggregate(myquery):
            #print(" ##### Is_Corresponding_SessionFormation retval  a controle = " + str(retval))
            if (retval['distantiel'] == diction['distantiel'] and str(retval['date_debut'][0:10]) == diction[
                'date_debut']
                    and str(retval['date_fin'][0:10]) == diction['date_fin'] and retval['ville'] == diction['ville']):
                user = {}
                user = retval


                user['id'] = str(nb_val)
                nb_val = nb_val + 1
                title = ""
                lms_class_code = ""
                if ('myclass' in retval.keys() and len(retval['myclass']) > 0):

                    if( "title" in retval['myclass'][0].keys()):
                        title = retval['myclass'][0]['title']

                    if ("lms_class_code" in retval['myclass'][0].keys()):
                        lms_class_code = retval['myclass'][0]['lms_class_code']

                user['title'] = title
                user['lms_class_code'] = lms_class_code

                RetObject.append(mycommon.JSONEncoder().encode(user))


        return True, RetObject

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de verifier si la session correspond à une session existante"



"""
Cette fonction permet de dupliquer une session de formation.
par defaut lorqu'on duplique

- code_session = old_code_session+"_dup"

/!\ : Important : on prend une tab de session_id
"""

def Duplicate_List_Session_Formation(diction):
    try:

        field_list = ['token', 'tab_session_id']
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé.")
                return False, " Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token', 'tab_session_id']
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " Toutes les informations necessaires n'ont pas été fournies"

        # Recuperation du recid du partner
        mydata = {}
        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']

        # Verifier la validité du token
        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data({'token':mytoken})
        if (local_status is not True):
            return local_status, my_partner


        # Verification de la validité de toutes les session dans tab_session_id
        tab_session_id = str(diction['tab_session_id']).split(',')
        for session_id in tab_session_id :
            if( MYSY_GV.dbname['session_formation'].count_documents({'_id':ObjectId(str(session_id)),
                                                                     'valide':'1',
                                                                     'partner_owner_recid':str(my_partner['recid'])}) != 1):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " La session_id :"+str(session_id)+" n'est pas valide ")
                return False, " La session_id :"+str(session_id)+" n'est pas valide  "


        # Duplicata
        cpt = 0
        now = str(datetime.now())
        for session_id in tab_session_id:
            session_data = MYSY_GV.dbname['session_formation'].find_one({'_id':ObjectId(str(session_id)),
                                                                     'valide':'1',
                                                                     'partner_owner_recid':str(my_partner['recid'])})

            if( session_data is None):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " La session n'est pas valide ")
                return False, " La session n'est pas valide "



            duplicated_session = session_data
            del duplicated_session['_id']
            duplicated_session['code_session'] = str(session_data['code_session'])+"_dup"
            duplicated_session['date_update'] = now
            duplicated_session['update_by'] = str(my_partner['_id'])
            local_retval = MYSY_GV.dbname['session_formation'].insert_one(duplicated_session)

            """
                    # Ajout de l'evenement dans l'historique
                    """
            now = str(datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
            history_event_dict = {}
            history_event_dict['token'] = diction['token']
            history_event_dict['related_collection'] = "session_formation"
            history_event_dict['related_collection_recid'] = str(local_retval.inserted_id)
            history_event_dict['action_date'] = str(now)
            history_event_dict['action_description'] = "Creation (Dupliqué depuis "+str(duplicated_session['code_session'])+" "
            local_status, local_retval = mycommon.Collection_Historique.Add_Historique_Event(history_event_dict)
            if (local_status is False):
                mycommon.myprint(
                    " WARNING : Impossible de logguer l'historique pour l'évènement : " + str(history_event_dict))

            cpt = cpt + 1

        if(cpt > 1 ):
            return True, "("+str(cpt)+" sessions ont été correctement dupliquées "
        else:
            return True, "La session a été correctement dupliquée "

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de dupliquer la session de formation"


"""
Cette fonction les conventions par email de formation en partant de la session.

Algo :

On recuperer les stagiaires qu'on groupe par client_rattachement_id 
Pour tous ceux qui on le meme, on envoie une convention groupée

pour les autres, on envoi des conventions individuelle
 
"""
def Prepare_and_Send_Convention_From_Session_By_Email(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  "


        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("client_rattachement_id",
                                                                              {'session_id':str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                                  "client_rattachement_id": { '$ne': ''}
                                                                              }
                                                                              )


        print(" ### la liste des liste_client_rattachement_id = ", liste_client_rattachement_id)


        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer"

            tab_saved_file_full_path.append(saved_file_full_path)

        print(" #### tab_saved_file_full_path = ", tab_saved_file_full_path)

        # Recupération des données du modèle de document
        is_convention_by_client = "0"
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        if( courrier_template_data and "edit_by_client" in  courrier_template_data.keys() and courrier_template_data['edit_by_client'] == "1"):
            is_convention_by_client = "1"

        if( str(is_convention_by_client) == "1" ):
            # Envoie des conventions pour les inscrits AVEC client_id  (conventions d'entreprise)
            for single_client in liste_client_rattachement_id :
                print(" Traintement du client_id = ", single_client)

                # Recuperation des contacts de communication du client
                local_diction = {}
                local_diction['token'] = diction['token']
                local_diction['_id'] = str(single_client)

                #print(" ##### local_diction = ", local_diction)
                local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(local_diction)


                if (local_status is False):
                    mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                    return False, " Impossible de récupérer les contacts de communication du client "

                #print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
                tab_local_email_production = []
                for tmp in partner_client_contact_communication:
                    tmp_JSON = ast.literal_eval(tmp)
                    if ("email" in tmp_JSON.keys()):
                        tab_local_email_production.append(str(tmp_JSON["email"]))

                list_local_email_production = ",".join(tab_local_email_production)

                new_diction_client = {}
                new_diction_client['partner_client_id'] = single_client
                new_diction_client['token'] = diction['token']
                new_diction_client['courrier_template_id'] = diction['courrier_template_id']
                new_diction_client['email_test'] = diction['email_test']
                new_diction_client['email_production'] = str(list_local_email_production)
                new_diction_client['session_id'] = diction['session_id']
                new_diction_client['request_digital_signature'] = ""
                if( "request_digital_signature" in diction.keys() ):
                    new_diction_client['request_digital_signature'] = diction['request_digital_signature']


                #print(" ##### new_diction_client 0102 = ", new_diction_client)

                local_status, local_retval = Inscription_mgt.Sent_Convention_Stagiaire_By_Email_By_Partner_client(tab_saved_file_full_path, Folder, new_diction_client)

                if( local_status is False):
                    mycommon.myprint("  WARNING : impossible d'envoyer la convention au client :  "+str( single_client))

        elif (str(is_convention_by_client) == "0"):
            liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
                {"$or": [{'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          "client_rattachement_id": ''
                          },
                         {'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          'client_rattachement_id': {'$exists': False}
                          }]
                 }
                )
            # Envoie des conventions pour les inscrits SANS client_id (conventions individuelles)
            print(" ### is_convention_by_client ==== 0 : ")
            print(" ### LIST traitement de l'inscrit : ", liste_inscription_no_client)


            for single_inscrit_no_client in liste_inscription_no_client :
                print(" ### traitement de l'inscrit : ",single_inscrit_no_client )
                #field_list_obligatoire = [ 'token', 'inscription_id', 'courrier_template_id', 'email_test', 'email_production' ]
                new_diction_no_client = {}
                new_diction_no_client['token'] = str(diction['token'])
                new_diction_no_client['inscription_id'] = str(single_inscrit_no_client['_id'])
                new_diction_no_client['courrier_template_id'] = diction['courrier_template_id']

                new_diction_no_client['session_id'] = diction['session_id']
                new_diction_no_client['email_test'] = diction['email_test']
                new_diction_no_client['email_production'] = diction['email_production']
                new_diction_no_client['request_digital_signature'] = ""
                if ("request_digital_signature" in diction.keys()):
                    new_diction_no_client['request_digital_signature'] = diction['request_digital_signature']

                print(" ##### new_diction_no_client = ", new_diction_no_client)
                local_status, local_retval = Inscription_mgt.Sent_Convention_Stagiaire_By_Email(tab_saved_file_full_path, Folder, new_diction_no_client)

                if (local_status is False):
                    mycommon.myprint("  WARNING impossible d'envoyer la convention a l'apprenant :  " + str(single_inscrit_no_client['_id']) )

        else:
            return False, " Vous devez definir si la convention est individuelle ou par client"

        # Traitement de l'eventuel fichier joint
        tab_files_to_attache_to_mail = []



        return True, " Les conventions ont été correctement envoyées par emails"

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer les conventions par email "



"""
Cette fonction envoie les convention cochées par 'default'.
La fonction va envoyer les convention par defaut pour les inscrit rattachés à une societé et ceux rattaché à une societe.

Regle de gestion des adresse email :
Si le champ 'email_test' est rempli, alors il prend le pas sur la recherche des emails par defaut. Cela signifie que le
client veux faire un test.

"""
def Prepare_and_Send_Default_Convention_From_Session_By_Email(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        is_test_mode = "0"
        email_test = ""
        if( "email_test" in diction.keys() and diction['email_test']):
            email_test = diction['email_test']
            if( mycommon.isEmailValide(email_test) is False ):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " L'adresse email du test est invalide  ")
                return False, " L'adresse email du test est invalide "

            is_test_mode =  "1"



        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  "


        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("client_rattachement_id",
                                                                              {'session_id':str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                                  "client_rattachement_id": { '$ne': ''}
                                                                              }
                                                                              )


        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer"

            tab_saved_file_full_path.append(saved_file_full_path)

        """
        On verifie s'il y a des convention d'entreprise à faire, si oui on verifie si il y a un document 
        qui a été configuré
        """

        if (len(liste_client_rattachement_id) > 0):
            """ 
            # Recupération de la convention d'entreprise par defaut de à envoyer par email :
            - ref_interne = "CONVENTION_STAGIAIRE"
            - default_version = "1"
            - edit_by_client = "1"
            - type_doc = "email"
            """

            default_courrier_template_client_count = MYSY_GV.dbname['courrier_template'].count_documents(
                {'valide': '1',
                 'locked': '0',
                 'ref_interne': 'CONVENTION_STAGIAIRE',
                 'default_version': '1',
                 'edit_by_client': '1',
                 'type_doc': 'email',
                 'partner_owner_recid': str(my_partner['recid'])}
            )

            if( default_courrier_template_client_count <= 0 ):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Aucune convention d'entreprise n'est configurée ")
                return False, " Aucune convention d'entreprise n'est configurée  "

            if (default_courrier_template_client_count > 1):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Il y a " + str(
                        default_courrier_template_client_count) + " conventions d'entreprise configurées. Il ne doit y avoir qu'une seule ")
                return False, " Il y a " + str(
                    default_courrier_template_client_count) + " conventions d'entreprise configurées. Il ne doit y avoir qu'une seule "

        nb_liste_inscription_no_client = MYSY_GV.dbname['inscription'].count_documents(
            {"$or": [{'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      "client_rattachement_id": ''
                      },
                     {'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      'client_rattachement_id': {'$exists': False}
                      }]
             }
        )

        """
        On verifie s'il y a des conventions individuelles à faire, si oui on verifie si il y a un document 
        qui a été configuré
        """

        if (nb_liste_inscription_no_client > 0):
            """ 
            # Recupération de la convention individuelle par defaut de à envoyer par email :
            - ref_interne = "CONVENTION_STAGIAIRE"
            - default_version = "1"
            - edit_by_client = "0"
            - type_doc = "email"
            """



            default_courrier_template_individuel_count = MYSY_GV.dbname['courrier_template'].count_documents(
                {"$or": [{'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': '0',
                          'type_doc': 'email',
                          'partner_owner_recid': str(my_partner['recid'])}
                    ,
                         {'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': {'$exists': False},
                          'type_doc': 'email',
                          'partner_owner_recid': str(my_partner['recid'])}

                         ]
                 }
            )

            if (default_courrier_template_individuel_count <= 0):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Aucune convention individuelle n'est configurée ")
                return False, " Aucune convention d'individuelle n'est configurée  "


            if (default_courrier_template_individuel_count > 1):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Il y a "+str(default_courrier_template_individuel_count)+" conventions individuelles par défaut configurées. Il ne doit y avoir qu'une seule ")
                return False, " Il y a "+str(default_courrier_template_individuel_count)+" conventions individuelles par défaut configurées. Il ne doit y avoir qu'une seule "



        # Envoi des conventions d'entreprise par default
        if( len(liste_client_rattachement_id) > 0 ):
            # Il y a bien de inscris rattachés à des client. on cherche et envoie les conventions d'entreprises)

            # Recuperer les datas de la convention par defautl
            default_courrier_template_client_data = MYSY_GV.dbname['courrier_template'].find_one(
                {'valide': '1',
                 'locked': '0',
                 'ref_interne': 'CONVENTION_STAGIAIRE',
                 'default_version': '1',
                 'edit_by_client': '1',
                 'type_doc': 'email',
                 'partner_owner_recid': str(my_partner['recid'])}
            )

            for single_client in liste_client_rattachement_id :

                # Recuperation des contacts de communication du client
                local_diction = {}
                local_diction['token'] = diction['token']
                local_diction['_id'] = str(single_client)

                print(" ## Traitement du client  id: ", str(single_client))
                local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(local_diction)


                if (local_status is False):
                    mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                    return False, " Impossible de récupérer les contacts de communication du client "


                tab_local_email_production = []
                for tmp in partner_client_contact_communication:
                    tmp_JSON = ast.literal_eval(tmp)
                    if ("email" in tmp_JSON.keys()):
                        tab_local_email_production.append(str(tmp_JSON["email"]))

                list_local_email_production = ",".join(tab_local_email_production)

                new_diction_client = {}
                new_diction_client['partner_client_id'] = single_client
                new_diction_client['token'] = diction['token']
                new_diction_client['courrier_template_id'] = default_courrier_template_client_data['_id']

                if( is_test_mode == "1"):
                    new_diction_client['email_test'] = email_test
                    new_diction_client['email_production'] = ""
                else:
                    new_diction_client['email_test'] = ""
                    new_diction_client['email_production'] = str(list_local_email_production)

                new_diction_client['session_id'] = diction['session_id']

                new_diction_client['request_digital_signature'] = ""
                if ("request_digital_signature" in diction.keys()):
                    new_diction_client['request_digital_signature'] = diction['request_digital_signature']

                local_status, local_retval = Inscription_mgt.Sent_Convention_Stagiaire_By_Email_By_Partner_client(tab_saved_file_full_path, Folder, new_diction_client)

                if( local_status is False):
                    mycommon.myprint("  WARNING : impossible d'envoyer la convention au client :  "+str( single_client))


        # Envoi des conventions individuelles par defaut
        if (nb_liste_inscription_no_client > 0):
            liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
                {"$or": [{'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          "client_rattachement_id": ''
                          },
                         {'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          'client_rattachement_id': {'$exists': False}
                          }]
                 }
                )

            # Recuperer les datas de la convention par defautl
            default_courrier_template_individuel_data = MYSY_GV.dbname['courrier_template'].find_one(
                {"$or": [{'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': '0',
                          'type_doc': 'email',
                          'partner_owner_recid': str(my_partner['recid'])}
                    ,
                         {'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': {'$exists': False},
                          'type_doc': 'email',
                          'partner_owner_recid': str(my_partner['recid'])}

                         ]
                 }
            )

            # Envoie des conventions pour les inscrits SANS client_id (conventions individuelles)
            print(" ### is_convention_by_client ==== 0 : ")
            print(" ### LIST traitement de l'inscrit : ", liste_inscription_no_client)


            for single_inscrit_no_client in liste_inscription_no_client :
                print(" ### traitement de l'inscrit : ",single_inscrit_no_client )
                #field_list_obligatoire = [ 'token', 'inscription_id', 'courrier_template_id', 'email_test', 'email_production' ]
                new_diction_no_client = {}
                new_diction_no_client['token'] = str(diction['token'])
                new_diction_no_client['inscription_id'] = str(single_inscrit_no_client['_id'])
                new_diction_no_client['courrier_template_id'] = default_courrier_template_individuel_data['_id']

                new_diction_no_client['session_id'] = diction['session_id']
                if (is_test_mode == "1"):
                    new_diction_no_client['email_test'] = email_test
                    new_diction_no_client['email_production'] = ""
                else:
                    new_diction_no_client['email_test'] = ""
                    new_diction_no_client['email_production'] = ""

                print(" ##### new_diction_no_client = ", new_diction_no_client)
                local_status, local_retval = Inscription_mgt.Sent_Convention_Stagiaire_By_Email(tab_saved_file_full_path, Folder, new_diction_no_client)

                if (local_status is False):
                    mycommon.myprint("  WARNING impossible d'envoyer la convention a l'apprenant :  " + str(single_inscrit_no_client['_id']) )

        else:
            return False, " Vous devez definir si la convention est individuelle ou par client"

        # Traitement de l'eventuel fichier joint
        tab_files_to_attache_to_mail = []


        return True, " Les conventions ont été correctement envoyées par emails"

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer les conventions par email "




"""
Cette fonction permet de telecharger PDF toutes les conventions d'une session.
Depuis la session, l'utilisateur choisi le modele PDF et
telecharge les conventions d'entreprise et les conventions individuelles.

On créé un fichier zip qui sera téléchargé avec toutes les pièces jointes

"""
def Prepare_and_Send_Convention_From_Session_By_PDF(diction):
    try:
        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"
            elif str(diction[val]).strip() == "":
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  Le champ obligatoire  '" + val + "' est vide  ")
                return False, " Le champ obligatoire  '" + val + "' est vide  "


        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  "

        # Stokage des nom de fichier à zipper
        list_file_name_to_zip = []

        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client

        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("client_rattachement_id",
                                                                              {'session_id':str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                                  "client_rattachement_id": { '$ne': ''}
                                                                              }
                                                                              )

        print(" ### la liste des liste_client_rattachement_id = ", liste_client_rattachement_id)

        # Recuperer inscriptions indépenantes, donc pas liée à un client


        liste_inscription_no_client = MYSY_GV.dbname['inscription'].find({"$or": [{'session_id': str(diction['session_id']),
                                                                                  'status': '1',
                                                                                  'partner_owner_recid': str(my_partner['recid']),
                                                                                  "client_rattachement_id": ''
                                                                                  },
                                                                                 {'session_id': str(diction['session_id']),
                                                                                  'status': '1',
                                                                                  'partner_owner_recid': str(my_partner['recid']),
                                                                                  'client_rattachement_id': {'$exists': False}
                                                                                  }]
                                                                         }
                                                                         )

        """
        for val in liste_inscription_no_client:
            print(" ### la liste des liste_inscription_no_client = ", str(val))
        """



        # Recupération des données du modèle de document
        is_convention_by_client = "0"
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        if (courrier_template_data and "edit_by_client" in courrier_template_data.keys() and courrier_template_data[
            'edit_by_client'] == "1"):
            is_convention_by_client = "1"

        if (str(is_convention_by_client) == "1"):
            # Envoie des conventions pour les inscrits AVEC client_id
            for val in liste_client_rattachement_id:
                local_diction = {}
                # field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'client_id']
                local_diction['token'] = diction['token']
                local_diction['session_id'] = diction['session_id']
                local_diction['courrier_template_id'] = diction['courrier_template_id']
                local_diction['client_id'] = str(val)

                print(" #### Traitemnt de local_diction aaa = ", local_diction)
                local_status, local_full_file_name = Create_Convention_By_Client_PDF(local_diction)
                if( local_status is False):
                    return local_status, local_full_file_name
                else:
                    print(" ###  pdf_file_name = ", local_full_file_name)
                    list_file_name_to_zip.append(str(local_full_file_name))




        elif (str(is_convention_by_client) == "0"):
            liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
                {"$or": [{'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          "client_rattachement_id": ''
                          },
                         {'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          'client_rattachement_id': {'$exists': False}
                          }]
                 }
                )
            for val in liste_inscription_no_client:
                local_diction = {}
                # field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'client_id']
                local_diction['token'] = diction['token']
                local_diction['session_id'] = diction['session_id']
                local_diction['courrier_template_id'] = diction['courrier_template_id']
                local_diction['inscription_id'] = str(val['_id'])

                #print(" #### Traitemnt de local_diction bb = ", local_diction)
                local_status, local_full_file_name = Create_Convention_By_Stagiaire_PDF(local_diction)
                if( local_status is False):
                    return local_status, local_full_file_name
                else:
                    list_file_name_to_zip.append(str(local_full_file_name))


        # Create a ZipFile Object
        todays_date = str(date.today().strftime("%d/%m/%Y"))
        ts = datetime.now().timestamp()
        ts = str(ts).replace(".", "").replace(",", "")[-3:]
        zip_file_name = str(MYSY_GV.TEMPORARY_DIRECTORY_V2)+"List_Convention_session_"+str(diction['session_id'])+"_"+str(ts)+".zip"

        with ZipFile(zip_file_name, 'w') as zip_object:
            for pdf_files in list_file_name_to_zip :
                #print(" ### fichier a zipper = ", pdf_files)
                zip_object.write(str(pdf_files))


        if os.path.exists(zip_file_name):
            # print(" ### ok os.path.exists(outputFilename) "+str(outputFilename))

            return True, send_file(zip_file_name, as_attachment=True)


        return False, " Impossible de générer les conventions par pdf PDF (1) "



    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, "  Impossible de générer les conventions par pdf PDF  "



"""
Cette fonction permet de telecharge les conventions par defaut pour les entreprises et les individuels
"""
def Prepare_and_Send_Default_Convention_From_Session_By_PDF(diction):
    try:
        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  "

        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("client_rattachement_id",
                                                                              {'session_id': str(
                                                                                  diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                               "client_rattachement_id": {'$ne': ''}
                                                                               }
                                                                              )



        # --------
        """
                On verifie s'il y a des convention d'entreprise à faire, si oui on verifie si il y a un document 
                qui a été configuré
                """

        if (len(liste_client_rattachement_id) > 0):
            """ 
            # Recupération de la convention d'entreprise par defaut de à envoyer par email :
            - ref_interne = "CONVENTION_STAGIAIRE"
            - default_version = "1"
            - edit_by_client = "1"
            - type_doc = "pdf"
            """

            default_courrier_template_client_count = MYSY_GV.dbname['courrier_template'].count_documents(
                {'valide': '1',
                 'locked': '0',
                 'ref_interne': 'CONVENTION_STAGIAIRE',
                 'default_version': '1',
                 'edit_by_client': '1',
                 'type_doc': 'pdf',
                 'partner_owner_recid': str(my_partner['recid'])}
            )

            if (default_courrier_template_client_count <= 0):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Aucune convention d'entreprise n'est configurée ")
                return False, " Aucune convention d'entreprise n'est configurée  "

            if (default_courrier_template_client_count > 1):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Il y a " + str(
                        default_courrier_template_client_count) + " conventions d'entreprise par défaut  configurées. Il ne doit y avoir qu'une seule ")
                return False, " Il y a " + str(
                    default_courrier_template_client_count) + " conventions d'entreprise par défaut  configurées. Il ne doit y avoir qu'une seule "

        nb_liste_inscription_no_client = MYSY_GV.dbname['inscription'].count_documents(
            {"$or": [{'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      "client_rattachement_id": ''
                      },
                     {'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      'client_rattachement_id': {'$exists': False}
                      }]
             }
        )

        """
        On verifie s'il y a des conventions individuelles à faire, si oui on verifie si il y a un document 
        qui a été configuré
        """

        if (nb_liste_inscription_no_client > 0):
            """ 
            # Recupération de la convention individuelle par defaut de à envoyer par email :
            - ref_interne = "CONVENTION_STAGIAIRE"
            - default_version = "1"
            - edit_by_client = "0"
            - type_doc = "pdf"
            """
            qry_indi = {"$or": [{'valide': '1',
                                 'locked': '0',
                                 'ref_interne': 'CONVENTION_STAGIAIRE',
                                 'default_version': '1',
                                 'edit_by_client': '0',
                                 'type_doc': 'pdf',
                                 'partner_owner_recid': str(my_partner['recid'])}
                ,
                                {'valide': '1',
                                 'locked': '0',
                                 'ref_interne': 'CONVENTION_STAGIAIRE',
                                 'default_version': '1',
                                 'edit_by_client': {'$exists': False},
                                 'type_doc': 'pdf',
                                 'partner_owner_recid': str(my_partner['recid'])}

                                ]
                        }

            print(" ### qry_indi = ", qry_indi)

            default_courrier_template_individuel_count = MYSY_GV.dbname['courrier_template'].count_documents(
                {"$or": [{'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': '0',
                          'type_doc': 'pdf',
                          'partner_owner_recid': str(my_partner['recid'])}
                    ,
                         {'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': {'$exists': False},
                          'type_doc': 'pdf',
                          'partner_owner_recid': str(my_partner['recid'])}

                         ]
                 }
            )

            if (default_courrier_template_individuel_count <= 0):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Aucune convention individuelle n'est configurée ")
                return False, " Aucune convention d'individuelle n'est configurée  "

            if (default_courrier_template_individuel_count > 1):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Il y a " + str(
                        default_courrier_template_individuel_count) + " conventions individuelles configurées. Il ne doit y avoir qu'une seule ")
                return False, " Il y a " + str(
                    default_courrier_template_individuel_count) + " conventions individuelles configurées. Il ne doit y avoir qu'une seule "

        # -------------

        # Stokage des nom de fichier à zipper
        list_file_name_to_zip = []



        # Recuperer inscriptions indépenantes, donc pas liée à un client
        liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
            {"$or": [{'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      "client_rattachement_id": ''
                      },
                     {'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      'client_rattachement_id': {'$exists': False}
                      }]
             }
        )
        # Envoi des conventions d'entreprise par default
        if (len(liste_client_rattachement_id) > 0):

            # Recuperer les datas de la convention par defautl
            default_courrier_template_client_data = MYSY_GV.dbname['courrier_template'].find_one(
                {'valide': '1',
                 'locked': '0',
                 'ref_interne': 'CONVENTION_STAGIAIRE',
                 'default_version': '1',
                 'edit_by_client': '1',
                 'type_doc': 'pdf',
                 'partner_owner_recid': str(my_partner['recid'])}
            )


            for val in liste_client_rattachement_id:
                local_diction = {}
                # field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'client_id']
                local_diction['token'] = diction['token']
                local_diction['session_id'] = diction['session_id']
                local_diction['courrier_template_id'] = default_courrier_template_client_data['_id']
                local_diction['client_id'] = str(val)

                print(" #### Traitemnt de local_diction aaa = ", local_diction)
                local_status, local_full_file_name = Create_Convention_By_Client_PDF(local_diction)
                if( local_status is False):
                    return local_status, local_full_file_name
                else:
                    print(" ###  pdf_file_name = ", local_full_file_name)
                    list_file_name_to_zip.append(str(local_full_file_name))

                # Envoi des conventions individuelles par defaut

        # Envoi des conventions individuelles par defaut
        if (nb_liste_inscription_no_client > 0):
            liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
                {"$or": [{'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          "client_rattachement_id": ''
                          },
                         {'session_id': str(diction['session_id']),
                          'status': '1',
                          'partner_owner_recid': str(my_partner['recid']),
                          'client_rattachement_id': {'$exists': False}
                          }]
                 }
            )
            # Recuperer les datas de la convention par defautl
            default_courrier_template_individuel_data = MYSY_GV.dbname['courrier_template'].find_one(
                {"$or": [{'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': '0',
                          'type_doc': 'pdf',
                          'partner_owner_recid': str(my_partner['recid'])}
                    ,
                         {'valide': '1',
                          'locked': '0',
                          'ref_interne': 'CONVENTION_STAGIAIRE',
                          'default_version': '1',
                          'edit_by_client': {'$exists': False},
                          'type_doc': 'pdf',
                          'partner_owner_recid': str(my_partner['recid'])}

                         ]
                 }
            )

            for val in liste_inscription_no_client:
                local_diction = {}
                # field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'client_id']
                local_diction['token'] = diction['token']
                local_diction['session_id'] = diction['session_id']
                local_diction['courrier_template_id'] = default_courrier_template_individuel_data['_id']
                local_diction['inscription_id'] = str(val['_id'])

                #print(" #### Traitemnt de local_diction bb = ", local_diction)
                local_status, local_full_file_name = Create_Convention_By_Stagiaire_PDF(local_diction)
                if( local_status is False):
                    return local_status, local_full_file_name
                else:
                    list_file_name_to_zip.append(str(local_full_file_name))


        # Create a ZipFile Object
        todays_date = str(date.today().strftime("%d/%m/%Y"))
        ts = datetime.now().timestamp()
        ts = str(ts).replace(".", "").replace(",", "")[-3:]
        zip_file_name = str(MYSY_GV.TEMPORARY_DIRECTORY_V2)+"List_Convention_session_"+str(diction['session_id'])+"_"+str(ts)+".zip"

        with ZipFile(zip_file_name, 'w') as zip_object:
            for pdf_files in list_file_name_to_zip :
                #print(" ### fichier a zipper = ", pdf_files)
                zip_object.write(str(pdf_files))


        if os.path.exists(zip_file_name):
            # print(" ### ok os.path.exists(outputFilename) "+str(outputFilename))
            return True, send_file(zip_file_name, as_attachment=True)

        return False, " Impossible de générer les conventions par  PDF (1) "



    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, "  Impossible de générer les conventions par pdf PDF  "




""" 
Cette fonction créer un PDF par client et retounr l'url complet du fichier pdf
pour les client (convention de type client)
"""

def Create_Convention_By_Client_PDF(diction):
    try:
        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'client_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner


        # 1 - Verifier que le modele de courrier est bien editable par client
        template_courrier_data = MYSY_GV.dbname['courrier_template'].find_one({'_id':ObjectId(str(diction['courrier_template_id'])),
                                                                               'valide':'1',
                                                                               'locked':'0',
                                                                               'partner_owner_recid':str(my_partner['recid'])})

        if( template_courrier_data is None or "edit_by_client" not in template_courrier_data.keys() or str(template_courrier_data['edit_by_client']) != "1" ):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  Le modèle de courrier n'est pas éditable par client ")
            return False, "  Le modèle de courrier n'est pas éditable par client  "

        # Verifier que le client est valide
        client_data = MYSY_GV.dbname['partner_client'].find_one({'_id':ObjectId(str(diction['client_id'])), 'valide':'1',
                                                                               'locked':'0',
                                                                               'partner_recid':str(my_partner['recid'])})

        if (client_data is None ):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant du client est invalide ")
            return False, "   L'identifiant du client est invalide   "

        tab_client = []
        tab_client.append(client_data['_id'])


        # Verifier que la session est valide
        session_data = MYSY_GV.dbname['session_formation'].find_one(
            {'_id': ObjectId(str(diction['session_id'])), 'valide': '1', 'partner_owner_recid': str(my_partner['recid'])})

        if (session_data is None):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide  ")
            return False, "  L'identifiant de la session  est invalide  "

        tab_session = []
        tab_session.append(session_data['_id'])

        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find_one(
            {'internal_url': str(session_data['class_internal_url']), 'valide': '1',
             'partner_owner_recid': str(my_partner['recid']), 'locked': '0'})

        tab_class = []
        tab_class.append(class_data['_id'])

        # Recuperation des données du stagiaire
        inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                               'status': '1',
                                                               'partner_owner_recid': str(my_partner['recid']),
                                                               'client_rattachement_id': str(
                                                                   diction['client_id'])})

        tab_participant = []
        for val in inscription_data:
            tab_participant.append(val['_id'])

        print(" ### tab_participant = ", tab_participant)
        # Creation du dictionnaire d'information à utiliser pour la creation du doc
        convention_dictionnary_data = {}
        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = tab_participant
        new_diction['list_session_id'] = tab_session
        new_diction['list_class_id'] = tab_class
        new_diction['list_client_id'] = tab_client

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)

        if (local_status is False):
            return local_status, local_retval

        convention_dictionnary_data = local_retval

        body = {
            "params": convention_dictionnary_data,
        }

        """
         Creation du ficier PDF
        """


        contenu_doc_Template = jinja2.Template(str(template_courrier_data['contenu_doc']))

        sourceHtml = contenu_doc_Template.render(params=body["params"])

        todays_date = str(date.today().strftime("%d/%m/%Y"))
        ts = datetime.now().timestamp()
        ts = str(ts).replace(".", "").replace(",", "")[-5:]

        orig_file_name = "Convention_" + str(my_partner['recid'])[0:5] + "_" + str(ts) + ".pdf"
        outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name)

        # open output file for writing (truncated binary)
        resultFile = open(outputFilename, "w+b")

        # convert HTML to PDF
        pisaStatus = pisa.CreatePDF(
            src=sourceHtml,  # the HTML to convert
            dest=resultFile)  # file handle to receive result

        # close output file
        resultFile.close()

        """
            25/01/2024 : pour loger une action dans la collection  ==> courrier_template_tracking_history
            """
        # Recuperation des données du stagiaire
        inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                               'status': '1',
                                                               'partner_owner_recid': str(my_partner['recid']),
                                                               'client_rattachement_id': str(
                                                                   diction['client_id'])})

        qry = {'session_id': str(diction['session_id']),
                                                               'status': '1',
                                                               'partner_owner_recid': str(my_partner['recid']),
                                                               'client_rattachement_id': str(
                                                                   diction['client_id'])}

        for inscription in inscription_data:
           
            local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(my_partner, "CONVENTION_STAGIAIRE_ENTREPRISE", str(diction['session_id']), 'inscription', str(inscription['_id']),
                                                                                                                                    str(template_courrier_data['_id']))
            if( local_status is False ):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  WARNING : impossible d'exécuter la fonction Editic_Log_History_Action_From_courrier_template_type_document_ref_interne local_retval =  ", local_retval)

        return True, outputFilename

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer le fichier pdf de convention client "


"""
Cette fonction créer un PDF par client et retounr l'url complet du fichier pdf pour les 
personnes sans rattachement client (convention indivisuelle)
"""
def Create_Convention_By_Stagiaire_PDF(diction):
    try:
        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'inscription_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner


        # 1 - Verifier que le modele de courrier est bien editable par individu
        template_courrier_data = MYSY_GV.dbname['courrier_template'].find_one({'_id':ObjectId(str(diction['courrier_template_id'])),
                                                                               'valide':'1',
                                                                               'locked':'0',
                                                                               'ref_interne': 'CONVENTION_STAGIAIRE',
                                                                               'partner_owner_recid':str(my_partner['recid'])})

        if (template_courrier_data is None ):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "L'identifiant du modèle de document "+str(diction['courrier_template_id'])+" est invalide")
            return False, "L'identifiant du modèle de document "+str(diction['courrier_template_id'])+" est invalide "

        if( "edit_by_client" in template_courrier_data.keys() and str(template_courrier_data['edit_by_client']) == "1"  ):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  Le modèle de courrier n'est pas éditable par stagiaire. ")
            return False, "  Le modèle de courrier n'est pas éditable par stagiaire  "

        # Verifier que le statgiaire est valide
        local_qry = {'_id':ObjectId(str(diction['inscription_id'])), 'status':'1',
                                                                               'partner_owner_recid':str(my_partner['recid'])}

        statgiaire_data = MYSY_GV.dbname['inscription'].find_one({'_id':ObjectId(str(diction['inscription_id'])), 'status':'1',
                                                                               'partner_owner_recid':str(my_partner['recid'])})

        if (statgiaire_data is None ):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant du stagiaire est invalide  ")
            return False, "   L'identifiant du stagiaire est invalide   "

        tab_stagiaire = []
        tab_stagiaire.append(statgiaire_data['_id'])

        """
                update du 30/01/2024 
                si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
                En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
                Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
                """
        tab_apprenant = []
        if ("apprenant_id" in statgiaire_data.keys() and statgiaire_data['apprenant_id']):
            tab_apprenant.append(ObjectId(str(statgiaire_data['apprenant_id'])))


        # Verifier que la session est valide
        session_data = MYSY_GV.dbname['session_formation'].find_one(
            {'_id': ObjectId(str(diction['session_id'])), 'valide': '1', 'partner_owner_recid': str(my_partner['recid'])})

        if (session_data is None):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide  ")
            return False, "  L'identifiant de la session  est invalide  "

        tab_session = []
        tab_session.append(session_data['_id'])

        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find_one(
            {'internal_url': str(session_data['class_internal_url']), 'valide': '1',
             'partner_owner_recid': str(my_partner['recid']), 'locked': '0'})

        tab_class = []
        tab_class.append(class_data['_id'])



        # Creation du dictionnaire d'information à utiliser pour la creation du doc
        convention_dictionnary_data = {}
        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = tab_stagiaire
        new_diction['list_session_id'] = tab_session
        new_diction['list_class_id'] = tab_class
        new_diction['list_client_id'] = []
        new_diction['list_apprenant_id'] = tab_apprenant

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)

        if (local_status is False):
            return local_status, local_retval

        convention_dictionnary_data = local_retval

        body = {
            "params": convention_dictionnary_data,
        }

        """
         Creation du ficier PDF
        """
        contenu_doc_Template = jinja2.Template(str(template_courrier_data['contenu_doc']))

        sourceHtml = contenu_doc_Template.render(params=body["params"])

        todays_date = str(date.today().strftime("%d/%m/%Y"))
        ts = datetime.now().timestamp()
        ts = str(ts).replace(".", "").replace(",", "")[-5:]

        orig_file_name = "Convention_" + str(my_partner['recid'])[0:5] + "_" + str(ts) + ".pdf"
        outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name)

        # open output file for writing (truncated binary)
        resultFile = open(outputFilename, "w+b")

        # convert HTML to PDF
        pisaStatus = pisa.CreatePDF(
            src=sourceHtml,  # the HTML to convert
            dest=resultFile)  # file handle to receive result

        # close output file
        resultFile.close()

        """
                       25/01/2024 : pour loger une action dans la collection  ==> courrier_template_tracking_history
                    """

        """local_status, local_retval = module_editique.Editic_Log_History_Action(my_partner, template_courrier_data, str(diction['session_id']),
                                                                               'inscription', str(diction['inscription_id']) )"""

        local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
            my_partner, "CONVENTION_STAGIAIRE_INDIVIDUELLE", str(diction['session_id']), 'inscription',
            str(diction['inscription_id']),
            str(str(template_courrier_data['_id'])))

        return True, outputFilename

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer le fichier pdf de convention par stagiaire "


"""
Cette fonction permet de telecharger PDF toutes les conVOCAtion d'une session.
Depuis la session, l'utilisateur choisi le modele PDF et
telecharge les conventions d'entreprise et les conventions individuelles.

On créé un fichier zip qui sera téléchargé avec toutes les pièces jointes

"""
def Prepare_and_Send_Convocation_From_Session_By_PDF(diction):
    try:
        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  "

        # Stokage des nom de fichier à zipper
        list_file_name_to_zip = []



        liste_inscription = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                                  'status': '1',
                                                                                  'partner_owner_recid': str(my_partner['recid']),
                                                                                  }
                                                                         )

        """
        for val in liste_inscription_no_client:
            print(" ### la liste des liste_inscription_no_client = ", str(val))
        """



        # Recupération des données du modèle de document
        is_convention_by_client = "0"
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        if (courrier_template_data and "edit_by_client" in courrier_template_data.keys() and courrier_template_data[
            'edit_by_client'] == "1"):
            is_convention_by_client = "1"


        if (str(is_convention_by_client) == "0"):
            liste_participants = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                                  'status': '1',
                                                                                  'partner_owner_recid': str(my_partner['recid']),
                                                                                  }
                                                                         )
            for val in liste_participants:
                local_diction = {}
                # field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'client_id']
                local_diction['token'] = diction['token']
                local_diction['session_id'] = diction['session_id']
                local_diction['courrier_template_id'] = diction['courrier_template_id']
                local_diction['inscription_id'] = str(val['_id'])

                print(" #### Traitemnt de local_diction bb = ", local_diction)
                local_status, local_full_file_name = Create_Convocation_By_Stagiaire_PDF(local_diction)
                if( local_status is False):
                    return local_status, local_full_file_name
                else:
                    list_file_name_to_zip.append(str(local_full_file_name))


        # Create a ZipFile Object
        todays_date = str(date.today().strftime("%d/%m/%Y"))
        ts = datetime.now().timestamp()
        ts = str(ts).replace(".", "").replace(",", "")[-3:]
        zip_file_name = str(MYSY_GV.TEMPORARY_DIRECTORY_V2)+"List_Convocation_session_"+str(diction['session_id'])+"_"+str(ts)+".zip"

        with ZipFile(zip_file_name, 'w') as zip_object:
            for pdf_files in list_file_name_to_zip :
                #print(" ### fichier a zipper = ", pdf_files)
                zip_object.write(str(pdf_files))


        if os.path.exists(zip_file_name):
            # print(" ### ok os.path.exists(outputFilename) "+str(outputFilename))

            return True, send_file(zip_file_name, as_attachment=True)


        return False, " Impossible de générer les conventions par pdf PDF (1) "



    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, "  Impossible de générer les conventions par pdf PDF  "



"""
Cette fonction créer une convocation PDF par participant à une session de formation
Peu importe le rattachement client ou pas.
"""
def Create_Convocation_By_Stagiaire_PDF(diction):
    try:
        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'inscription_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        qry = {'_id':ObjectId(str(diction['courrier_template_id'])),
                                                                               'valide':'1',
                                                                               'locked':'0',
                                                                               'ref_interne': 'CONVOCATION_STAGIAIRE',
                                                                               'partner_owner_recid':str(my_partner['recid'])}

        print(" ##### qry =  ", qry)

        # 1 - Verifier que le modele de courrier est bien editable par individu
        template_courrier_data = MYSY_GV.dbname['courrier_template'].find_one({'_id':ObjectId(str(diction['courrier_template_id'])),
                                                                               'valide':'1',
                                                                               'locked':'0',
                                                                               'ref_interne': 'CONVOCATION_STAGIAIRE',
                                                                               'partner_owner_recid':str(my_partner['recid'])})

        if( template_courrier_data is None ):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant du  modèle de courrier est invalide ")
            return False, " L'identifiant du  modèle de courrier est invalide   "

        if  ("edit_by_client" in template_courrier_data.keys() and str(template_courrier_data['edit_by_client']) == "1"):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  Le modèle de courrier n'est pas éditable par stagiaire. ")
            return False, "  Le modèle de courrier n'est pas éditable par stagiaire  "


        # Verifier que le statgiaire est valide
        local_qry = {'_id':ObjectId(str(diction['inscription_id'])), 'status':'1',
                                                                               'partner_owner_recid':str(my_partner['recid'])}

        statgiaire_data = MYSY_GV.dbname['inscription'].find_one({'_id':ObjectId(str(diction['inscription_id'])), 'status':'1',
                                                                               'partner_owner_recid':str(my_partner['recid'])})

        if (statgiaire_data is None ):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant du stagiaire est invalide  ")
            return False, "   L'identifiant du stagiaire est invalide   "

        tab_stagiaire = []
        tab_stagiaire.append(statgiaire_data['_id'])

        """
                update du 30/01/2024 
                si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
                En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
                Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
                """
        tab_apprenant = []
        if ("apprenant_id" in statgiaire_data.keys() and statgiaire_data['apprenant_id']):
            tab_apprenant.append(ObjectId(str(statgiaire_data['apprenant_id'])))


        # Verifier que la session est valide
        session_data = MYSY_GV.dbname['session_formation'].find_one(
            {'_id': ObjectId(str(diction['session_id'])), 'valide': '1', 'partner_owner_recid': str(my_partner['recid'])})

        if (session_data is None):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide  ")
            return False, "  L'identifiant de la session  est invalide  "

        tab_session = []
        tab_session.append(session_data['_id'])

        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find_one(
            {'internal_url': str(session_data['class_internal_url']), 'valide': '1',
             'partner_owner_recid': str(my_partner['recid']), 'locked': '0'})

        tab_class = []
        tab_class.append(class_data['_id'])


        # Recuperation des sequences d'une session de formation
        tab_sequence_session = []
        for local_retval in MYSY_GV.dbname['session_formation_sequence'].find({'partner_owner_recid':str(my_partner['recid']),
                                                                               'session_id':str(session_data['_id']),
                                                                               'valide':'1',
                                                                               'locked':'0'}):
            tab_sequence_session.append(local_retval['_id'])


        # Creation du dictionnaire d'information à utiliser pour la creation du doc
        convention_dictionnary_data = {}
        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = tab_stagiaire
        new_diction['list_session_id'] = tab_session
        new_diction['list_class_id'] = tab_class
        new_diction['list_client_id'] = []
        new_diction['list_apprenant_id'] = tab_apprenant
        new_diction['list_sequence_session_id'] = tab_sequence_session

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)

        if (local_status is False):
            return local_status, local_retval

        convention_dictionnary_data = local_retval

        body = {
            "params": convention_dictionnary_data,
        }

        """
         Creation du ficier PDF
        """
        contenu_doc_Template = jinja2.Template(str(template_courrier_data['contenu_doc']))

        sourceHtml = contenu_doc_Template.render(params=body["params"])

        todays_date = str(date.today().strftime("%d/%m/%Y"))
        ts = datetime.now().timestamp()
        ts = str(ts).replace(".", "").replace(",", "")[-5:]

        orig_file_name = "Convocation_" + str(my_partner['recid'])[0:5] + "_" + str(ts) + ".pdf"
        outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name)

        # open output file for writing (truncated binary)
        resultFile = open(outputFilename, "w+b")

        # convert HTML to PDF
        pisaStatus = pisa.CreatePDF(
            src=sourceHtml,  # the HTML to convert
            dest=resultFile)  # file handle to receive result

        # close output file
        resultFile.close()

        """
                       25/01/2024 : pour loger une action dans la collection  ==> courrier_template_tracking_history
                    """
        print(" str(diction['inscription_id']) == ", str(diction['inscription_id']))
        """  local_status, local_retval = module_editique.Editic_Log_History_Action(my_partner, template_courrier_data,
                                                                               str(diction['session_id']), 'inscription', str(diction['inscription_id']))
        """

        local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
            my_partner, "CONVOCATION_STAGIAIRE", str(diction['session_id']), 'inscription',
            str(diction['inscription_id']),
            str(str(template_courrier_data['_id'])))



        return True, outputFilename

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer le fichier pdf de convocation par stagiaire "


"""
Cette fonction prepare et envoi les conVocation a chaque 
participant à la session de formation
"""
def Prepare_and_Send_Convocation_From_Session_By_Email(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  "


        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_participants = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                                  'status': '1',
                                                                                  'partner_owner_recid': str(my_partner['recid']),
                                                                                  }
                                                                         )


        print(" ### la liste des liste_participants = ", liste_participants)


        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer"

            tab_saved_file_full_path.append(saved_file_full_path)

        print(" #### tab_saved_file_full_path = ", tab_saved_file_full_path)

        # Recupération des données du modèle de document
        is_convention_by_client = "0"
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        if( courrier_template_data and "edit_by_client" in  courrier_template_data.keys() and courrier_template_data['edit_by_client'] == "1"):
            is_convention_by_client = "1"


        if (str(is_convention_by_client) == "0"):
            liste_inscription = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                                  'status': '1',
                                                                                  'partner_owner_recid': str(my_partner['recid']),
                                                                                  }
                                                                         )

            print(" ### LIST liste_inscription: ", liste_inscription)


            for single_inscrit in liste_inscription :
                print(" ### convocation traitement de l'inscrit : ",single_inscrit )
                #field_list_obligatoire = [ 'token', 'inscription_id', 'courrier_template_id', 'email_test', 'email_production' ]
                new_diction_no_client = {}
                new_diction_no_client['token'] = str(diction['token'])
                new_diction_no_client['inscription_id'] = str(single_inscrit['_id'])
                new_diction_no_client['courrier_template_id'] = diction['courrier_template_id']

                new_diction_no_client['session_id'] = diction['session_id']
                new_diction_no_client['email_test'] = diction['email_test']
                new_diction_no_client['email_production'] = diction['email_production']

                print(" ##### new_diction_no_client = ", new_diction_no_client)
                local_status, local_retval = Sent_Convocation_Stagiaire_By_Email(tab_saved_file_full_path, Folder, new_diction_no_client)

                if (local_status is False):
                    mycommon.myprint("  WARNING impossible d'envoyer la convocation a l'apprenant :  " + str(single_inscrit['_id']) )


                # Traitement de l'eventuel fichier joint
                tab_files_to_attache_to_mail = []

                """
                25/01/2024 : pour loger une action dans la collection  ==> courrier_template_tracking_history
                """
                """local_status, local_retval = module_editique.Editic_Log_History_Action(my_partner, courrier_template_data,
                                                                                       str(diction['session_id']), "inscription", str(single_inscrit['_id']))"""

                local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                    my_partner, "CONVOCATION_STAGIAIRE", str(diction['session_id']), 'inscription',
                    str(single_inscrit['_id']),
                    str(str(courrier_template_data['_id'])))


        return True, " Les convocations ont été correctement envoyées par emails"

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer les conventions par email "


"""
Envoi d'une convocation pour un participant donné par email
Si le participants est rattaché à un client , alors on va mettre en copie de 
l'email les contacts de communication du client de rattachement
"""
def Sent_Convocation_Stagiaire_By_Email(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'inscription_id', 'courrier_template_id', 'email_test', 'email_production']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que le stagiaire est bien inscrit. Le statut de l'inscription doit etre "1"
        is_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'_id': ObjectId(str(diction['inscription_id'])),
             'status': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_inscription_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de l'inscription est invalide ")
            return False, "  L'identifiant de l'inscription est invalide "

        # Traitement de l'eventuel fichier joint
        tab_files_to_attache_to_mail = []

        for saved_file in tab_files:
            """
            status, saved_file = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer"
            """

            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(saved_file, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(saved_file)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

        # Verification de la validité des adresses email_recu
        """
        /!\ : Si l'email de test est repli, alors on considere que c'est un test, on ne prend pas en compte l'email de email_production.
        Ceci pour forcer les utilisateur à ne remplir que l'email de prod s'il veulent l'envoyer en prod.

        Si l'adresse email_prodution = "defaul", cela veut dire qu'on envoie la convention à :
        - l'adresse email du stagiaire et ses tuteurs (si les tuteurs on cochés la case 'inclu com'

        """

        send_in_production = 0

        tab_emails_destinataire = []

        if ("email_test" in diction.keys() and diction['email_test']):
            send_in_production = 0
            tab_email_test = str(diction['email_test']).replace(";", ",").split(",")
            for email in tab_email_test:
                email = email.strip()
                if (mycommon.isEmailValide(email) is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " L'adresse email " + str(email) + " est invalide ")
                    return False, " L'adresse email " + str(email) + " est invalide "
            tab_emails_destinataire = tab_email_test

        elif ("email_production" in diction.keys() and diction['email_production']):
            send_in_production = 1
            if (str(diction['email_production']) != "default"):
                tab_email_prod = str(diction['email_production']).replace(";", ",").split(",")
                for email in tab_email_prod:
                    email = email.strip()
                    if (mycommon.isEmailValide(str(email)) is False):
                        mycommon.myprint(
                            str(inspect.stack()[0][
                                    3]) + " L'adresse email '" + str(email) + "' est invalide ")
                        return False, " L'adresse email " + str(email) + " est invalide "
                tab_emails_destinataire = tab_email_prod
        else:
            send_in_production = 1
            # On va chercher les adresse email de communication du
            local_dict = {'token': str(diction['token']), '_id': str(diction['inscription_id'])}

            local_status, tab_apprenant_contact = Inscription_mgt.Get_Statgiaire_Communication_Contact(local_dict)
            if (local_status is False):
                return local_status, tab_apprenant_contact

            tmp_tab = []
            # print(" ### tab_apprenant_contact = ", tab_apprenant_contact)

            for tmp in tab_apprenant_contact:
                if ("email" in tmp.keys()):
                    tab_emails_destinataire.append((tmp['email']))

        if (len(tab_emails_destinataire) <= 0):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " Aucune adresse email n'a été fourni. ")
            return False, "  Aucune adresse email n'a été fourni. "

        # print(" ### tab_emails_destinataire = ", tab_emails_destinataire)

        # Verifier que le 'courrier_template_id' est valide
        # Ici le template doit etre un email
        is_courrier_template_id_valide = MYSY_GV.dbname['courrier_template'].count_documents(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'type_doc': 'email',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
            )

        if (is_courrier_template_id_valide != 1):
            mycommon.myprint(str(inspect.stack()[0][3]) + "  L'identifiant du modèle de courrier est invalide ")
            return False, "  L'identifiant du modèle de courrier est invalide "

        # Recupération des données du modèle de document
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        # Recuperation des eventuelles pièces jointes du modèle que courrier
        local_dic = {}
        local_dic['token'] = str(diction['token'])
        local_dic['object_owner_collection'] = "courrier_template"
        local_dic['object_owner_id'] = str(courrier_template_data['_id'])

        local_status, local_retval = attached_file_mgt.Get_List_object_owner_collection_Stored_Files(local_dic)
        if (local_status is False):
            return local_status, local_retval

        # print(" ###  file stocké = ", local_retval)

        # Recuperation des fichiers attachés au modele de courrier, s'il y en a
        for file in local_retval:
            local_JSON = ast.literal_eval(file)

            saved_file = local_JSON['full_path']

            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(saved_file, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(saved_file)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

        # Recuperation des données du stagaire
        inscription_data = MYSY_GV.dbname['inscription'].find_one({'_id': ObjectId(str(diction['inscription_id'])),
                                                                   'status': '1',
                                                                   'partner_owner_recid': str(my_partner['recid'])})

        tab_apprenant_client_rattachement_contact = ""
        # Verifier si le modele de courrier n'est par  'edit_by_client', au quel cas on verifie que l'appressant est bien lié à un client
        if ("edit_by_client" in courrier_template_data.keys() and str(courrier_template_data['edit_by_client']) == "0"):
            stagiaire_client_id = ""

            if ("client_rattachement_id" in inscription_data.keys() and inscription_data['client_rattachement_id']):
                stagiaire_client_id = str(inscription_data['client_rattachement_id'])
                local_diction = {"token":str(diction['token']), "_id":stagiaire_client_id }

                print(" ##### local_diction pr Get_Partner_Client_Communication_Contact= ", local_diction)
                local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(local_diction)

                if (local_status is True):
                    print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
                    tab_local_email_production = []
                    for tmp in partner_client_contact_communication:
                        tmp_JSON = ast.literal_eval(tmp)
                        if ("email" in tmp_JSON.keys()):
                            tab_local_email_production.append(str(tmp_JSON["email"]))

                    tab_apprenant_client_rattachement_contact = ",".join(tab_local_email_production)


        tab_participant = []
        tab_participant.append(inscription_data['_id'])

        """
        update du 30/01/2024 
        si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
        En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
        Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
        """
        tab_apprenant = []
        if( "apprenant_id" in inscription_data.keys() and inscription_data['apprenant_id']) :
            tab_apprenant.append(ObjectId(str(inscription_data['apprenant_id'])))



        # Recuperations des info de la session de formation
        session_data = MYSY_GV.dbname['session_formation'].find_one(
            {'_id': ObjectId(str(inscription_data['session_id'])), 'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        tab_session = []
        tab_session.append(session_data['_id'])

        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find_one(
            {'internal_url': str(session_data['class_internal_url']), 'valide': '1',
             'partner_owner_recid': str(my_partner['recid']), 'locked': '0'})

        tab_class = []
        tab_class.append(class_data['_id'])

        # Creation du dictionnaire d'information à utiliser pour la creation du doc
        convention_dictionnary_data = {}
        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = tab_participant
        new_diction['list_session_id'] = tab_session
        new_diction['list_class_id'] = tab_class
        new_diction['list_client_id'] = []
        new_diction['list_apprenant_id'] = tab_apprenant

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)

        if (local_status is False):
            return local_status, local_retval

        convention_dictionnary_data = local_retval

        body = {
            "params": convention_dictionnary_data,
        }

        # Verifier s'il s'agit d'un document à envoyer avec une version de pièce jointe.
        if ("joint_pdf" in courrier_template_data.keys() and str(courrier_template_data['joint_pdf']) == "1"):
            # Il s'agit bien d'un envoie avec 'contenu_doc' en pièce jointe PDF
            """
            1 - Creation du PDF
            """
            contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))

            sourceHtml = contenu_doc_Template.render(params=body["params"])

            todays_date = str(date.today().strftime("%d/%m/%Y"))
            ts = datetime.now().timestamp()
            ts = str(ts).replace(".", "").replace(",", "")[-5:]

            orig_file_name = "Convocation_" + str(my_partner['recid'])[0:5] + "_" + str(ts) + ".pdf"
            outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name)

            # open output file for writing (truncated binary)
            resultFile = open(outputFilename, "w+b")

            # convert HTML to PDF
            pisaStatus = pisa.CreatePDF(
                src=sourceHtml,  # the HTML to convert
                dest=resultFile)  # file handle to receive result

            # close output file
            resultFile.close()

            # Attachement du fichier joint
            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(outputFilename, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(outputFilename)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

            ## Creation du  mail au format email

            corps_mail_Template = jinja2.Template(str(courrier_template_data['corps_mail']))

            sourceHtml = corps_mail_Template.render(params=body["params"])

            html_mime = MIMEText(sourceHtml, 'html')

            # Creation de l'email à enoyer
            msg = MIMEMultipart("alternative")

        else:
            # Il s'agit d'une simple email

            ## Creation du  mail au format email

            contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))

            sourceHtml = contenu_doc_Template.render(params=body["params"])

            html_mime = MIMEText(sourceHtml, 'html')

            # Creation de l'email à enoyer
            msg = MIMEMultipart("alternative")

        """
        Recuperation des parametre SMTP du partner si le client a decidé d'utiliser son propre smpt
        """
        partner_own_smtp_value = "0"
        partner_own_smtp = MYSY_GV.dbname['base_partner_setup'].find_one(
            {'partner_owner_recid': str(my_partner['recid']),
             'config_name': 'partner_smtp',
             'valide': '1',
             'locked': '0'})

        if (partner_own_smtp and "config_value" in partner_own_smtp.keys()):
            partner_own_smtp_value = partner_own_smtp['config_value']

            if (str(partner_own_smtp_value) == "1"):
                partner_SMTP_COUNT_password = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_user_pwd',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_smtpsrv = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_server',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_user = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_user',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_From_User = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_count_from_name',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_port = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_count_port',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

        if (str(partner_own_smtp_value) == "1"):
            smtpserver = smtplib.SMTP(partner_SMTP_COUNT_smtpsrv, partner_SMTP_COUNT_port)
        else:
            smtpserver = smtplib.SMTP(MYSY_GV.O365_SMTP_COUNT_smtpsrv, MYSY_GV.O365_SMTP_COUNT_port)

        if (str(partner_own_smtp_value) == "1"):
            msg.attach(html_mime)
            msg['From'] = partner_SMTP_COUNT_From_User
            msg['Cc'] = tab_apprenant_client_rattachement_contact
            msg['Bcc'] = 'contact@mysy-training.com'
            msg['Subject'] = courrier_template_data['sujet']
            # msg['to'] = "billardman01@hotmail.com"
            toaddrs = ", ".join(tab_emails_destinataire)
            msg['to'] = str(toaddrs)

            # Attacher l'eventuelle pièces jointes
            for myfile in tab_files_to_attache_to_mail:
                msg.attach(myfile['attached_file'])

            smtpserver.ehlo()
            smtpserver.starttls()
            smtpserver.login(partner_SMTP_COUNT_user, partner_SMTP_COUNT_password)

        else:
            msg.attach(html_mime)
            msg['From'] = MYSY_GV.O365_SMTP_COUNT_From_User
            msg['Cc'] = tab_apprenant_client_rattachement_contact
            msg['Bcc'] = 'contact@mysy-training.com'
            msg['Subject'] = courrier_template_data['sujet']
            # msg['to'] = "billardman01@hotmail.com"
            toaddrs = ", ".join(tab_emails_destinataire)
            msg['to'] = str(toaddrs)

            for myfile in tab_files_to_attache_to_mail:
                msg.attach(myfile['attached_file'])

            smtpserver.ehlo()
            smtpserver.starttls()
            smtpserver.login(MYSY_GV.O365_SMTP_COUNT_user, MYSY_GV.O365_SMTP_COUNT_password)

        val = smtpserver.send_message(msg)
        smtpserver.close()
        print(" Email envoyé " + str(val))

        """
        # Ajout de l'evenement dans l'historique
        """

        # L'action n'est loggué pour les envois reels (en prod)
        if (send_in_production == 1):
            now = str(datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
            history_event_dict = {}
            history_event_dict['token'] = diction['token']
            history_event_dict['related_collection'] = "inscription"
            history_event_dict['related_collection_recid'] = str(diction['inscription_id'])
            history_event_dict['action_date'] = str(now)
            history_event_dict['action_description'] = "Convention envoyée par email à la liste : " + str(
                tab_emails_destinataire)
            local_status, local_retval = mycommon.Collection_Historique.Add_Historique_Event(history_event_dict)
            if (local_status is False):
                mycommon.myprint(
                    " WARNING : Impossible de logguer l'historique pour l'évènement : " + str(history_event_dict))

        return True, "L'email a été correctement envoyé "

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer la convention par email "



"""
Cette fonction prepare et envoi les attestation de formation a chaque 
participant à la session de formation
"""
def Prepare_and_Send_Attestation_From_Session_By_Email(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id',  'email_test', 'email_production', 'tab_inscriptions_ids']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  "

        tab_inscriptions_ids = ""
        if ("tab_inscriptions_ids" in diction.keys()):
            if diction['tab_inscriptions_ids']:
                tab_inscriptions_ids = diction['tab_inscriptions_ids']

        tab_inscriptions_ids_splited = str(tab_inscriptions_ids).split(",")

        tab_inscriptions_ids_splited_obj = []
        for tmp in tab_inscriptions_ids_splited :
            tab_inscriptions_ids_splited_obj.append(ObjectId(str(tmp)))

        qry = {"session_id":str(diction['session_id']),
                                                                 'partner_owner_recid':str(my_partner['recid']),
                                                                 'valide':"1",
                                                                 "locked":'0',
                                                                  '_id': { '$in': tab_inscriptions_ids_splited_obj} }




        for attestation_formation_data in MYSY_GV.dbname['attestation_formation'].find({"session_id":str(diction['session_id']),
                                                                 'partner_owner_recid':str(my_partner['recid']),
                                                                 'valide':"1",
                                                                 "locked":'0',
                                                                  '_id': { '$in': tab_inscriptions_ids_splited_obj} }):




            # Recupération des données du modèle de document
            is_convention_by_client = "0"
            courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
                {'_id': ObjectId(str(attestation_formation_data['courrier_template_id'])),
                 'valide': '1',
                 'locked': '0',
                 'partner_owner_recid': str(my_partner['recid'])}
            )

            if( courrier_template_data and "edit_by_client" in  courrier_template_data.keys() and courrier_template_data['edit_by_client'] == "1"):
                is_convention_by_client = "1"


            #field_list_obligatoire = [ 'token', 'inscription_id', 'courrier_template_id', 'email_test', 'email_production' ]
            new_diction_no_client = {}
            new_diction_no_client['token'] = str(diction['token'])
            new_diction_no_client['inscription_id'] = str(attestation_formation_data['inscription_id'])
            new_diction_no_client['courrier_template_id'] = attestation_formation_data['courrier_template_id']

            new_diction_no_client['session_id'] = diction['session_id']
            new_diction_no_client['email_test'] = diction['email_test']
            new_diction_no_client['email_production'] = diction['email_production']

            print(" ##### new_diction_no_client = ", new_diction_no_client)
            tab_saved_file_full_path = []
            local_status, local_retval = Sent_Attestation_Stagiaire_By_Email(tab_saved_file_full_path, Folder, new_diction_no_client)

            if (local_status is False):
                mycommon.myprint("  WARNING impossible d'envoyer l'attestation de formation a l'apprenant :  " + str(attestation_formation_data['inscription_id']) )


            # Traitement de l'eventuel fichier joint
            tab_files_to_attache_to_mail = []

            """
            25/01/2024 : pour loger une action dans la collection  ==> courrier_template_tracking_history
            """
            local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                my_partner, "ATTESTATION_FORMATION", str(diction['session_id']), 'inscription',
                str(attestation_formation_data['inscription_id']),
                str(attestation_formation_data['courrier_template_id']))

            # Mettre à jour avec la date d'envoi de l'attestation
            updata_data = {}
            updata_data['date_update'] = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
            updata_data['update_by'] = str(my_partner['recid'])
            updata_data['statut'] = "1"
            updata_data['date_envoie'] = datetime.now().strftime("%d/%m/%Y %H:%M:%S")


            MYSY_GV.dbname['attestation_formation'].find_one_and_update({'_id':ObjectId(str(attestation_formation_data['_id'])),
                                                                         'partner_owner_recid':str(my_partner['recid'])},
                                                                        {'$set':updata_data})


        return True, " Les attestation ont été correctement envoyées par emails"

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer les conventions par email "



"""
Envoi d'une attestation pour un participant donné par email
Si le participants est rattaché à un client , alors on va mettre en copie de 
l'email les contacts de communication du client de rattachement
"""
def Sent_Attestation_Stagiaire_By_Email(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'inscription_id', 'courrier_template_id', 'email_test', 'email_production']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que le stagiaire est bien inscrit. Le statut de l'inscription doit etre "1"
        is_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'_id': ObjectId(str(diction['inscription_id'])),
             'status': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_inscription_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de l'inscription est invalide ")
            return False, "  L'identifiant de l'inscription est invalide "

        # Traitement de l'eventuel fichier joint
        tab_files_to_attache_to_mail = []

        for saved_file in tab_files:
            """
            status, saved_file = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer"
            """

            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(saved_file, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(saved_file)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

        # Verification de la validité des adresses email_recu
        """
        /!\ : Si l'email de test est repli, alors on considere que c'est un test, on ne prend pas en compte l'email de email_production.
        Ceci pour forcer les utilisateur à ne remplir que l'email de prod s'il veulent l'envoyer en prod.

        Si l'adresse email_prodution = "defaul", cela veut dire qu'on envoie la convention à :
        - l'adresse email du stagiaire et ses tuteurs (si les tuteurs on cochés la case 'inclu com'

        """

        send_in_production = 0

        tab_emails_destinataire = []

        if ("email_test" in diction.keys() and diction['email_test']):
            send_in_production = 0
            tab_email_test = str(diction['email_test']).replace(";", ",").split(",")
            for email in tab_email_test:
                email = email.strip()
                if (mycommon.isEmailValide(email) is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " L'adresse email " + str(email) + " est invalide ")
                    return False, " L'adresse email " + str(email) + " est invalide "
            tab_emails_destinataire = tab_email_test

        elif ("email_production" in diction.keys() and diction['email_production']):
            send_in_production = 1
            if (str(diction['email_production']) != "default"):
                tab_email_prod = str(diction['email_production']).replace(";", ",").split(",")
                for email in tab_email_prod:
                    email = email.strip()
                    if (mycommon.isEmailValide(str(email)) is False):
                        mycommon.myprint(
                            str(inspect.stack()[0][
                                    3]) + " L'adresse email '" + str(email) + "' est invalide ")
                        return False, " L'adresse email " + str(email) + " est invalide "
                tab_emails_destinataire = tab_email_prod
        else:
            send_in_production = 1
            # On va chercher les adresse email de communication du
            local_dict = {'token': str(diction['token']), '_id': str(diction['inscription_id'])}

            local_status, tab_apprenant_contact = Inscription_mgt.Get_Statgiaire_Communication_Contact(local_dict)
            if (local_status is False):
                return local_status, tab_apprenant_contact

            tmp_tab = []
            # print(" ### tab_apprenant_contact = ", tab_apprenant_contact)

            for tmp in tab_apprenant_contact:
                if ("email" in tmp.keys()):
                    tab_emails_destinataire.append((tmp['email']))

        if (len(tab_emails_destinataire) <= 0):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " Aucune adresse email n'a été fourni. ")
            return False, "  Aucune adresse email n'a été fourni. "

        # print(" ### tab_emails_destinataire = ", tab_emails_destinataire)

        # Verifier que le 'courrier_template_id' est valide
        # Ici le template doit etre un email
        is_courrier_template_id_valide = MYSY_GV.dbname['courrier_template'].count_documents(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'type_doc': 'email',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
            )

        if (is_courrier_template_id_valide != 1):
            mycommon.myprint(str(inspect.stack()[0][3]) + "  L'identifiant du modèle de courrier est invalide ")
            return False, "  L'identifiant du modèle de courrier est invalide "

        # Recupération des données du modèle de document
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        # Recuperation des eventuelles pièces jointes du modèle que courrier
        local_dic = {}
        local_dic['token'] = str(diction['token'])
        local_dic['object_owner_collection'] = "courrier_template"
        local_dic['object_owner_id'] = str(courrier_template_data['_id'])

        local_status, local_retval = attached_file_mgt.Get_List_object_owner_collection_Stored_Files(local_dic)
        if (local_status is False):
            return local_status, local_retval

        # print(" ###  file stocké = ", local_retval)

        # Recuperation des fichiers attachés au modele de courrier, s'il y en a
        for file in local_retval:
            local_JSON = ast.literal_eval(file)

            saved_file = local_JSON['full_path']

            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(saved_file, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(saved_file)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

        # Recuperation des données du stagaire
        inscription_data = MYSY_GV.dbname['inscription'].find_one({'_id': ObjectId(str(diction['inscription_id'])),
                                                                   'status': '1',
                                                                   'partner_owner_recid': str(my_partner['recid'])})

        tab_apprenant_client_rattachement_contact = ""
        # Verifier si le modele de courrier n'est par  'edit_by_client', au quel cas on verifie que l'appressant est bien lié à un client
        if ("edit_by_client" in courrier_template_data.keys() and str(courrier_template_data['edit_by_client']) == "0"):
            stagiaire_client_id = ""

            if ("client_rattachement_id" in inscription_data.keys() and inscription_data['client_rattachement_id']):
                stagiaire_client_id = str(inscription_data['client_rattachement_id'])
                local_diction = {"token":str(diction['token']), "_id":stagiaire_client_id }

                print(" ##### local_diction pr Get_Partner_Client_Communication_Contact= ", local_diction)
                local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(local_diction)

                if (local_status is True):
                    print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
                    tab_local_email_production = []
                    for tmp in partner_client_contact_communication:
                        tmp_JSON = ast.literal_eval(tmp)
                        if ("email" in tmp_JSON.keys()):
                            tab_local_email_production.append(str(tmp_JSON["email"]))

                    tab_apprenant_client_rattachement_contact = ",".join(tab_local_email_production)


        tab_participant = []
        tab_participant.append(inscription_data['_id'])

        """
        update du 30/01/2024 
        si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
        En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
        Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
        """
        tab_apprenant = []
        if( "apprenant_id" in inscription_data.keys() and inscription_data['apprenant_id']) :
            tab_apprenant.append(ObjectId(str(inscription_data['apprenant_id'])))



        # Recuperations des info de la session de formation
        session_data = MYSY_GV.dbname['session_formation'].find_one(
            {'_id': ObjectId(str(inscription_data['session_id'])), 'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        tab_session = []
        tab_session.append(session_data['_id'])

        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find_one(
            {'internal_url': str(session_data['class_internal_url']), 'valide': '1',
             'partner_owner_recid': str(my_partner['recid']), 'locked': '0'})

        tab_class = []
        tab_class.append(class_data['_id'])

        # Creation du dictionnaire d'information à utiliser pour la creation du doc
        convention_dictionnary_data = {}
        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = tab_participant
        new_diction['list_session_id'] = tab_session
        new_diction['list_class_id'] = tab_class
        new_diction['list_client_id'] = []
        new_diction['list_apprenant_id'] = tab_apprenant

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)

        if (local_status is False):
            return local_status, local_retval

        convention_dictionnary_data = local_retval

        body = {
            "params": convention_dictionnary_data,
        }

        # Verifier s'il s'agit d'un document à envoyer avec une version de pièce jointe.
        if ("joint_pdf" in courrier_template_data.keys() and str(courrier_template_data['joint_pdf']) == "1"):
            # Il s'agit bien d'un envoie avec 'contenu_doc' en pièce jointe PDF
            """
            1 - Creation du PDF
            """
            contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))

            sourceHtml = contenu_doc_Template.render(params=body["params"])

            todays_date = str(date.today().strftime("%d/%m/%Y"))
            ts = datetime.now().timestamp()
            ts = str(ts).replace(".", "").replace(",", "")[-5:]

            orig_file_name = "Convocation_" + str(my_partner['recid'])[0:5] + "_" + str(ts) + ".pdf"
            outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name)

            # open output file for writing (truncated binary)
            resultFile = open(outputFilename, "w+b")

            # convert HTML to PDF
            pisaStatus = pisa.CreatePDF(
                src=sourceHtml,  # the HTML to convert
                dest=resultFile)  # file handle to receive result

            # close output file
            resultFile.close()

            # Attachement du fichier joint
            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(outputFilename, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(outputFilename)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

            ## Creation du  mail au format email

            corps_mail_Template = jinja2.Template(str(courrier_template_data['corps_mail']))

            sourceHtml = corps_mail_Template.render(params=body["params"])

            html_mime = MIMEText(sourceHtml, 'html')

            # Creation de l'email à enoyer
            msg = MIMEMultipart("alternative")

        else:
            # Il s'agit d'une simple email

            ## Creation du  mail au format email

            contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))

            sourceHtml = contenu_doc_Template.render(params=body["params"])

            html_mime = MIMEText(sourceHtml, 'html')

            # Creation de l'email à enoyer
            msg = MIMEMultipart("alternative")

        """
        Recuperation des parametre SMTP du partner si le client a decidé d'utiliser son propre smpt
        """
        partner_own_smtp_value = "0"
        partner_own_smtp = MYSY_GV.dbname['base_partner_setup'].find_one(
            {'partner_owner_recid': str(my_partner['recid']),
             'config_name': 'partner_smtp',
             'valide': '1',
             'locked': '0'})

        if (partner_own_smtp and "config_value" in partner_own_smtp.keys()):
            partner_own_smtp_value = partner_own_smtp['config_value']

            if (str(partner_own_smtp_value) == "1"):
                partner_SMTP_COUNT_password = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_user_pwd',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_smtpsrv = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_server',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_user = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_user',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_From_User = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_count_from_name',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_port = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_count_port',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

        if (str(partner_own_smtp_value) == "1"):
            smtpserver = smtplib.SMTP(partner_SMTP_COUNT_smtpsrv, partner_SMTP_COUNT_port)
        else:
            smtpserver = smtplib.SMTP(MYSY_GV.O365_SMTP_COUNT_smtpsrv, MYSY_GV.O365_SMTP_COUNT_port)

        if (str(partner_own_smtp_value) == "1"):
            msg.attach(html_mime)
            msg['From'] = partner_SMTP_COUNT_From_User
            msg['Cc'] = tab_apprenant_client_rattachement_contact
            msg['Bcc'] = 'contact@mysy-training.com'
            msg['Subject'] = courrier_template_data['sujet']
            # msg['to'] = "billardman01@hotmail.com"
            toaddrs = ", ".join(tab_emails_destinataire)
            msg['to'] = str(toaddrs)

            # Attacher l'eventuelle pièces jointes
            for myfile in tab_files_to_attache_to_mail:
                msg.attach(myfile['attached_file'])

            smtpserver.ehlo()
            smtpserver.starttls()
            smtpserver.login(partner_SMTP_COUNT_user, partner_SMTP_COUNT_password)

        else:
            msg.attach(html_mime)
            msg['From'] = MYSY_GV.O365_SMTP_COUNT_From_User
            msg['Cc'] = tab_apprenant_client_rattachement_contact
            msg['Bcc'] = 'contact@mysy-training.com'
            msg['Subject'] = courrier_template_data['sujet']
            # msg['to'] = "billardman01@hotmail.com"
            toaddrs = ", ".join(tab_emails_destinataire)
            msg['to'] = str(toaddrs)

            for myfile in tab_files_to_attache_to_mail:
                msg.attach(myfile['attached_file'])

            smtpserver.ehlo()
            smtpserver.starttls()
            smtpserver.login(MYSY_GV.O365_SMTP_COUNT_user, MYSY_GV.O365_SMTP_COUNT_password)

        val = smtpserver.send_message(msg)
        smtpserver.close()
        print(" Email envoyé " + str(val))

        """
        # Ajout de l'evenement dans l'historique
        """

        # L'action n'est loggué pour les envois reels (en prod)
        if (send_in_production == 1):
            now = str(datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
            history_event_dict = {}
            history_event_dict['token'] = diction['token']
            history_event_dict['related_collection'] = "inscription"
            history_event_dict['related_collection_recid'] = str(diction['inscription_id'])
            history_event_dict['action_date'] = str(now)
            history_event_dict['action_description'] = "Attestation de formation envoyée par email à la liste : " + str(
                tab_emails_destinataire)
            local_status, local_retval = mycommon.Collection_Historique.Add_Historique_Event(history_event_dict)
            if (local_status is False):
                mycommon.myprint(
                    " WARNING : Impossible de logguer l'historique pour l'évènement : " + str(history_event_dict))

        return True, "L'email a été correctement envoyé "

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer la convention par email "


"""
Cette fonction permet de facturer une session de formation selon les regles suivants :
Sur une session : 
- les participants groupés par client_rattachement_id
- facturation du client si j'ai un client rattachement id
- Cas particulier : 
    - un participant qui est en autonome (donc pas de client )
    - pour le facturer il faudra obligatoirement créer une client.
    
/!\ : On ajoute un champ 'invoiced' à chaque ligne.
ainsi si une lignes est déjà invoiced, alors on ne la refacture pas.

/!\ : Pour les inscrit sans client_rattachement_id, voici comment on procede : 
1 - on verifier s'il y a un client avec la meme adresse email, si oui on recupere l'id du client
qu'on vient mettre sur l'inscription. 

2 - si aucun client avec cette adresse email, on va créer un client de type 'particulier' et 
on vient mettre à jour inscription.

Infiné, apres la facturation, toutes les lignes d'inscription on bel et bien un 'client_rattachement_id'

17/05/2024 : /!\
Maintenant on facture à partir du champs "facture_client_rattachement_id" et non 
"client_rattachement_id" qui lui est concerné par les convention et autre documents
administratifs

"""
def Prepare_and_Send_Facture_From_Session_By_Email(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  ", False

        """
                Gestion des inscriptions n'ayant pas de client ID
                """
        liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
            {"$or": [{'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      "facture_client_rattachement_id": '',
                      "invoiced": {'$ne': '1'}
                      },
                     {'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      'facture_client_rattachement_id': {'$exists': False},
                      "invoiced": {'$ne': '1'}
                      }]
             }
        )

        # CONTROLE : Verification des data client (si tous les clients 'particuliers' sont ok
        for local_inscription_no_client in liste_inscription_no_client:
            print(" ### List des inscrit n'ayant pas de 'client_id': ", local_inscription_no_client)

            """ 
            - Verifier s'il y a un client avec la même adresse email, 
            si non, créer le client
            """
            is_inscription_no_client_exist = MYSY_GV.dbname['partner_client'].count_documents(
                {'partner_recid': str(my_partner['recid']),
                 'email': str(local_inscription_no_client['email'])})

            # S'il ya plusieurs clients avec la meme adresse email, alors il y a un bin's
            if (is_inscription_no_client_exist > 1):
                mycommon.myprint(" Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients")
                return False, "  Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients ", False

            # Si le client existe, verifier qu'il est valide et pas locké
            if (is_inscription_no_client_exist == 1):
                is_inscription_no_client_valide_no_locked = MYSY_GV.dbname['partner_client'].count_documents(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                if (is_inscription_no_client_valide_no_locked == 0):
                    mycommon.myprint(" Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide")
                    return False, "  Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide ", False

                # Mise à jour de l'inscription avec l'_id du client
                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set': {'facture_client_rattachement_id': str(
                                                             inscription_no_client_valide_no_locked_data['_id'])}})

            # Si il n'y a pas de client associé à cette adresse email, alors on crée le client
            if (is_inscription_no_client_exist == 0):

                new_client_contact_data = {}
                new_client_data = {}
                new_partner_all_list = ['token', "raison_sociale", "nom", "siret", "tva", "email",
                              "telephone", "website", "comment", "address",
                              'adr_adresse', 'adr_code_postal', 'adr_ville', 'adr_pays',
                              'invoice_email', 'invoice_nom', 'invoice_siret',
                              'invoice_tva', 'invoice_condition_paiement_id', 'invoice_adresse', 'invoice_ville',
                              'invoice_code_postal', 'invoice_pays', "client_type_id",
                              'is_fournisseur', 'is_client', 'is_financeur', 'is_client']

                # Pre Remplir les champs à vide
                for tmp in new_partner_all_list:
                    new_client_data[str(tmp)] = ""


                new_client_data['token'] = diction['token']
                new_client_contact_data['token'] = diction['token']

                if ("nom" in local_inscription_no_client.keys()):
                    new_client_data['raison_sociale'] = local_inscription_no_client['nom']
                    new_client_data['nom'] = local_inscription_no_client['nom']

                    # Pour le contact
                    new_client_contact_data['nom'] = local_inscription_no_client['nom']

                if ("prenom" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['prenom'] = local_inscription_no_client['prenom']

                if ("civilite" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['civilite'] = str(local_inscription_no_client['civilite']).lower()


                if( "email" in local_inscription_no_client.keys() ):
                    new_client_data['email'] = local_inscription_no_client['email']
                    new_client_data['invoice_email'] = local_inscription_no_client['email']

                    # Pour le contact
                    new_client_contact_data['email'] = local_inscription_no_client['email']

                if ("telephone" in local_inscription_no_client.keys()):
                    new_client_data['telephone'] = local_inscription_no_client['telephone']

                    # Pour le contact
                    new_client_contact_data['telephone'] = local_inscription_no_client['telephone']

                if ("adresse" in local_inscription_no_client.keys()):
                    new_client_data['adr_adresse'] = local_inscription_no_client['adresse']
                    new_client_data['invoice_adresse'] = local_inscription_no_client['adresse']

                    # Pour le contact
                    new_client_contact_data['adr_adresse'] = local_inscription_no_client['adresse']


                if("code_postal" in local_inscription_no_client.keys()):
                    new_client_data['adr_code_postal'] = local_inscription_no_client['code_postal']
                    new_client_data['invoice_code_postal'] = local_inscription_no_client['code_postal']

                    # Pour le contact
                    new_client_contact_data['adr_code_postal'] = local_inscription_no_client['code_postal']

                if ("ville" in local_inscription_no_client.keys()):
                    new_client_data['adr_ville'] = local_inscription_no_client['ville']
                    new_client_data['invoice_ville'] = local_inscription_no_client['ville']

                    # Pour le contact
                    new_client_contact_data['adr_ville'] = local_inscription_no_client['ville']

                if ("pays" in local_inscription_no_client.keys()):
                    new_client_data['adr_pays'] = local_inscription_no_client['pays']
                    new_client_data['invoice_pays'] = local_inscription_no_client['pays']

                    # Pour le contact
                    new_client_contact_data['adr_pays'] = local_inscription_no_client['pays']


                new_client_data['is_client'] = "1"
                new_client_data['is_company'] = "0"


                new_client_status, new_client_retval = partner_client.Add_Partner_Client(new_client_data)
                if (new_client_status is False):
                    mycommon.myprint(" Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ")
                    return False, " Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ", False



                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                # Créer le contact de communication du client
                new_client_contact_data['include_com'] = "1"
                new_client_contact_data['related_collection'] = "partner_client"
                new_client_contact_data['related_collection_owner_id'] = str(inscription_no_client_valide_no_locked_data['_id'])
                local_add_contact_status, local_add_contact_retval = Contact.Add_Contact(new_client_contact_data)
                if( local_add_contact_status is False ):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " WARNING : Impossible de créer le contact "+str(local_add_contact_retval))

                # Mise à jour de l'inscription avec l'_id du client
                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set': {'facture_client_rattachement_id': str(
                                                             inscription_no_client_valide_no_locked_data['_id'])}})


        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("facture_client_rattachement_id",
                                                                              {'session_id':str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                                  "facture_client_rattachement_id": { '$ne': ''},
                                                                               "invoiced": {'$ne': '1'}
                                                                              }
                                                                              )


        print(" ### la liste des liste_client_rattachement_id = ", liste_client_rattachement_id)

        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer", False

            tab_saved_file_full_path.append(saved_file_full_path)

        print(" #### tab_saved_file_full_path = ", tab_saved_file_full_path)

        # Recupération des données du modèle de document
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )


        tab_local_invoice_ref_interne = []
        # Envoie des factures pour les inscrits AVEC client_id  (conventions d'entreprise)
        for single_client in liste_client_rattachement_id:
            print(" Traitement du client_id = ", single_client)

            # Recuperation des contacts de communication du client
            local_diction = {}
            local_diction['token'] = diction['token']
            local_diction['_id'] = str(single_client)

            print(" ##### local_diction = ", local_diction)
            local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(
                local_diction)

            if (local_status is False):
                mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                return False, " Impossible de récupérer les contacts de communication du client ", False

            print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
            tab_local_email_production = []
            for tmp in partner_client_contact_communication:
                tmp_JSON = ast.literal_eval(tmp)
                if ("email" in tmp_JSON.keys()):
                    tab_local_email_production.append(str(tmp_JSON["email"]))

            list_local_email_production = ",".join(tab_local_email_production)

            new_diction_client = {}
            new_diction_client['partner_client_id'] = single_client
            new_diction_client['token'] = diction['token']
            new_diction_client['courrier_template_id'] = diction['courrier_template_id']
            new_diction_client['email_test'] = diction['email_test']
            new_diction_client['email_production'] = str(list_local_email_production)
            new_diction_client['session_id'] = diction['session_id']

            print(" ##### new_diction_client 0102 = ", new_diction_client)


            # Creation de la facture du client
            diction_invoice = {}
            diction_invoice['token'] = diction['token']
            diction_invoice['partner_client_id'] = single_client
            diction_invoice['session_id'] = diction['session_id']
            #print(" ##### diction_invoice 0202 = ", diction_invoice)
            local_create_invoice_status, local_create_invoice_retval, local_invoice_ref_interne = Invoice_Partner_From_Session(diction_invoice)
            if( local_create_invoice_status is False ):
                return local_create_invoice_status, local_create_invoice_retval, False


            """
            Mettre à jour les lignes associées à ce client pour dire que la ligne est facturé
            """
            now = str(datetime.now())
            update_data = {}
            update_data['invoiced'] = "1"
            update_data['invoiced_ref'] = str(local_invoice_ref_interne)
            update_data['invoiced_date'] = now
            update_data['date_update'] = now
            update_data['update_by'] = str(my_partner['_id'])

            update = MYSY_GV.dbname['inscription'].update_many({'partner_owner_recid':str(my_partner['recid']),
                                                                'session_id':diction['session_id'],
                                                                'facture_client_rattachement_id':str(single_client)},
                                                               {'$set':update_data})


            tab_local_invoice_ref_interne.append(str(local_invoice_ref_interne))

        """
                07/03/20204 : mettre un statut de facturation sur la session afin de voir
                tout de suite quel session est entièrement facturée ou partiellement.

                regles : 
                Si toutes les inscription associées à une session sont facturée ==> invoiced_statut de la session = 2
                Si au moins une inscription associée à une session est facturé ==> invoiced_statut de la session = 1
                Si non invoiced_statut de la session =0
                """
        nb_inscription_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': diction['session_id'],
             'invoiced': '1'})


        nb_inscription_non_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': diction['session_id'],
             'invoiced': {'$ne': '1'}})

        nb_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': diction['session_id'],
             'status': '1'})


        invoiced_statut = "0"
        if (nb_inscription_facture == nb_inscription_valide):
            # toutes les inscription valides ont été facturée
            invoiced_statut = "2"
        elif (nb_inscription_facture > 0):
            # Au moins une ligne a été facturée
            invoiced_statut = "1"

        # Mise à jour du statut de facturation de la session
        MYSY_GV.dbname['session_formation'].update_one({'partner_owner_recid': my_partner['recid'],
                                                        'valide': '1',
                                                        '_id': ObjectId(str(diction['session_id']))
                                                        },
                                                       {'$set': {'invoiced_statut': invoiced_statut}})


        # Creation de l'historique dans les action 'courrier_template_tracking_history'
        local_qry = {'partner_owner_recid':str(my_partner['recid']), 'session_id':str(diction['session_id']),
                                                                        'invoiced_ref': {'$in': tab_local_invoice_ref_interne} }

        #print(" ###  local_qry = ", local_qry)

        for val in MYSY_GV.dbname['inscription'].find({'partner_owner_recid':str(my_partner['recid']),
                                                       'session_id':str(diction['session_id']),
                                                                        'invoiced_ref': {'$in': tab_local_invoice_ref_interne} }):

            ref_facture = ""
            if( "invoiced_ref" in val.keys() ):
                ref_facture = val['invoiced_ref']

            local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                my_partner, "FACTURATION_SESSION", str(diction['session_id']), 'inscription',
                str(val['_id']),
                str(diction['courrier_template_id']),
                "Facture : "+str(ref_facture)
            )

            if( local_status is False ):
                mycommon.myprint(" WARNING : Impossible de logguer l'historique l'inscription_id : " + str(val['_id']))


        return_message = " La session a été correctement facturée.\nListe des factures : "
        for tmp in tab_local_invoice_ref_interne:
            return_message += "\n - "+str(tmp)

        return True, str(return_message), tab_local_invoice_ref_interne

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer et d'envoyer les factures par email ", False

def Prepare_and_Send_Facture_From_Session_By_Email_SAVE_ORIG(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  ", False

        """
                Gestion des inscriptions n'ayant pas de client ID
                """
        liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
            {"$or": [{'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      "client_rattachement_id": '',
                      "invoiced": {'$ne': '1'}
                      },
                     {'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      'client_rattachement_id': {'$exists': False},
                      "invoiced": {'$ne': '1'}
                      }]
             }
        )

        # CONTROLE : Verification des data client (si tous les clients 'particuliers' sont ok
        for local_inscription_no_client in liste_inscription_no_client:
            print(" ### List des inscrit n'ayant pas de 'client_id': ", local_inscription_no_client)

            """ 
            - Verifier s'il y a un client avec la même adresse email, 
            si non, créer le client
            """
            is_inscription_no_client_exist = MYSY_GV.dbname['partner_client'].count_documents(
                {'partner_recid': str(my_partner['recid']),
                 'email': str(local_inscription_no_client['email'])})

            # S'il ya plusieurs clients avec la meme adresse email, alors il y a un bin's
            if (is_inscription_no_client_exist > 1):
                mycommon.myprint(" Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients")
                return False, "  Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients ", False

            # Si le client existe, verifier qu'il est valide et pas locké
            if (is_inscription_no_client_exist == 1):
                is_inscription_no_client_valide_no_locked = MYSY_GV.dbname['partner_client'].count_documents(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                if (is_inscription_no_client_valide_no_locked == 0):
                    mycommon.myprint(" Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide")
                    return False, "  Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide ", False

                # Mise à jour de l'inscription avec l'_id du client
                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set': {'client_rattachement_id': str(
                                                             inscription_no_client_valide_no_locked_data['_id'])}})

            # Si il n'y a pas de client associé à cette adresse email, alors on crée le client
            if (is_inscription_no_client_exist == 0):

                new_client_contact_data = {}
                new_client_data = {}
                new_partner_all_list = ['token', "raison_sociale", "nom", "siret", "tva", "email",
                              "telephone", "website", "comment", "address",
                              'adr_adresse', 'adr_code_postal', 'adr_ville', 'adr_pays',
                              'invoice_email', 'invoice_nom', 'invoice_siret',
                              'invoice_tva', 'invoice_condition_paiement_id', 'invoice_adresse', 'invoice_ville',
                              'invoice_code_postal', 'invoice_pays', "client_type_id",
                              'is_fournisseur', 'is_client', 'is_financeur', 'is_client']

                # Pre Remplir les champs à vide
                for tmp in new_partner_all_list:
                    new_client_data[str(tmp)] = ""


                new_client_data['token'] = diction['token']
                new_client_contact_data['token'] = diction['token']

                if ("nom" in local_inscription_no_client.keys()):
                    new_client_data['raison_sociale'] = local_inscription_no_client['nom']
                    new_client_data['nom'] = local_inscription_no_client['nom']

                    # Pour le contact
                    new_client_contact_data['nom'] = local_inscription_no_client['nom']

                if ("prenom" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['prenom'] = local_inscription_no_client['prenom']

                if ("civilite" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['civilite'] = str(local_inscription_no_client['civilite']).lower()


                if( "email" in local_inscription_no_client.keys() ):
                    new_client_data['email'] = local_inscription_no_client['email']
                    new_client_data['invoice_email'] = local_inscription_no_client['email']

                    # Pour le contact
                    new_client_contact_data['email'] = local_inscription_no_client['email']

                if ("telephone" in local_inscription_no_client.keys()):
                    new_client_data['telephone'] = local_inscription_no_client['telephone']

                    # Pour le contact
                    new_client_contact_data['telephone'] = local_inscription_no_client['telephone']

                if ("adresse" in local_inscription_no_client.keys()):
                    new_client_data['adr_adresse'] = local_inscription_no_client['adresse']
                    new_client_data['invoice_adresse'] = local_inscription_no_client['adresse']

                    # Pour le contact
                    new_client_contact_data['adr_adresse'] = local_inscription_no_client['adresse']


                if("code_postal" in local_inscription_no_client.keys()):
                    new_client_data['adr_code_postal'] = local_inscription_no_client['code_postal']
                    new_client_data['invoice_code_postal'] = local_inscription_no_client['code_postal']

                    # Pour le contact
                    new_client_contact_data['adr_code_postal'] = local_inscription_no_client['code_postal']

                if ("ville" in local_inscription_no_client.keys()):
                    new_client_data['adr_ville'] = local_inscription_no_client['ville']
                    new_client_data['invoice_ville'] = local_inscription_no_client['ville']

                    # Pour le contact
                    new_client_contact_data['adr_ville'] = local_inscription_no_client['ville']

                if ("pays" in local_inscription_no_client.keys()):
                    new_client_data['adr_pays'] = local_inscription_no_client['pays']
                    new_client_data['invoice_pays'] = local_inscription_no_client['pays']

                    # Pour le contact
                    new_client_contact_data['adr_pays'] = local_inscription_no_client['pays']


                new_client_data['is_client'] = "1"
                new_client_data['is_company'] = "0"


                new_client_status, new_client_retval = partner_client.Add_Partner_Client(new_client_data)
                if (new_client_status is False):
                    mycommon.myprint(" Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ")
                    return False, " Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ", False



                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                # Créer le contact de communication du client
                new_client_contact_data['include_com'] = "1"
                new_client_contact_data['related_collection'] = "partner_client"
                new_client_contact_data['related_collection_owner_id'] = str(inscription_no_client_valide_no_locked_data['_id'])
                local_add_contact_status, local_add_contact_retval = Contact.Add_Contact(new_client_contact_data)
                if( local_add_contact_status is False ):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " WARNING : Impossible de créer le contact "+str(local_add_contact_retval))

                # Mise à jour de l'inscription avec l'_id du client
                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set': {'client_rattachement_id': str(
                                                             inscription_no_client_valide_no_locked_data['_id'])}})


        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("client_rattachement_id",
                                                                              {'session_id':str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                                  "client_rattachement_id": { '$ne': ''},
                                                                               "invoiced": {'$ne': '1'}
                                                                              }
                                                                              )


        print(" ### la liste des liste_client_rattachement_id = ", liste_client_rattachement_id)

        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer", False

            tab_saved_file_full_path.append(saved_file_full_path)

        print(" #### tab_saved_file_full_path = ", tab_saved_file_full_path)

        # Recupération des données du modèle de document
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )


        tab_local_invoice_ref_interne = []
        # Envoie des factures pour les inscrits AVEC client_id  (conventions d'entreprise)
        for single_client in liste_client_rattachement_id:
            print(" Traitement du client_id = ", single_client)

            # Recuperation des contacts de communication du client
            local_diction = {}
            local_diction['token'] = diction['token']
            local_diction['_id'] = str(single_client)

            print(" ##### local_diction = ", local_diction)
            local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(
                local_diction)

            if (local_status is False):
                mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                return False, " Impossible de récupérer les contacts de communication du client ", False

            print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
            tab_local_email_production = []
            for tmp in partner_client_contact_communication:
                tmp_JSON = ast.literal_eval(tmp)
                if ("email" in tmp_JSON.keys()):
                    tab_local_email_production.append(str(tmp_JSON["email"]))

            list_local_email_production = ",".join(tab_local_email_production)

            new_diction_client = {}
            new_diction_client['partner_client_id'] = single_client
            new_diction_client['token'] = diction['token']
            new_diction_client['courrier_template_id'] = diction['courrier_template_id']
            new_diction_client['email_test'] = diction['email_test']
            new_diction_client['email_production'] = str(list_local_email_production)
            new_diction_client['session_id'] = diction['session_id']

            print(" ##### new_diction_client 0102 = ", new_diction_client)


            # Creation de la facture du client
            diction_invoice = {}
            diction_invoice['token'] = diction['token']
            diction_invoice['partner_client_id'] = single_client
            diction_invoice['session_id'] = diction['session_id']
            #print(" ##### diction_invoice 0202 = ", diction_invoice)
            local_create_invoice_status, local_create_invoice_retval, local_invoice_ref_interne = Invoice_Partner_From_Session(diction_invoice)
            if( local_create_invoice_status is False ):
                return local_create_invoice_status, local_create_invoice_retval, False


            """
            Mettre à jour les lignes associées à ce client pour dire que la ligne est facturé
            """
            now = str(datetime.now())
            update_data = {}
            update_data['invoiced'] = "1"
            update_data['invoiced_ref'] = str(local_invoice_ref_interne)
            update_data['invoiced_date'] = now
            update_data['date_update'] = now
            update_data['update_by'] = str(my_partner['_id'])

            update = MYSY_GV.dbname['inscription'].update_many({'partner_owner_recid':str(my_partner['recid']),
                                                                'session_id':diction['session_id'],
                                                                'client_rattachement_id':str(single_client)},
                                                               {'$set':update_data})


            tab_local_invoice_ref_interne.append(str(local_invoice_ref_interne))

        """
                07/03/20204 : mettre un statut de facturation sur la session afin de voir
                tout de suite quel session est entièrement facturée ou partiellement.

                regles : 
                Si toutes les inscription associées à une session sont facturée ==> invoiced_statut de la session = 2
                Si au moins une inscription associée à une session est facturé ==> invoiced_statut de la session = 1
                Si non invoiced_statut de la session =0
                """
        nb_inscription_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': diction['session_id'],
             'invoiced': '1'})


        nb_inscription_non_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': diction['session_id'],
             'invoiced': {'$ne': '1'}})

        nb_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': diction['session_id'],
             'status': '1'})


        invoiced_statut = "0"
        if (nb_inscription_facture == nb_inscription_valide):
            # toutes les inscription valides ont été facturée
            invoiced_statut = "2"
        elif (nb_inscription_facture > 0):
            # Au moins une ligne a été facturée
            invoiced_statut = "1"

        # Mise à jour du statut de facturation de la session
        MYSY_GV.dbname['session_formation'].update_one({'partner_owner_recid': my_partner['recid'],
                                                        'valide': '1',
                                                        '_id': ObjectId(str(diction['session_id']))
                                                        },
                                                       {'$set': {'invoiced_statut': invoiced_statut}})


        # Creation de l'historique dans les action 'courrier_template_tracking_history'
        local_qry = {'partner_owner_recid':str(my_partner['recid']), 'session_id':str(diction['session_id']),
                                                                        'invoiced_ref': {'$in': tab_local_invoice_ref_interne} }

        #print(" ###  local_qry = ", local_qry)

        for val in MYSY_GV.dbname['inscription'].find({'partner_owner_recid':str(my_partner['recid']),
                                                       'session_id':str(diction['session_id']),
                                                                        'invoiced_ref': {'$in': tab_local_invoice_ref_interne} }):

            ref_facture = ""
            if( "invoiced_ref" in val.keys() ):
                ref_facture = val['invoiced_ref']

            local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                my_partner, "FACTURATION_SESSION", str(diction['session_id']), 'inscription',
                str(val['_id']),
                str(diction['courrier_template_id']),
                "Facture : "+str(ref_facture)
            )

            if( local_status is False ):
                mycommon.myprint(" WARNING : Impossible de logguer l'historique l'inscription_id : " + str(val['_id']))


        return_message = " La session a été correctement facturée.\nListe des factures : "
        for tmp in tab_local_invoice_ref_interne:
            return_message += "\n - "+str(tmp)

        return True, str(return_message), tab_local_invoice_ref_interne

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer et d'envoyer les factures par email ", False


"""
Cette fonction va créer une facture pour un client
lié à des apprenants sur une session de formation
"""
def Invoice_Partner_From_Session( diction):
    try:

        field_list_obligatoire = ['token',  'partner_client_id', 'session_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        # Verifier que la session est valide
        is_session_id_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_id_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session est invalide ")
            return False, "  L'identifiant de la session est invalide ", False

        # Verifier que ce client a bien des inscriptions valide pour cette session
        nb_valide_inscription_pr_client = MYSY_GV.dbname['inscription'].count_documents(
            {'facture_client_rattachement_id': str(diction['partner_client_id']),
             'session_id': str(diction['session_id']),
             'partner_owner_recid': str(my_partner['recid']),
             'status': '1'})

        if (nb_valide_inscription_pr_client <= 0):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  Aucune inscription valide pour ce client pour cette session  ")
            return False, "  Aucune inscription valide pour ce client pour cette session ", False

        partner_client_id_data = MYSY_GV.dbname['partner_client'].find_one(
            {'_id': ObjectId(str(diction['partner_client_id'])),
             'valide': '1',
             'locked': '0',
             'partner_recid': str(my_partner['recid'])})


        # Recuperation des données du stagiaire
        inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                   'status': '1',
                                                                   'partner_owner_recid': str(my_partner['recid']),
                                                                   'facture_client_rattachement_id':str(diction['partner_client_id'])})
        tab_apprenant = []
        tab_participant = []
        for val in inscription_data:
            tab_participant.append(val['_id'])

            """
                    update du 30/01/2024 
                    si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
                    En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
                    Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
                    """

            if ("apprenant_id" in val.keys() and val['apprenant_id']):
                tab_apprenant.append(ObjectId(str(val['apprenant_id'])))


        print(" ### tab_participant = ", tab_participant)

        # Recuperations des info de la session de formation
        session_data = MYSY_GV.dbname['session_formation'].find_one({'_id': ObjectId(str(diction['session_id']))})

        tab_session = []
        tab_session.append(session_data['_id'])


        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find({'internal_url': str(session_data['class_internal_url']),
                                                     'valide': '1',
                                                     'partner_owner_recid': str(my_partner['recid']),
                                                     'locked': '0'})


        price_by = "perstagiaire"
        if( "perstagiaire" in session_data.keys() ):
            price_by = session_data['perstagiaire']
            if( price_by not in MYSY_GV.TRAINING_PRICE) :
                mycommon.myprint(str(inspect.stack()[0][3]) + " Le prix par " + str(price_by) + " n'est valide. Les valeurs autorisées sont " + str(MYSY_GV.TRAINING_PRICE))
                return False, " Le prix par " + str(price_by) + " n'est valide. Les valeurs autorisées sont " + str(MYSY_GV.TRAINING_PRICE) + " ", False

        partner_invoice_header_data = {}

        list_partner_invoice_header_champ =  ['order_header_client_id', 'order_header_ref_interne', 'order_header_email_client', 'order_header_origin', 'order_header_ref_client', 'order_header_vendeur_id',
          'order_header_date_cmd', 'order_header_date_expiration', 'order_header_adr_fact_adresse', 'order_header_adr_fact_code_postal', 'order_header_adr_fact_ville', 'order_header_adr_fact_pays',
          'order_header_adr_liv_adresse', 'order_header_adr_liv_code_postal', 'order_header_adr_liv_ville', 'order_header_adr_liv_pays', 'valide', 'locked', 'date_update',
          'order_header_montant_reduction', 'order_header_tax', 'order_header_tax_amount', 'total_header_hors_taxe_after_header_reduction', 'total_header_hors_taxe_before_header_reduction',
          'total_header_toutes_taxes', 'total_lines_hors_taxe_after_lines_reduction', 'total_lines_hors_taxe_before_lines_reduction', 'total_lines_montant_reduction', 'invoice_header_ref_interne',
          'invoice_header_type', 'invoice_date', 'update_by']

        # PreRemplir les champs
        for val in list_partner_invoice_header_champ:
            partner_invoice_header_data[str(val)] = ""


        partner_invoice_header_data['order_header_client_id'] = str(partner_client_id_data['_id'])

        """
                Recuperation des conditions de paiement depuis le client
                """
        ction_paiement_code = ""
        ction_paiement_desc = ""
        ction_paiement_depart = "facture"
        ction_paiement_nb_jour = "0"

        if ("invoice_condition_paiement_id" in partner_client_id_data.keys() and partner_client_id_data[
            'invoice_condition_paiement_id']):
            ction_paiement_data = MYSY_GV.dbname['base_partner_paiement_condition'].find_one(
                {'partner_owner_recid': my_partner['recid'],
                 'valide': '1',
                 'locked': '0',
                 '_id': ObjectId(str(partner_client_id_data['invoice_condition_paiement_id']))})

            if (ction_paiement_data and "code" in ction_paiement_data.keys() and ction_paiement_data['code']):
                ction_paiement_code = ction_paiement_data['code']

            if (ction_paiement_data and "description" in ction_paiement_data.keys() and ction_paiement_data[
                'description']):
                ction_paiement_desc = ction_paiement_data['description']

            if (ction_paiement_data and "nb_jour" in ction_paiement_data.keys() and ction_paiement_data['nb_jour'] and
                    "depart" in ction_paiement_data.keys() and ction_paiement_data['depart']):
                ction_paiement_nb_jour = ction_paiement_data['nb_jour']
                ction_paiement_depart = ction_paiement_data['depart']

        nb_jour_int = mycommon.tryInt(str(ction_paiement_nb_jour))
        today = datetime.today()
        date_echance = datetime.today()

        if (str(ction_paiement_depart) == "mois"):
            days_in_month = lambda dt: monthrange(dt.year, dt.month)[1]
            first_day_next_month = today.replace(day=1) + timedelta(days_in_month(today))
            date_echance = first_day_next_month + timedelta(days=nb_jour_int)

        else:
            date_echance = today + timedelta(days=nb_jour_int)

        date_echance = date_echance.strftime("%d/%m/%Y")
        partner_invoice_header_data['invoice_date_echeance'] = str(date_echance)
        partner_invoice_header_data['order_header_condition_paiement_code'] = str(ction_paiement_code)
        partner_invoice_header_data['order_header_condition_paiement_description'] = str(ction_paiement_desc)


        code_session = ""
        if( "code_session" in session_data.keys() ):
            code_session = session_data['code_session']
        partner_invoice_header_data['order_header_ref_interne'] = "Code_Session_"+str(code_session)

        order_header_email_client = ""
        if ("email" in partner_client_id_data.keys()):
            order_header_email_client = partner_client_id_data['email']
        partner_invoice_header_data['order_header_email_client'] = order_header_email_client

        order_header_origin = "session_id_"+str(session_data['_id'])
        partner_invoice_header_data['order_header_origin'] = order_header_origin

        order_header_adr_fact_adresse = ""
        if( "invoice_adresse" in partner_client_id_data.keys() ):
            order_header_adr_fact_adresse = partner_client_id_data['invoice_adresse']
        partner_invoice_header_data['order_header_adr_fact_adresse'] = order_header_adr_fact_adresse

        order_header_adr_fact_ville = ""
        if ("invoice_ville" in partner_client_id_data.keys()):
            order_header_adr_fact_ville = partner_client_id_data['invoice_adresse']
        partner_invoice_header_data['order_header_adr_fact_ville'] = order_header_adr_fact_ville

        order_header_adr_fact_code_postal = ""
        if ("invoice_code_postal" in partner_client_id_data.keys()):
            order_header_adr_fact_code_postal = partner_client_id_data['invoice_code_postal']
        partner_invoice_header_data['order_header_adr_fact_code_postal'] = order_header_adr_fact_code_postal

        order_header_adr_fact_pays = ""
        if ("invoice_pays" in partner_client_id_data.keys()):
            order_header_adr_fact_pays = partner_client_id_data['invoice_pays']
        partner_invoice_header_data['order_header_adr_fact_pays'] = order_header_adr_fact_pays

        order_header_montant_reduction = "0"
        partner_invoice_header_data['order_header_montant_reduction'] = order_header_montant_reduction


        # Calcul du Totol HT sans reduction
        total_ht = 0
        prix_session = 0
        session_price = 0
        if( "prix_session" not in session_data.keys() ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : La session n'a pas de prix valide")
            return False, " Facturation : La session n'a pas de prix valide ", False

        if( str(session_data['prix_session']).strip() == "" ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : La session n'a pas de prix valide (2) ")
            return False, " Facturation : La session n'a pas de prix valide (2) ", False

        prix_session = mycommon.tryFloat(str(session_data['prix_session']))


        if( str(price_by).strip() == "persession" ):
            total_ht = round(prix_session, 2)
        else:
            total_ht = round(prix_session * nb_valide_inscription_pr_client, 2)

        partner_invoice_header_data['total_header_hors_taxe_before_header_reduction'] = total_ht

        # Recupération de la TVA de l'entité qui facture
        taux_tva_statuts, taux_tva_retval = partner_base_setup.Get_Given_Partner_Basic_Setup({'token':str(diction['token']), 'config_name':'tva'})

        if( taux_tva_statuts is False ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : Impossible de récupérer le taux de TVA ")
            return False, " Facturation : Impossible de récupérer le taux de TVA ", False

        tmp =  ast.literal_eval(taux_tva_retval[0])
        taux_tva_retval = tmp['config_value']
        print(" ### taux_tva_retval =  ", taux_tva_retval)
        tva_status, tva_value = mycommon.IsFloat(str(taux_tva_retval))
        if (tva_status is False):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : Le taux de TVA est invalide ")
            return False, " Facturation : Le taux de TVA est invalide ", False

        partner_invoice_header_data['order_header_tax'] = taux_tva_retval
        partner_invoice_header_data['order_header_tax_amount'] = str(round(tva_value * total_ht/100, 2))
        partner_invoice_header_data['total_header_toutes_taxes'] = str(round(total_ht + (tva_value * total_ht)/100, 2))
        partner_invoice_header_data['invoice_header_type'] = "facture"

        # Récuperation de la sequence de l'objet "partner_invoice_header" dans la collection : "mysy_sequence"
        retval_sequence_invoice = MYSY_GV.dbname['mysy_sequence'].find_one(
            {'partner_invoice_header': 'partner_order_header',
             'valide': '1', 'partner_owner_recid': str(
                my_partner['recid'])})

        if (retval_sequence_invoice is None):
            # Il n'y pas de sequence pour le partenaire, on va aller chercher la sequence par defaut
            retval_sequence_invoice = MYSY_GV.dbname['mysy_sequence'].find_one(
                {'related_mysy_object': 'partner_invoice_header',
                 'valide': '1', 'partner_owner_recid': 'default'})

        if (retval_sequence_invoice is None or "current_val" not in retval_sequence_invoice.keys()):
            # Il n'y aucune sequence meme par defaut.

            mycommon.myprint(" Facture : Impossible de récupérer la sequence 'retval_sequence_invoice' ")
            return False, "Facture : Impossible de récupérer la sequence 'retval_sequence_invoice' ", False

        current_seq_value = str(retval_sequence_invoice['current_val'])
        new_sequence_value = int(mycommon.tryInt(current_seq_value)) + 1
        new_sequance_data_to_update = {'current_val': new_sequence_value}

        ret_val2 = MYSY_GV.dbname['mysy_sequence'].find_one_and_update(
            {'_id': ObjectId(str(retval_sequence_invoice['_id'])), 'valide': '1'},
            {"$set": new_sequance_data_to_update},
            return_document=ReturnDocument.AFTER,
            upsert=False,
        )
        invoice_date_time = str(datetime.now().strftime("%d/%m/%Y"))


        """
        Verifier qu'il n'y pas une facture du partenaire avec le meme ref interne
        """
        is_already_invoice_ref_exist = MYSY_GV.dbname['partner_invoice_header'].count_documents({'partner_invoice_header':str(my_partner['recid']),
                                                                                                 'valide':'1',
                                                                                                 'invoice_header_ref_interne':str(retval_sequence_invoice['prefixe'] + str(current_seq_value))})

        if( is_already_invoice_ref_exist > 0 ):
            mycommon.myprint(" Facture : Il existe déjà une facture avec la même ref. interne : "+str(retval_sequence_invoice['prefixe'] + str(current_seq_value)))
            return False, " Facture : Il existe déjà une facture avec la même ref. interne : "+str(retval_sequence_invoice['prefixe'] + str(current_seq_value)), False


        partner_invoice_header_data['invoice_header_ref_interne'] = retval_sequence_invoice['prefixe'] + str(current_seq_value)
        partner_invoice_header_data['invoice_header_type'] = "facture"
        partner_invoice_header_data['invoice_date'] = invoice_date_time
        partner_invoice_header_data['update_by'] = str(my_partner['_id'])
        partner_invoice_header_data['valide'] = "1"
        partner_invoice_header_data['locked'] = "0"
        partner_invoice_header_data['partner_owner_recid'] = str(my_partner['recid'])
        partner_invoice_header_data['date_update'] = str(datetime.now())


        print(" #### partner_invoice_header_data = ", partner_invoice_header_data)
        inserted_invoice_id = MYSY_GV.dbname['partner_invoice_header'].insert_one(partner_invoice_header_data).inserted_id
        if (not inserted_invoice_id):
            mycommon.myprint(" Facture : Impossible de créer l'entête de la facture ")
            return False, " Facture : Impossible de créer l'entête de la facture ", False


        """
        Création des lignes de facture.
        Pour memo, dans la collection : partner_invoice_line
        order_line_formation = titre formation
        order_line_qty = nb participants
        order_line_comment =  la liste des personnes participans
        """

        partner_invoice_line_data = {}
        list_partner_invoice_line_champ = ['order_line_formation', 'order_line_qty', 'order_line_prix_unitaire', 'order_line_tax', 'order_line_tax_amount', 'order_line_montant_toutes_taxes',
             'order_line_montant_hors_taxes', 'order_line_type_reduction', 'order_line_type_valeur', 'order_line_montant_reduction', 'order_header_ref_interne',
             'order_line_comment', 'order_header_id', 'valide', 'locked', 'date_update', 'partner_owner_recid', 'invoice_header_ref_interne', 'invoice_line_type',
             'invoice_date', 'invoice_header_id']


        # PreRemplir les champs
        for val in list_partner_invoice_line_champ:
            partner_invoice_line_data[str(val)] = ""

        nb_participant_du_client = len(tab_apprenant)

        nom_prenom_email_participant = ""
        for val in tab_apprenant:
            local_apprenant = MYSY_GV.dbname['apprenant'].find_one({'_id':val, 'partner_owner_recid':str(my_partner['recid']),
                                                                    'valide':'1', 'locked':'0'})

            local_nom = ""
            local_prenom = ""
            local_email = ""
            if( "nom" in local_apprenant.keys() ):
                local_nom = local_apprenant['nom']

            if ("prenom" in local_apprenant.keys()):
                local_prenom = local_apprenant['prenom']

            if ("email" in local_apprenant.keys()):
                local_email = local_apprenant['email']

            nom_prenom_email_participant += local_nom+" "+local_prenom+" "+local_email+"\n"

        partner_invoice_line_data['order_line_formation'] = class_data[0]['internal_url']
        partner_invoice_line_data['order_line_qty'] = str(nb_participant_du_client)
        partner_invoice_line_data['order_line_prix_unitaire'] = str(prix_session)
        partner_invoice_line_data['order_line_montant_hors_taxes'] = str(total_ht)
        partner_invoice_line_data['order_line_comment'] = str(nom_prenom_email_participant)
        partner_invoice_line_data['invoice_header_id'] = str(inserted_invoice_id)
        partner_invoice_line_data['invoice_line_type'] = "facture"
        partner_invoice_line_data['invoice_header_ref_interne'] = partner_invoice_header_data['invoice_header_ref_interne']
        partner_invoice_line_data['update_by'] = str(my_partner['_id'])
        partner_invoice_line_data['valide'] = "1"
        partner_invoice_line_data['locked'] = "0"
        partner_invoice_line_data['partner_owner_recid'] = str(my_partner['recid'])

        print(" #### partner_invoice_line_data = ", partner_invoice_line_data)
        inserted_invoice_id = MYSY_GV.dbname['partner_invoice_line'].insert_one(
            partner_invoice_line_data).inserted_id
        if (not inserted_invoice_id):
            mycommon.myprint(" Facture : Impossible de créer les lignes  de la facture "+str(partner_invoice_header_data['invoice_header_ref_interne']))
            return False, " Facture : Impossible de créer les lignes  de la facture "+str(partner_invoice_header_data['invoice_header_ref_interne']), False


        return True, "L'email a été correctement envoyé ", str(partner_invoice_header_data['invoice_header_ref_interne'])

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer la convention par email ", False





"""
Envoie par email pour les stagiaires rattachés à un client

important : 
si le champ 'email_test' est rempli, alors il s'agit d'un email de test.
donc on n'envoie pas l'email à l'adresss de prod ou contact du client
"""
def Sent_Facture_Stagiaire_By_Email_By_Partner_client(tab_files_name_full_path, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'courrier_template_id', 'email_test', 'email_production', 'partner_client_id', 'session_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste"

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        # Verifier que la session est valide
        is_session_id_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_id_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session est invalide ")
            return False, "  L'identifiant de la session est invalide "

        # Verifier que ce client a bien des inscriptions valide pour cette session
        is_valide_inscription_pr_client = MYSY_GV.dbname['inscription'].count_documents(
            {'facture_client_rattachement_id': str(diction['partner_client_id']),
             'session_id': str(diction['session_id']),
             'partner_owner_recid': str(my_partner['recid']),
             'status': '1'})

        if (is_valide_inscription_pr_client <= 0):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  Aucune inscription valide pour ce client pour cette session  ")
            return False, "  Aucune inscription valide pour ce client pour cette session "

        partner_client_id_data = MYSY_GV.dbname['partner_client'].find_one(
            {'_id': ObjectId(str(diction['partner_client_id'])),
             'valide': '1',
             'locked': '0',
             'partner_recid': str(my_partner['recid'])})


        # Traitement de l'eventuel fichier joint
        tab_files_to_attache_to_mail = []

        for file_name_full_path in tab_files_name_full_path:
            """status, saved_file = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer"
            """

            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(file_name_full_path, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(file_name_full_path)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

        # Verification de la validité des adresses email_recu
        """
        /!\ : Si l'email de test est repli, alors on considere que c'est un test, on ne prend pas en compte l'email de email_production.
        Ceci pour forcer les utilisateur à ne remplir que l'email de prod s'il veulent l'envoyer en prod.

        Si l'adresse email_prodution = "defaul", cela veut dire qu'on envoie la convention à :
        - l'adresse email du stagiaire et ses tuteurs (si les tuteurs on cochés la case 'inclu com'

        """

        send_in_production = 0

        tab_emails_destinataire = []
        if ("email_test" in diction.keys() and diction['email_test']):
            tab_email_test = str(diction['email_test']).replace(";", ",").split(",")
            for email in tab_email_test:
                email = email.strip()
                if (mycommon.isEmailValide(email) is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][
                                3]) + " L'adresse email " + str(email) + " est invalide ")
                    return False, " L'adresse email " + str(email) + " est invalide "
            tab_emails_destinataire = tab_email_test

        elif ("email_production" in diction.keys() and diction['email_production']):
            send_in_production = 1
            if (str(diction['email_production']) != "default"):
                tab_email_prod = str(diction['email_production']).replace(";", ",").split(",")
                for email in tab_email_prod:
                    email = email.strip()
                    if (mycommon.isEmailValide(str(email)) is False):
                        mycommon.myprint(
                            str(inspect.stack()[0][
                                    3]) + " L'adresse email '" + str(email) + "' est invalide ")
                        return False, " L'adresse email " + str(email) + " est invalide "
                tab_emails_destinataire = tab_email_prod
            else:
                tab_email_prod = "default"


        else:
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + " Aucune adresse email n'a été fourni. ")
            return False, "  Aucune adresse email n'a été fourni. "


        #print(" ## laaa : tab_emails_destinataire lalala = ", tab_emails_destinataire)

        # Verifier que le 'courrier_template_id' est valide
        # Ici le template doit etre un email
        is_courrier_template_id_valide = MYSY_GV.dbname['courrier_template'].count_documents(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'type_doc': 'email',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
            )

        if (is_courrier_template_id_valide != 1):
            mycommon.myprint(str(inspect.stack()[0][3]) + "  L'identifiant du modèle de courrier est invalide ")
            return False, "  L'identifiant du modèle de courrier est invalide "

        # Recupération des données du modèle de document
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        # Recuperation des eventuelles pièces jointes du modèle que courrier
        local_dic = {}
        local_dic['token'] = str(diction['token'])
        local_dic['object_owner_collection'] = "courrier_template"
        local_dic['object_owner_id'] = str(courrier_template_data['_id'])

        local_status, local_retval = attached_file_mgt.Get_List_object_owner_collection_Stored_Files(local_dic)
        if (local_status is False):
            return local_status, local_retval

        # print(" ###  file stocké = ", local_retval)

        # Recuperation des fichiers attachés au modele de courrier, s'il y en a

        for file in local_retval:
            local_JSON = ast.literal_eval(file)

            saved_file = local_JSON['full_path']

            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(saved_file, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(saved_file)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

        # Recuperation des données du stagiaire
        inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                   'status': '1',
                                                                   'partner_owner_recid': str(my_partner['recid']),
                                                                   'facture_client_rattachement_id':str(diction['partner_client_id'])})
        tab_apprenant = []
        tab_participant = []
        for val in inscription_data:
            tab_participant.append(val['_id'])

            """
                    update du 30/01/2024 
                    si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
                    En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
                    Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
                    """

            if ("apprenant_id" in val.keys() and val['apprenant_id']):
                tab_apprenant.append(ObjectId(str(val['apprenant_id'])))


        print(" ### tab_participant = ", tab_participant)

        # Recuperations des info de la session de formation
        session_data = MYSY_GV.dbname['session_formation'].find_one({'_id': ObjectId(str(diction['session_id']))})

        tab_session = []
        tab_session.append(session_data['_id'])


        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find({'internal_url': str(session_data['class_internal_url']),
                                                     'valide': '1',
                                                     'partner_owner_recid': str(my_partner['recid']),
                                                     'locked': '0'})

        tab_class = []
        for val in class_data:
            tab_class.append(val['_id'])

        # Recuperer les données du client
        client_data = MYSY_GV.dbname['partner_client'].find_one({'_id':ObjectId(str(diction['partner_client_id'])),
                                                                 'valide':'1',
                                                                 'locked':'0',
                                                                 'partner_recid': str(my_partner['recid']),
                                                                 })


        tab_client = []
        tab_client.append(client_data['_id'])

        # Creation du dictionnaire d'information à utiliser pour la creation du doc
        convention_dictionnary_data = {}
        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = tab_participant
        new_diction['list_session_id'] = tab_session
        new_diction['list_class_id'] = tab_class
        new_diction['list_client_id'] = tab_client

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)

        if (local_status is False):
            return local_status, local_retval

        convention_dictionnary_data = local_retval


        ## Creation du PDF

        body = {
            "params": convention_dictionnary_data,
        }

        # ---
        # Verifier s'il s'agit d'un document à envoyer avec une version de pièce jointe.
        if ("joint_pdf" in courrier_template_data.keys() and str(courrier_template_data['joint_pdf']) == "1"):
            # Il s'agit bien d'un envoie avec 'contenu_doc' en pièce jointe PDF
            """
            1 - Creation du PDF
            """
            contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))

            sourceHtml = contenu_doc_Template.render(params=body["params"])

            todays_date = str(date.today().strftime("%d/%m/%Y"))
            ts = datetime.now().timestamp()
            ts = str(ts).replace(".", "").replace(",", "")[-5:]

            orig_file_name = "Convention_" + str(my_partner['recid'])[0:5] + "_" + str(ts) + ".pdf"
            outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name)

            # open output file for writing (truncated binary)
            resultFile = open(outputFilename, "w+b")

            # convert HTML to PDF
            pisaStatus = pisa.CreatePDF(
                src=sourceHtml,  # the HTML to convert
                dest=resultFile)  # file handle to receive result

            # close output file
            resultFile.close()

            # Attachement du fichier joint
            file_to_attache_to_mail = MIMEBase('application', "octet-stream")
            file_to_attache_to_mail.set_payload(open(outputFilename, "rb").read())

            encoders.encode_base64(file_to_attache_to_mail)
            file_to_attache_to_mail.add_header('Content-Disposition',
                                               'attachment; filename="{0}"'.format(os.path.basename(outputFilename)))

            new_node = {"attached_file": file_to_attache_to_mail}
            tab_files_to_attache_to_mail.append(new_node)

            ## Creation du  mail au format email

            corps_mail_Template = jinja2.Template(str(courrier_template_data['corps_mail']))

            sourceHtml = corps_mail_Template.render(params=body["params"])

            html_mime = MIMEText(sourceHtml, 'html')

            # Creation de l'email à enoyer
            msg = MIMEMultipart("alternative")

        else:
            # Il s'agit d'une simple email

            ## Creation du  mail au format email
            contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))

            sourceHtml = contenu_doc_Template.render(params=body["params"])

            html_mime = MIMEText(sourceHtml, 'html')

            # Creation de l'email à enoyer
            msg = MIMEMultipart("alternative")

        # ---

        """
        ## Creation du PDF

        contenu_doc_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))

        sourceHtml = contenu_doc_Template.render(params=body["params"])

        html_mime = MIMEText(sourceHtml, 'html')

        # Creation de l'email à enoyer
        msg = MIMEMultipart("alternative")
        """
        """
                Recuperation des parametre SMTP du partner si le client a decidé d'utiliser son propre smpt
                """
        partner_own_smtp_value = "0"
        partner_own_smtp = MYSY_GV.dbname['base_partner_setup'].find_one(
            {'partner_owner_recid': str(my_partner['recid']),
             'config_name': 'partner_smtp',
             'valide': '1',
             'locked': '0'})

        if (partner_own_smtp and "config_value" in partner_own_smtp.keys()):
            partner_own_smtp_value = partner_own_smtp['config_value']

            if (str(partner_own_smtp_value) == "1"):
                partner_SMTP_COUNT_password = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_user_pwd',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_smtpsrv = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_server',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_user = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_user',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_From_User = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_count_from_name',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

                partner_SMTP_COUNT_port = str(MYSY_GV.dbname['base_partner_setup'].find_one(
                    {'partner_owner_recid': str(my_partner['recid']),
                     'config_name': 'smtp_count_port',
                     'valide': '1',
                     'locked': '0'}, {'config_value': 1})['config_value'])

        if (str(partner_own_smtp_value) == "1"):
            smtpserver = smtplib.SMTP(partner_SMTP_COUNT_smtpsrv, partner_SMTP_COUNT_port)
        else:
            smtpserver = smtplib.SMTP(MYSY_GV.O365_SMTP_COUNT_smtpsrv, MYSY_GV.O365_SMTP_COUNT_port)

        if (str(partner_own_smtp_value) == "1"):
            msg.attach(html_mime)
            msg['From'] = partner_SMTP_COUNT_From_User
            msg['Bcc'] = 'contact@mysy-training.com'
            msg['Subject'] = courrier_template_data['sujet']
            # msg['to'] = "billardman01@hotmail.com"
            toaddrs = ", ".join(tab_emails_destinataire)
            msg['to'] = str(toaddrs)

            # Attacher l'eventuelle pièces jointes
            for myfile in tab_files_to_attache_to_mail:
                msg.attach(myfile['attached_file'])

            smtpserver.ehlo()
            smtpserver.starttls()
            smtpserver.login(partner_SMTP_COUNT_user, partner_SMTP_COUNT_password)

        else:
            msg.attach(html_mime)
            msg['From'] = MYSY_GV.O365_SMTP_COUNT_From_User
            msg['Bcc'] = 'contact@mysy-training.com'
            msg['Subject'] = courrier_template_data['sujet']
            # msg['to'] = "billardman01@hotmail.com"
            toaddrs = ", ".join(tab_emails_destinataire)
            msg['to'] = str(toaddrs)

            for myfile in tab_files_to_attache_to_mail:
                msg.attach(myfile['attached_file'])

            smtpserver.ehlo()
            smtpserver.starttls()
            smtpserver.login(MYSY_GV.O365_SMTP_COUNT_user, MYSY_GV.O365_SMTP_COUNT_password)

        val = smtpserver.send_message(msg)
        smtpserver.close()
        print(" Email envoyé " + str(val))

        """
       25/01/2024 : pour loger une action dans la collection  ==> courrier_template_tracking_history
       """
        # Recuperation des données du stagiaire
        inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                               'status': '1',
                                                               'partner_owner_recid': str(my_partner['recid']),
                                                               'facture_client_rattachement_id': str(
                                                                   diction['partner_client_id'])})

        for inscription in inscription_data:

            local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                my_partner, "CONVENTION_STAGIAIRE_ENTREPRISE", str(diction['session_id']), 'inscription',
                str(inscription['_id']), str(courrier_template_data['_id']))

            #print(" local_status = ", local_status)


        """
                # Ajout de l'evenement dans l'historique
                """

        # L'action n'est loggué pour les envois reels (en prod)
        if (send_in_production == 1):
            now = str(datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
            history_event_dict = {}
            history_event_dict['token'] = diction['token']
            history_event_dict['related_collection'] = "partner_client"
            history_event_dict['related_collection_recid'] = str(diction['partner_client_id'])
            history_event_dict['action_date'] = str(now)
            history_event_dict['action_description'] = "Convention envoyée par email à la liste : " + str(
                tab_emails_destinataire)
            local_status, local_retval = mycommon.Collection_Historique.Add_Historique_Event(history_event_dict)
            if (local_status is False):
                mycommon.myprint(
                    " WARNING : Impossible de logguer l'historique pour l'évènement : " + str(history_event_dict))

        return True, "L'email a été correctement envoyé "

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer la convention par email "




"""
Cette fonction ne facture uniquement et exclusivement les lignes d'inscriptions sur lesquels
il ya une demande d'éclatement de la facture
C'est a dire que ligne d'inscription dispose d'un champ : "invoice_split" qui est valide.
Pour etre valide, ce champs est de type : 
'invoice_split':{split_type : fixe/percent, tab_split : [{partner_client:cccc, invoice_part:40}, {partner_client:eeeeee, invoice_part:60} ....] }},

"""
def Invoice_Inscrption_With_Split_Session_By_Inscription_Id( tab_files, diction):
    try:

        field_list_obligatoire = ['token',   'session_id', 'tab_inscription_ids']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        # Verifier que la session est valide
        is_session_id_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_id_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session est invalide ")
            return False, "  L'identifiant de la session est invalide ", False



        my_inscription_ids = ""
        if ("tab_inscription_ids" in diction.keys()):
            if diction['tab_inscription_ids']:
                my_inscription_ids = diction['tab_inscription_ids']

        if( str(my_inscription_ids) == "all"):
            # Recuperation des données du stagiaire
            inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                   'status': '1',
                                                                   'partner_owner_recid': str(my_partner['recid']),
                                                                   "invoiced": {'$ne': '1'}, })


        else:
            tab_my_inscription_ids = str(my_inscription_ids).split(",")

            tab_my_inscription_ids_ObjectId = []
            for tmp in tab_my_inscription_ids :
                tab_my_inscription_ids_ObjectId.append(ObjectId(str(tmp)))


            # Recuperation des données du stagiaire
            inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                       'status': '1',
                                                                       'partner_owner_recid': str(my_partner['recid']),
                                                                        '_id':{'$in':tab_my_inscription_ids_ObjectId},
                                                                    "invoiced": {'$ne': '1'},})

        print(" ###  inscription_data = ", inscription_data)

        tab_inscrit_for_splited_invoice = []
        tab_inscrit_for_NOT_splited_invoice = []
        tab_apprenant = []
        tab_participant = []
        for val in inscription_data:
            if( "invoice_split" in val.keys() and val['invoice_split'] ):
                tab_participant.append(val['_id'])

                node = {}
                node['inscription_id'] = str(val['_id'])


                """
                        update du 30/01/2024 
                        si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
                        En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
                        Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
                        """

                if ("apprenant_id" in val.keys() and val['apprenant_id']):
                    tab_apprenant.append(ObjectId(str(val['apprenant_id'])))
                    node['apprenant_id'] = str(val['apprenant_id'])
                else:
                    node['apprenant_id'] = ""

                if( "invoice_split" in val.keys() ):
                    node['invoice_split'] = val['invoice_split']

                else:
                    node['invoice_split'] = ""


                # Verifier que le mode d'eclatement de la factue est soit : percent, soit fixe
                if( "invoice_split" in val.keys() and val['invoice_split'] and
                "split_type" in val['invoice_split'].keys() and val['invoice_split']['split_type']):
                    if( str(val['invoice_split']['split_type']).lower() not in ['percent', 'fixe'] ):
                        mycommon.myprint(
                            str(inspect.stack()[0][
                                    3]) + " Le mode de partage de la facture doit être 'percent' ou 'fixe' ")
                        return False, "   Le mode de partage de la facture doit être 'percent' ou 'fixe'  ", False

                if ("invoice_split" in val.keys() and val['invoice_split'] and
                        "tab_split" in val['invoice_split'].keys() and val['invoice_split']['tab_split']):

                    for tmp in val['invoice_split']['tab_split'] :
                        if("invoice_part" in tmp.keys() ):
                            is_float_status, is_float_retaval = mycommon.IsFloat(str(tmp['invoice_part']))

                            if( is_float_status is False ):
                                mycommon.myprint(
                                    str(inspect.stack()[0][
                                            3]) + " La valeur de partage de la facture n'est pas un nombre decimal ")
                                return False, "  La valeur de partage de la facture n'est pas un nombre decimal   ", False

                            elif ( is_float_retaval < 0 ):
                                mycommon.myprint(
                                    str(inspect.stack()[0][
                                            3]) + " La valeur de partage de la facture est inférieure à 0  ")
                                return False, "  La valeur de partage de la facture est inférieure à 0    ", False

                tab_inscrit_for_splited_invoice.append(node)

            else:
                node = {}
                node['inscription_id'] = str(val['_id'])
                if ("apprenant_id" in val.keys() and val['apprenant_id']):
                    node['apprenant_id'] = str(val['apprenant_id'])
                else:
                    node['apprenant_id'] = ""

                tab_inscrit_for_NOT_splited_invoice.append(node)


        print(" ### La liste des tab_inscrit_for_splited_invoice ", tab_inscrit_for_splited_invoice)
        print(" ### La liste des tab_inscrit_for_NOT_splited_invoice ", tab_inscrit_for_NOT_splited_invoice)
        print(" ### tab_apprenant = ", tab_apprenant)



        """
        Creation du diction pour la facture SANS SPLIT de facture
        """
        local_tmp_tab = []
        list_non_splited_invoice = []
        no_split_tab_inscription_ids = ""
        for tmp in tab_inscrit_for_NOT_splited_invoice:
            if( "inscription_id" in tmp.keys() ):
                local_tmp_tab.append(tmp['inscription_id'])

        no_split_tab_inscription_ids = ",".join(local_tmp_tab)

        local_diction_for_NOT_INVOICE_SPLIT = {}
        local_diction_for_NOT_INVOICE_SPLIT['tab_inscription_ids'] = no_split_tab_inscription_ids
        local_diction_for_NOT_INVOICE_SPLIT['token'] = diction['token']
        local_diction_for_NOT_INVOICE_SPLIT['session_id'] = diction['session_id']
        local_diction_for_NOT_INVOICE_SPLIT['courrier_template_id'] = ""
        local_diction_for_NOT_INVOICE_SPLIT['email_test'] = ""
        local_diction_for_NOT_INVOICE_SPLIT['email_production'] = ""

        print(" ### local_diction_for_NOT_INVOICE_SPLIT = ", local_diction_for_NOT_INVOICE_SPLIT)
        status, retval, invoice_ref = Prepare_and_Send_Facture_From_Session_By_Inscription_Id_SAVE_ORIG(tab_files,
                                                                                                 MYSY_GV.TEMPORARY_DIRECTORY_V2,
                                                                                                 local_diction_for_NOT_INVOICE_SPLIT)

        print(" ### status =  ", status)
        print(" ### retval =  ", retval)
        print(" ### local_diction_for_NOT_INVOICE_SPLIT invoice_ref =  ", invoice_ref)
        for tmp in invoice_ref:
            list_non_splited_invoice.append(tmp)

        list_non_splited_invoice_str =   ', '.join(invoice_ref)
        print(" ### BBBB list_non_splited_invoice_str  =  ", list_non_splited_invoice_str)

        global_list_facture = list_non_splited_invoice_str

        list_splited_invoice = []
        """
          Creation du diction pour la facture AVEC SPLIT de facture
          """
        for tmp in tab_inscrit_for_splited_invoice:
            local_diction_for_WITH_INVOICE_SPLIT = {}
            local_diction_for_WITH_INVOICE_SPLIT['token'] = diction['token']
            local_diction_for_WITH_INVOICE_SPLIT['session_id'] = diction['session_id']
            local_diction_for_WITH_INVOICE_SPLIT['inscription_id'] = tmp['inscription_id']

            print(" ### local_diction_for_WITH_INVOICE_SPLIT = ", local_diction_for_WITH_INVOICE_SPLIT)
            status, retval, invoice_ref = Invoice_Splited_Partner_From_Session_By_Inscription_Id( local_diction_for_WITH_INVOICE_SPLIT)

            print(" ### status =  ", status)
            print(" ### retval =  ", retval)
            print(" ### local_diction_for_WITH_INVOICE_SPLIT invoice_ref =  ", invoice_ref)
            list_splited_invoice = str(invoice_ref).replace('[', '').replace(']', '').replace("'", "")


            list_non_splited_invoice_str = list_non_splited_invoice_str+", "+str(list_splited_invoice)

        global_list_facture = list_non_splited_invoice_str
        tab_global_list_facture = str(global_list_facture).split(",")
        print(" ### tab_global_list_facture  =  ", tab_global_list_facture)
        
        """
        Recupeer le modele de courrier "courrier_template_type_document_ref_interne":"FACTURATION_SESSION"
        depuis la collection courrier_template_tracking
        """
        print(' QRY : ', {'courrier_template_type_document_ref_interne':'FACTURATION_SESSION',
                                                                                           'partner_owner_recid':my_partner['recid'],
                                                                                       'valide':'1',
                                                                                       'locked':'0',
                                                                                       }
              )

        courrier_template_count = MYSY_GV.dbname['courrier_template'].count_documents({'ref_interne':'FACTURATION_SESSION',
                                                                                           'partner_owner_recid':'default',
                                                                                       'valide':'1',
                                                                                       'locked':'0',
                                                                                       })


        if( courrier_template_count != 1):
            mycommon.myprint(" WARNING : Impossible d'identifier le courrier_template_count associé à la facturation : ")

        else:
            courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one({'ref_interne':'FACTURATION_SESSION',
                                                                                           'partner_owner_recid':'default',
                                                                                       'valide':'1',
                                                                                       'locked':'0',
                                                                                       })


            print(" ### courrier_template_data =  ", courrier_template_data)

            print(" QRYY  =  ", {'partner_owner_recid': str(my_partner['recid']),
                                                               'session_id': str(diction['session_id']),
                                                               'invoiced_ref': {'$in': tab_global_list_facture}})

            if( courrier_template_data and '_id' in courrier_template_data.keys() ):

                for local_data in tab_inscrit_for_splited_invoice :
                    for val in MYSY_GV.dbname['inscription'].find({'partner_owner_recid': str(my_partner['recid']),
                                                                   'session_id': str(diction['session_id']),
                                                                   '_id':ObjectId(str(local_data['inscription_id']))}):

                        ref_facture = ""
                        if ("invoiced_ref" in val.keys()):
                            ref_facture = val['invoiced_ref']


                        local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                            my_partner, "FACTURATION_SESSION", str(diction['session_id']), 'inscription',
                            str(val['_id']),
                            str(courrier_template_data['_id']),
                            "Facture : " + str(ref_facture)
                        )

                        if (local_status is False):
                            mycommon.myprint(" WARNING : Impossible de logguer l'historique l'inscription_id : " + str(val['_id']))

                for local_data in tab_inscrit_for_NOT_splited_invoice:
                    for val in MYSY_GV.dbname['inscription'].find({'partner_owner_recid': str(my_partner['recid']),
                                                                   'session_id': str(diction['session_id']),
                                                                   '_id': ObjectId(str(
                                                                       local_data['inscription_id']))}):

                        ref_facture = ""
                        if ("invoiced_ref" in val.keys()):
                            ref_facture = val['invoiced_ref']

                        print(" ### traintement login de val = ", val)

                        local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                            my_partner, "FACTURATION_SESSION", str(diction['session_id']), 'inscription',
                            str(val['_id']),
                            str(courrier_template_data['_id']),
                            "Facture : " + str(ref_facture)
                        )

                        if (local_status is False):
                            mycommon.myprint(
                                " WARNING : Impossible de logguer l'historique l'inscription_id : " + str(val['_id']))

        global_list_facture = str(global_list_facture).replace(",", "\n")

        """
               07/03/20204 : mettre un statut de facturation sur la session afin de voir
               tout de suite quel session est entièrement facturée ou partiellement.

               regles : 
               Si toutes les inscription associées à une session sont facturée ==> invoiced_statut de la session = 2
               Si au moins une inscription associée à une session est facturé ==> invoiced_statut de la session = 1
               Si non invoiced_statut de la session =0
               """
        nb_inscription_facture_termine =  MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'invoiced': '1',
             'session_id':str(diction['session_id'])
             })

        nb_inscription_non_facture_ou_encours = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': str(diction['session_id']),
             'invoiced': {'$ne': '1'}})

        nb_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'session_id': str(diction['session_id']),
             'status': '1'})

        invoiced_statut = "0"
        if (nb_inscription_facture_termine == nb_inscription_valide):
            # toutes les inscription valides ont été facturée
            invoiced_statut = "2"
        elif (nb_inscription_non_facture_ou_encours > 0):
            # Au moins une ligne a été facturée
            invoiced_statut = "1"

        # Mise à jour du statut de facturation de la session
        MYSY_GV.dbname['session_formation'].update_one({'partner_owner_recid': my_partner['recid'],
                                                        'valide': '1',
                                                        '_id': ObjectId(str(diction['session_id']))
                                                        },
                                                       {'$set': {'invoiced_statut': invoiced_statut}})

        return True, " Les factures suivantes été créées : \n "+str(global_list_facture), str(global_list_facture)

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de generer les factures ", False


"""
Facturation partielles d'une session.
C'est a dire le cas ou on souhaite facturer que les lignes selectionnées 

/!\ :
Cette fonction est identique à la fonction : "Prepare_and_Send_Facture_From_Session_By_Email" 
a l'exception qu'elle prend en compte les inscription_id à facturer

17/05/2024 : /!\
Maintenant on facture à partir du champs "facture_client_rattachement_id" et non 
"client_rattachement_id" qui lui est concerné par les convention et autre documents
administratifs

"""


def Prepare_and_Send_Facture_From_Session_By_Inscription_Id(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production',
                                  'tab_inscription_ids']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        my_inscription_ids = ""
        tab_my_inscription_ids = []
        tab_my_inscription_ids_Object = []
        if ("tab_inscription_ids" in diction.keys()):
            if diction['tab_inscription_ids']:
                my_inscription_ids = diction['tab_inscription_ids']

                tab_my_inscription_ids = str(my_inscription_ids).split(",")
                print(" ### tab_my_inscription_ids = ", tab_my_inscription_ids)
                for my_inscription_id in tab_my_inscription_ids:


                    tmp_count = MYSY_GV.dbname['inscription'].count_documents({'session_id': str(diction['session_id']),
                                                                               '_id': ObjectId(str(my_inscription_id)),
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid'])})

                    if (tmp_count != 1):
                        mycommon.myprint(str(inspect.stack()[0][3]) + " L'indentifiant de l'inscription " + str(
                            my_inscription_id) + " est invalide ")
                        return False, " L'indentifiant de l'inscription " + str(my_inscription_id) + " est invalide "

                    tab_my_inscription_ids_Object.append(ObjectId(str(my_inscription_id)))


        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  ", False

        """
                Gestion des inscriptions n'ayant pas de client ID
                """
        liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
            {"$or": [{'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      "facture_client_rattachement_id": '',
                      "invoiced": {'$ne': '1'},
                       "_id": {'$in': tab_my_inscription_ids_Object},
                      },
                     {'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      'facture_client_rattachement_id': {'$exists': False},
                      "invoiced": {'$ne': '1'},
                      "_id": {'$in': tab_my_inscription_ids_Object},
                      }]
             }
        )


        # CONTROLE : Verification des data client (si tous les clients 'particuliers' sont ok
        for local_inscription_no_client in liste_inscription_no_client:
            print(" ### List des inscrit n'ayant pas de 'client_id': ", local_inscription_no_client)

            """ 
            - Verifier s'il y a un client avec la même adresse email, 
            si non, créer le client
            """
            is_inscription_no_client_exist = MYSY_GV.dbname['partner_client'].count_documents(
                {'partner_recid': str(my_partner['recid']),
                 'email': str(local_inscription_no_client['email'])})

            # S'il ya plusieurs clients avec la meme adresse email, alors il y a un bin's
            if (is_inscription_no_client_exist > 1):
                mycommon.myprint(" Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients")
                return False, "  Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients ", False

            # Si le client existe, verifier qu'il est valide et pas locké
            if (is_inscription_no_client_exist == 1):
                is_inscription_no_client_valide_no_locked = MYSY_GV.dbname['partner_client'].count_documents(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                if (is_inscription_no_client_valide_no_locked == 0):
                    mycommon.myprint(" Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide")
                    return False, "  Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide ", False

                # Mise à jour de l'inscription avec l'_id du client
                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set': {
                                                             'facture_client_rattachement_id': str(inscription_no_client_valide_no_locked_data['_id']),
                                                             'client_rattachement_id': str(
                                                                 inscription_no_client_valide_no_locked_data['_id']),
                                                         }
                                                          })

            # Si il n'y a pas de client associé à cette adresse email, alors on crée le client
            if (is_inscription_no_client_exist == 0):

                new_client_contact_data = {}
                new_client_data = {}
                new_partner_all_list = ['token', "raison_sociale", "nom", "siret", "tva", "email",
                                        "telephone", "website", "comment", "address",
                                        'adr_adresse', 'adr_code_postal', 'adr_ville', 'adr_pays',
                                        'invoice_email', 'invoice_nom', 'invoice_siret',
                                        'invoice_tva', 'invoice_condition_paiement_id', 'invoice_adresse',
                                        'invoice_ville',
                                        'invoice_code_postal', 'invoice_pays', "client_type_id",
                                        'is_fournisseur', 'is_client', 'is_financeur', 'is_client']

                # Pre Remplir les champs à vide
                for tmp in new_partner_all_list:
                    new_client_data[str(tmp)] = ""

                new_client_data['token'] = diction['token']
                new_client_contact_data['token'] = diction['token']

                if ("nom" in local_inscription_no_client.keys()):
                    new_client_data['raison_sociale'] = local_inscription_no_client['nom']
                    new_client_data['nom'] = local_inscription_no_client['nom']

                    # Pour le contact
                    new_client_contact_data['nom'] = local_inscription_no_client['nom']

                if ("prenom" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['prenom'] = local_inscription_no_client['prenom']

                if ("civilite" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['civilite'] = str(local_inscription_no_client['civilite']).lower()

                if ("email" in local_inscription_no_client.keys()):
                    new_client_data['email'] = local_inscription_no_client['email']
                    new_client_data['invoice_email'] = local_inscription_no_client['email']

                    # Pour le contact
                    new_client_contact_data['email'] = local_inscription_no_client['email']

                if ("telephone" in local_inscription_no_client.keys()):
                    new_client_data['telephone'] = local_inscription_no_client['telephone']

                    # Pour le contact
                    new_client_contact_data['telephone'] = local_inscription_no_client['telephone']

                if ("adresse" in local_inscription_no_client.keys()):
                    new_client_data['adr_adresse'] = local_inscription_no_client['adresse']
                    new_client_data['invoice_adresse'] = local_inscription_no_client['adresse']

                    # Pour le contact
                    new_client_contact_data['adr_adresse'] = local_inscription_no_client['adresse']

                if ("code_postal" in local_inscription_no_client.keys()):
                    new_client_data['adr_code_postal'] = local_inscription_no_client['code_postal']
                    new_client_data['invoice_code_postal'] = local_inscription_no_client['code_postal']

                    # Pour le contact
                    new_client_contact_data['adr_code_postal'] = local_inscription_no_client['code_postal']

                if ("ville" in local_inscription_no_client.keys()):
                    new_client_data['adr_ville'] = local_inscription_no_client['ville']
                    new_client_data['invoice_ville'] = local_inscription_no_client['ville']

                    # Pour le contact
                    new_client_contact_data['adr_ville'] = local_inscription_no_client['ville']

                if ("pays" in local_inscription_no_client.keys()):
                    new_client_data['adr_pays'] = local_inscription_no_client['pays']
                    new_client_data['invoice_pays'] = local_inscription_no_client['pays']

                    # Pour le contact
                    new_client_contact_data['adr_pays'] = local_inscription_no_client['pays']

                new_client_data['is_client'] = "1"
                new_client_data['is_company'] = "0"

                new_client_status, new_client_retval = partner_client.Add_Partner_Client(new_client_data)
                if (new_client_status is False):
                    mycommon.myprint(" Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ")
                    return False, " Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ", False

                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                # Créer le contact de communication du client
                new_client_contact_data['include_com'] = "1"
                new_client_contact_data['related_collection'] = "partner_client"
                new_client_contact_data['related_collection_owner_id'] = str(
                    inscription_no_client_valide_no_locked_data['_id'])
                local_add_contact_status, local_add_contact_retval = Contact.Add_Contact(new_client_contact_data)
                if (local_add_contact_status is False):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " WARNING : Impossible de créer le contact " + str(
                        local_add_contact_retval))

                # Mise à jour de l'inscription avec l'_id du client
                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set':
                                                             {
                                                                 'facture_client_rattachement_id': str(
                                                             inscription_no_client_valide_no_locked_data['_id']),
                                                                 'client_rattachement_id': str(
                                                                     inscription_no_client_valide_no_locked_data['_id'])

                                                             }
                                                         })

        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = []

        liste_client_facturation_rattachement_id = MYSY_GV.dbname['inscription'].distinct("facture_client_rattachement_id",
                                                                              {'session_id': str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                               "facture_client_rattachement_id": {'$ne': ''},
                                                                               "invoiced": {'$ne': '1'},
                                                                               "_id": {'$in': tab_my_inscription_ids_Object},
                                                                               }
                                                                              )

        print(" ### la liste des liste_client_facturation_rattachement_id = ", liste_client_facturation_rattachement_id)

        liste_client_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct(
            "client_rattachement_id",
            {'session_id': str(diction['session_id']),
             'status': '1',
             'partner_owner_recid': str(
                 my_partner['recid']),
             "facture_client_rattachement_id": {'$ne': ''},
             "invoiced": {'$ne': '1'},
             "_id": {'$in': tab_my_inscription_ids_Object},
             }
            )

        print(" ### la liste des liste_client_client_rattachement_id = ", liste_client_client_rattachement_id)

        # Ajout des client de facturation
        for tmp in liste_client_facturation_rattachement_id :
            liste_client_rattachement_id.append(tmp)

        # Ajout des client_client de facturation
        for tmp in liste_client_client_rattachement_id:
            liste_client_rattachement_id.append(tmp)

        print(" ### la liste des liste_client_rattachement_id = ", liste_client_rattachement_id)

        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer", False

            tab_saved_file_full_path.append(saved_file_full_path)

        print(" #### tab_saved_file_full_path = ", tab_saved_file_full_path)

        # Recupération des données du modèle de document
        courrier_template_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'_id': ObjectId(str(diction['courrier_template_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])}
        )

        tab_local_invoice_ref_interne = []

        # Traitement pour les facture : liste_client_facturation_rattachement_id
        for single_client in liste_client_facturation_rattachement_id:
            print(" Traitement du client_id = ", single_client)

            # Recuperation des contacts de communication du client
            local_diction = {}
            local_diction['token'] = diction['token']
            local_diction['_id'] = str(single_client)

            print(" ##### local_diction = ", local_diction)
            local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(
                local_diction)

            if (local_status is False):
                mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                return False, " Impossible de récupérer les contacts de communication du client ", False

            print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
            tab_local_email_production = []
            for tmp in partner_client_contact_communication:
                tmp_JSON = ast.literal_eval(tmp)
                if ("email" in tmp_JSON.keys()):
                    tab_local_email_production.append(str(tmp_JSON["email"]))

            list_local_email_production = ",".join(tab_local_email_production)

            new_diction_client = {}
            new_diction_client['partner_client_id'] = single_client
            new_diction_client['token'] = diction['token']
            new_diction_client['courrier_template_id'] = diction['courrier_template_id']
            new_diction_client['email_test'] = diction['email_test']
            new_diction_client['email_production'] = str(list_local_email_production)
            new_diction_client['session_id'] = diction['session_id']

            print(" ##### new_diction_client 0102 = ", new_diction_client)

            # Creation de la facture du client
            diction_invoice = {}
            diction_invoice['token'] = diction['token']
            diction_invoice['partner_client_id'] = single_client
            diction_invoice['session_id'] = diction['session_id']
            diction_invoice['tab_inscription_ids'] = tab_my_inscription_ids_Object
            print(" ##### liste_client_facturation_rattachement_id : diction_invoice 0202 icici = ", diction_invoice)
            local_create_invoice_status, local_create_invoice_retval, local_invoice_ref_interne = Invoice_Partner_From_Session_By_Inscription_Id(
                diction_invoice)
            if (local_create_invoice_status is False):
                return local_create_invoice_status, local_create_invoice_retval, False

            """
            Mettre à jour les lignes associées à ce client pour dire que la ligne est facturé
            """
            now = str(datetime.now())
            update_data = {}
            update_data['invoiced'] = "1"
            update_data['invoiced_ref'] = str(local_invoice_ref_interne)
            update_data['invoiced_date'] = now
            update_data['date_update'] = now
            update_data['update_by'] = str(my_partner['_id'])

            update = MYSY_GV.dbname['inscription'].update_many({'partner_owner_recid': str(my_partner['recid']),
                                                                'session_id': diction['session_id'],
                                                                'facture_client_rattachement_id': str(single_client),
                                                                "_id": {'$in': tab_my_inscription_ids_Object},
                                                                },
                                                               {'$set': update_data})

            tab_local_invoice_ref_interne.append(str(local_invoice_ref_interne))


        # Traitement pour les facture : liste_client_client_rattachement_id
        for single_client in liste_client_client_rattachement_id:
                print(" Traitement du client_id = ", single_client)

                # Recuperation des contacts de communication du client
                local_diction = {}
                local_diction['token'] = diction['token']
                local_diction['_id'] = str(single_client)

                print(" ##### local_diction = ", local_diction)
                local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(
                    local_diction)

                if (local_status is False):
                    mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                    return False, " Impossible de récupérer les contacts de communication du client ", False

                print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
                tab_local_email_production = []
                for tmp in partner_client_contact_communication:
                    tmp_JSON = ast.literal_eval(tmp)
                    if ("email" in tmp_JSON.keys()):
                        tab_local_email_production.append(str(tmp_JSON["email"]))

                list_local_email_production = ",".join(tab_local_email_production)

                new_diction_client = {}
                new_diction_client['partner_client_id'] = single_client
                new_diction_client['token'] = diction['token']
                new_diction_client['courrier_template_id'] = diction['courrier_template_id']
                new_diction_client['email_test'] = diction['email_test']
                new_diction_client['email_production'] = str(list_local_email_production)
                new_diction_client['session_id'] = diction['session_id']

                print(" ##### new_diction_client 0102 = ", new_diction_client)

                # Creation de la facture du client
                diction_invoice = {}
                diction_invoice['token'] = diction['token']
                diction_invoice['partner_client_id'] = single_client
                diction_invoice['session_id'] = diction['session_id']
                diction_invoice['tab_inscription_ids'] = tab_my_inscription_ids_Object
                print(" ##### liste_client_client_rattachement_id : diction_invoice 0202 icici = ", diction_invoice)
                local_create_invoice_status, local_create_invoice_retval, local_invoice_ref_interne = Invoice_Partner_From_Session_By_Inscription_Id(
                    diction_invoice)
                if (local_create_invoice_status is False):
                    return local_create_invoice_status, local_create_invoice_retval, False

                """
                Mettre à jour les lignes associées à ce client pour dire que la ligne est facturé
                """
                now = str(datetime.now())
                update_data = {}
                update_data['invoiced'] = "1"
                update_data['invoiced_ref'] = str(local_invoice_ref_interne)
                update_data['invoiced_date'] = now
                update_data['date_update'] = now
                update_data['update_by'] = str(my_partner['_id'])

                update = MYSY_GV.dbname['inscription'].update_many({'partner_owner_recid': str(my_partner['recid']),
                                                                    'session_id': diction['session_id'],
                                                                    'facture_client_rattachement_id': str(
                                                                        single_client),
                                                                    "_id": {'$in': tab_my_inscription_ids_Object},
                                                                    },
                                                                   {'$set': update_data})

                tab_local_invoice_ref_interne.append(str(local_invoice_ref_interne))


        """
        07/03/20204 : mettre un statut de facturation sur la session afin de voir
        tout de suite quel session est entièrement facturée ou partiellement.
        
        regles : 
        Si toutes les inscription associées à une session sont facturée ==> invoiced_statut de la session = 2
        Si au moins une inscription associée à une session est facturé ==> invoiced_statut de la session = 1
        Si non invoiced_statut de la session =0
        """
        nb_inscription_facture = MYSY_GV.dbname['inscription'].count_documents({'partner_owner_recid':my_partner['recid'],
                                                                                'invoiced':'1'})

        nb_inscription_non_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'invoiced': {'$ne':'1'}})

        nb_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'status':'1'})

        invoiced_statut = "0"
        if( nb_inscription_facture == nb_inscription_valide ):
            # toutes les inscription valides ont été facturée
            invoiced_statut = "2"
        elif ( nb_inscription_facture > 0 ):
            # Au moins une ligne a été facturée
            invoiced_statut = "1"

        # Mise à jour du statut de facturation de la session
        MYSY_GV.dbname['session_formation'].update_one({'partner_owner_recid':my_partner['recid'],
                                                        'valide':'1',
                                                        '_id':ObjectId(str(diction['session_id']))
                                                        },
                                                       {'$set':{'invoiced_statut':invoiced_statut}})





        # Creation de l'historique dans les action 'courrier_template_tracking_history'
        local_qry = {'partner_owner_recid': str(my_partner['recid']), 'session_id': str(diction['session_id']),
                     'invoiced_ref': {'$in': tab_local_invoice_ref_interne}}

        # print(" ###  local_qry = ", local_qry)

        for val in MYSY_GV.dbname['inscription'].find({'partner_owner_recid': str(my_partner['recid']),
                                                       'session_id': str(diction['session_id']),
                                                       'invoiced_ref': {'$in': tab_local_invoice_ref_interne}}):

            ref_facture = ""
            if ("invoiced_ref" in val.keys()):
                ref_facture = val['invoiced_ref']

            local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                my_partner, "FACTURATION_SESSION", str(diction['session_id']), 'inscription',
                str(val['_id']),
                str(diction['courrier_template_id']),
                "Facture : " + str(ref_facture)
            )

            if (local_status is False):
                mycommon.myprint(" WARNING : Impossible de logguer l'historique l'inscription_id : " + str(val['_id']))

        return_message = " La session a été correctement facturée.\nListe des factures : "
        for tmp in tab_local_invoice_ref_interne:
            return_message += "\n - " + str(tmp)

        return True, str(return_message), tab_local_invoice_ref_interne

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer et d'envoyer les factures par email ", False


"""
Sauvegarde fonction avant modif 
"""


def Prepare_and_Send_Facture_From_Session_By_Inscription_Id_SAVE_ORIG(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production',
                                  'tab_inscription_ids']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        my_inscription_ids = ""
        tab_my_inscription_ids = []
        tab_my_inscription_ids_Object = []
        if ("tab_inscription_ids" in diction.keys()):
            if diction['tab_inscription_ids']:
                my_inscription_ids = diction['tab_inscription_ids']

                tab_my_inscription_ids = str(my_inscription_ids).split(",")
                print(" ### tab_my_inscription_ids = ", tab_my_inscription_ids)
                for my_inscription_id in tab_my_inscription_ids:

                    tmp_count = MYSY_GV.dbname['inscription'].count_documents({'session_id': str(diction['session_id']),
                                                                               '_id': ObjectId(str(my_inscription_id)),
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                               "invoiced": {'$ne': '1'},})

                    if (tmp_count != 1):
                        mycommon.myprint(str(inspect.stack()[0][3]) + " L'indentifiant de l'inscription " + str(
                            my_inscription_id) + " est invalide ")
                        return False, " L'indentifiant de l'inscription " + str(my_inscription_id) + " est invalide "

                    tab_my_inscription_ids_Object.append(ObjectId(str(my_inscription_id)))

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  ", False

        """
                Gestion des inscriptions n'ayant pas de client à facturer ID
                """
        liste_inscription_no_client = MYSY_GV.dbname['inscription'].find(
            {"$or": [{'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      "facture_client_rattachement_id": '',
                      "invoiced": {'$ne': '1'},
                      "_id": {'$in': tab_my_inscription_ids_Object},
                      },
                     {'session_id': str(diction['session_id']),
                      'status': '1',
                      'partner_owner_recid': str(my_partner['recid']),
                      'facture_client_rattachement_id': {'$exists': False},
                      "invoiced": {'$ne': '1'},
                      "_id": {'$in': tab_my_inscription_ids_Object},
                      }]
             }
        )

        # CONTROLE : Verification des data client (si tous les clients 'particuliers' sont ok
        for local_inscription_no_client in liste_inscription_no_client:
            print(" ### List des inscrit n'ayant pas de 'client_id': ", local_inscription_no_client)

            """ 
            - Verifier s'il y a un client avec la même adresse email, 
            si non, créer le client
            """
            is_inscription_no_client_exist = MYSY_GV.dbname['partner_client'].count_documents(
                {'partner_recid': str(my_partner['recid']),
                 'email': str(local_inscription_no_client['email'])})

            # S'il ya plusieurs clients avec la meme adresse email, alors il y a un bin's
            if (is_inscription_no_client_exist > 1):
                mycommon.myprint(" Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients")
                return False, "  Facturation : L'adresse email : " + str(
                    local_inscription_no_client['email']) + " correspond à plusieurs clients ", False

            # Si le client existe, verifier qu'il est valide et pas locké
            if (is_inscription_no_client_exist == 1):
                is_inscription_no_client_valide_no_locked = MYSY_GV.dbname['partner_client'].count_documents(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                if (is_inscription_no_client_valide_no_locked == 0):
                    mycommon.myprint(" Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide")
                    return False, "  Facturation : L'adresse email : " + str(
                        local_inscription_no_client['email']) + " correspond à un client non valide ", False

                # Mise à jour de l'inscription avec l'_id du client
                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set': {'client_rattachement_id': str(
                                                             inscription_no_client_valide_no_locked_data['_id'])}})

            # Si il n'y a pas de client associé à cette adresse email, alors on crée le client
            if (is_inscription_no_client_exist == 0):

                new_client_contact_data = {}
                new_client_data = {}
                new_partner_all_list = ['token', "raison_sociale", "nom", "siret", "tva", "email",
                                        "telephone", "website", "comment", "address",
                                        'adr_adresse', 'adr_code_postal', 'adr_ville', 'adr_pays',
                                        'invoice_email', 'invoice_nom', 'invoice_siret',
                                        'invoice_tva', 'invoice_condition_paiement_id', 'invoice_adresse',
                                        'invoice_ville',
                                        'invoice_code_postal', 'invoice_pays', "client_type_id",
                                        'is_fournisseur', 'is_client', 'is_financeur', 'is_client']

                # Pre Remplir les champs à vide
                for tmp in new_partner_all_list:
                    new_client_data[str(tmp)] = ""

                new_client_data['token'] = diction['token']
                new_client_contact_data['token'] = diction['token']

                if ("nom" in local_inscription_no_client.keys()):
                    new_client_data['raison_sociale'] = local_inscription_no_client['nom']
                    new_client_data['nom'] = local_inscription_no_client['nom']

                    # Pour le contact
                    new_client_contact_data['nom'] = local_inscription_no_client['nom']

                if ("prenom" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['prenom'] = local_inscription_no_client['prenom']

                if ("civilite" in local_inscription_no_client.keys()):
                    # Pour le contact
                    new_client_contact_data['civilite'] = str(local_inscription_no_client['civilite']).lower()

                if ("email" in local_inscription_no_client.keys()):
                    new_client_data['email'] = local_inscription_no_client['email']
                    new_client_data['invoice_email'] = local_inscription_no_client['email']

                    # Pour le contact
                    new_client_contact_data['email'] = local_inscription_no_client['email']

                if ("telephone" in local_inscription_no_client.keys()):
                    new_client_data['telephone'] = local_inscription_no_client['telephone']

                    # Pour le contact
                    new_client_contact_data['telephone'] = local_inscription_no_client['telephone']

                if ("adresse" in local_inscription_no_client.keys()):
                    new_client_data['adr_adresse'] = local_inscription_no_client['adresse']
                    new_client_data['invoice_adresse'] = local_inscription_no_client['adresse']

                    # Pour le contact
                    new_client_contact_data['adr_adresse'] = local_inscription_no_client['adresse']

                if ("code_postal" in local_inscription_no_client.keys()):
                    new_client_data['adr_code_postal'] = local_inscription_no_client['code_postal']
                    new_client_data['invoice_code_postal'] = local_inscription_no_client['code_postal']

                    # Pour le contact
                    new_client_contact_data['adr_code_postal'] = local_inscription_no_client['code_postal']

                if ("ville" in local_inscription_no_client.keys()):
                    new_client_data['adr_ville'] = local_inscription_no_client['ville']
                    new_client_data['invoice_ville'] = local_inscription_no_client['ville']

                    # Pour le contact
                    new_client_contact_data['adr_ville'] = local_inscription_no_client['ville']

                if ("pays" in local_inscription_no_client.keys()):
                    new_client_data['adr_pays'] = local_inscription_no_client['pays']
                    new_client_data['invoice_pays'] = local_inscription_no_client['pays']

                    # Pour le contact
                    new_client_contact_data['adr_pays'] = local_inscription_no_client['pays']

                new_client_data['is_client'] = "1"
                new_client_data['is_company'] = "0"

                new_client_status, new_client_retval = partner_client.Add_Partner_Client(new_client_data)
                if (new_client_status is False):
                    mycommon.myprint(" Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ")
                    return False, " Facturation : Impossible de créer un client associé à l'adresse email : " + str(
                        local_inscription_no_client['email']) + " ", False

                inscription_no_client_valide_no_locked_data = MYSY_GV.dbname['partner_client'].find_one(
                    {'partner_recid': str(my_partner['recid']),
                     'email': str(local_inscription_no_client['email']),
                     'valide': "1",
                     'locked': '0'})

                # Créer le contact de communication du client
                new_client_contact_data['include_com'] = "1"
                new_client_contact_data['related_collection'] = "partner_client"
                new_client_contact_data['related_collection_owner_id'] = str(
                    inscription_no_client_valide_no_locked_data['_id'])
                local_add_contact_status, local_add_contact_retval = Contact.Add_Contact(new_client_contact_data)
                if (local_add_contact_status is False):
                    mycommon.myprint(str(inspect.stack()[0][3]) + " WARNING : Impossible de créer le contact " + str(
                        local_add_contact_retval))

                # Mise à jour de l'inscription avec l'_id du client
                MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                          '_id': ObjectId(local_inscription_no_client['_id'])},
                                                         {'$set': {'client_rattachement_id': str(
                                                             inscription_no_client_valide_no_locked_data['_id'])}})

        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("facture_client_rattachement_id",
                                                                              {'session_id': str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                               "facture_client_rattachement_id": {'$ne': ''},
                                                                               "invoiced": {'$ne': '1'},
                                                                               "_id": {
                                                                                   '$in': tab_my_inscription_ids_Object},
                                                                               }
                                                                              )

        print(" ### la liste des liste_client_rattachement_id (client à facturer) = ", liste_client_rattachement_id)

        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer", False

            tab_saved_file_full_path.append(saved_file_full_path)

        print(" #### tab_saved_file_full_path = ", tab_saved_file_full_path)



        tab_local_invoice_ref_interne = []

        # Envoie des factures pour les inscrits AVEC client_id  (conventions d'entreprise)
        for single_client in liste_client_rattachement_id:
            print(" Traitement du client_id (client à facturer)  = ", single_client)

            # Recuperation des contacts de communication du client
            local_diction = {}
            local_diction['token'] = diction['token']
            local_diction['_id'] = str(single_client)

            print(" ##### local_diction = ", local_diction)
            local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(
                local_diction)

            if (local_status is False):
                mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                return False, " Impossible de récupérer les contacts de communication du client ", False

            print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
            tab_local_email_production = []
            for tmp in partner_client_contact_communication:
                tmp_JSON = ast.literal_eval(tmp)
                if ("email" in tmp_JSON.keys()):
                    tab_local_email_production.append(str(tmp_JSON["email"]))

            list_local_email_production = ",".join(tab_local_email_production)

            new_diction_client = {}
            new_diction_client['partner_client_id'] = single_client
            new_diction_client['token'] = diction['token']
            new_diction_client['courrier_template_id'] = diction['courrier_template_id']
            new_diction_client['email_test'] = diction['email_test']
            new_diction_client['email_production'] = str(list_local_email_production)
            new_diction_client['session_id'] = diction['session_id']

            print(" ##### new_diction_client 0102 = ", new_diction_client)

            # Creation de la facture du client
            diction_invoice = {}
            diction_invoice['token'] = diction['token']
            diction_invoice['partner_client_id'] = single_client
            diction_invoice['session_id'] = diction['session_id']
            diction_invoice['tab_inscription_ids'] = tab_my_inscription_ids_Object
            print(" ##### diction_invoice 0202 tttt = ", diction_invoice)
            local_create_invoice_status, local_create_invoice_retval, local_invoice_ref_interne = Invoice_Partner_From_Session_By_Inscription_Id(
                diction_invoice)
            if (local_create_invoice_status is False):
                return local_create_invoice_status, local_create_invoice_retval, False

            """
            Mettre à jour les lignes associées à ce client pour dire que la ligne est facturé
            """
            now = str(datetime.now())
            update_data = {}
            update_data['invoiced'] = "1"
            update_data['invoiced_ref'] = str(local_invoice_ref_interne)
            update_data['invoiced_date'] = now
            update_data['date_update'] = now
            update_data['update_by'] = str(my_partner['_id'])

            update = MYSY_GV.dbname['inscription'].update_many({'partner_owner_recid': str(my_partner['recid']),
                                                                'session_id': diction['session_id'],
                                                                'facture_client_rattachement_id': str(single_client),
                                                                "_id": {'$in': tab_my_inscription_ids_Object},
                                                                },
                                                               {'$set': update_data})

            tab_local_invoice_ref_interne.append(str(local_invoice_ref_interne))


        return_message = " La session a été correctement facturée.\nListe des factures : "
        for tmp in tab_local_invoice_ref_interne:
            return_message += "\n - " + str(tmp)

        return True, str(return_message), tab_local_invoice_ref_interne

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer et d'envoyer les factures par email ", False





"""
15/06/2024 - Facturation des inscription avec split


Cette fonction permet de factuer une ligner avec des split
c'est a dire que sur l'inscription, il a été clairement marqué (ajouté)
le champ "'invoice_split': {'split_type': 'percent', 'tab_split': [{'partner_client': '65f05b37c544f77525a30645', 'invoice_part': 40},
 {'partner_client': '65f05b37c544f77525a30645', 'invoice_part': 60}]}"
 
algo : 
On ne fait pas de regroupement dans ce cas de figure.
Pour CHAQUE LIGNE d'inscription, une facture correspondat au pourcentage ou au montant du 'partner_client' 
 
"""
def Prepare_and_Send_SPLITED_Facture_From_Session_By_Inscription_Id(tab_files, Folder, diction):
    try:

        field_list_obligatoire = ['token', 'session_id', 'courrier_template_id', 'email_test', 'email_production',
                                  'tab_inscription_ids']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        my_inscription_ids = ""
        tab_my_inscription_ids = []
        tab_my_inscription_ids_Object = []
        if ("tab_inscription_ids" in diction.keys()):
            if diction['tab_inscription_ids']:
                my_inscription_ids = diction['tab_inscription_ids']

                tab_my_inscription_ids = str(my_inscription_ids).split(",")
                print(" ### tab_my_inscription_ids = ", tab_my_inscription_ids)
                for my_inscription_id in tab_my_inscription_ids:

                    tmp_count = MYSY_GV.dbname['inscription'].count_documents({'session_id': str(diction['session_id']),
                                                                               '_id': ObjectId(str(my_inscription_id)),
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid'])})

                    if (tmp_count != 1):
                        mycommon.myprint(str(inspect.stack()[0][3]) + " L'indentifiant de l'inscription " + str(
                            my_inscription_id) + " est invalide ")
                        return False, " L'indentifiant de l'inscription " + str(my_inscription_id) + " est invalide "




                    tab_my_inscription_ids_Object.append(ObjectId(str(my_inscription_id)))

        # Verifier que la session est valide
        is_session_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session  est invalide ")
            return False, "  L'identifiant de la session  est invalide  ", False


        # Verifier qu'il ya bien des inscriptions valides dans la session pour le client
        liste_client_rattachement_id = MYSY_GV.dbname['inscription'].distinct("facture_client_rattachement_id",
                                                                              {'session_id': str(diction['session_id']),
                                                                               'status': '1',
                                                                               'partner_owner_recid': str(
                                                                                   my_partner['recid']),
                                                                               "facture_client_rattachement_id": {'$ne': ''},
                                                                               "invoiced": {'$ne': '1'},
                                                                               "_id": {
                                                                                   '$in': tab_my_inscription_ids_Object},
                                                                               }
                                                                              )

        print(" ### la liste des liste_client_rattachement_id (client à facturer) = ", liste_client_rattachement_id)

        # Sauvegarde des fichiers joints depuis le front
        tab_saved_file_full_path = []
        for file in tab_files:
            status, saved_file_full_path = mycommon.Upload_Save_PDF_IMG_File(file, Folder)
            if (status is False):
                mycommon.myprint("Impossible de récupérer correctement le fichier à importer")
                return False, "Impossible de récupérer correctement le fichier à importer", False

            tab_saved_file_full_path.append(saved_file_full_path)

        print(" #### tab_saved_file_full_path = ", tab_saved_file_full_path)



        tab_local_invoice_ref_interne = []

        # Envoie des factures pour les inscrits AVEC client_id  (conventions d'entreprise)
        for single_client in liste_client_rattachement_id:
            print(" Traitement du client_id (client à facturer)  = ", single_client)

            # Recuperation des contacts de communication du client
            local_diction = {}
            local_diction['token'] = diction['token']
            local_diction['_id'] = str(single_client)

            print(" ##### local_diction = ", local_diction)
            local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(
                local_diction)

            if (local_status is False):
                mycommon.myprint(" Impossible de récupérer les contacts de communication du client ")
                return False, " Impossible de récupérer les contacts de communication du client ", False

            print("  ### partner_client_contact_communication = ", partner_client_contact_communication)
            tab_local_email_production = []
            for tmp in partner_client_contact_communication:
                tmp_JSON = ast.literal_eval(tmp)
                if ("email" in tmp_JSON.keys()):
                    tab_local_email_production.append(str(tmp_JSON["email"]))

            list_local_email_production = ",".join(tab_local_email_production)

            new_diction_client = {}
            new_diction_client['partner_client_id'] = single_client
            new_diction_client['token'] = diction['token']
            new_diction_client['courrier_template_id'] = diction['courrier_template_id']
            new_diction_client['email_test'] = diction['email_test']
            new_diction_client['email_production'] = str(list_local_email_production)
            new_diction_client['session_id'] = diction['session_id']

            print(" ##### new_diction_client 0102 = ", new_diction_client)

            # Creation de la facture du client
            diction_invoice = {}
            diction_invoice['token'] = diction['token']
            diction_invoice['partner_client_id'] = single_client
            diction_invoice['session_id'] = diction['session_id']
            diction_invoice['tab_inscription_ids'] = tab_my_inscription_ids_Object
            print(" ##### diction_invoice 0202 tttt = ", diction_invoice)
            local_create_invoice_status, local_create_invoice_retval, local_invoice_ref_interne = Invoice_Partner_From_Session_By_Inscription_Id(
                diction_invoice)
            if (local_create_invoice_status is False):
                return local_create_invoice_status, local_create_invoice_retval, False

            """
            Mettre à jour les lignes associées à ce client pour dire que la ligne est facturé
            """
            now = str(datetime.now())
            update_data = {}
            update_data['invoiced'] = "1"
            update_data['invoiced_ref'] = str(local_invoice_ref_interne)
            update_data['invoiced_date'] = now
            update_data['date_update'] = now
            update_data['update_by'] = str(my_partner['_id'])

            update = MYSY_GV.dbname['inscription'].update_many({'partner_owner_recid': str(my_partner['recid']),
                                                                'session_id': diction['session_id'],
                                                                'facture_client_rattachement_id': str(single_client),
                                                                "_id": {'$in': tab_my_inscription_ids_Object},
                                                                },
                                                               {'$set': update_data})

            tab_local_invoice_ref_interne.append(str(local_invoice_ref_interne))

        """
        07/03/20204 : mettre un statut de facturation sur la session afin de voir
        tout de suite quel session est entièrement facturée ou partiellement.

        regles : 
        Si toutes les inscription associées à une session sont facturée ==> invoiced_statut de la session = 2
        Si au moins une inscription associée à une session est facturé ==> invoiced_statut de la session = 1
        Si non invoiced_statut de la session =0
        """
        nb_inscription_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'invoiced': '1'})

        nb_inscription_non_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'invoiced': {'$ne': '1'}})

        nb_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'status': '1'})

        invoiced_statut = "0"
        if (nb_inscription_facture == nb_inscription_valide):
            # toutes les inscription valides ont été facturée
            invoiced_statut = "2"
        elif (nb_inscription_facture > 0):
            # Au moins une ligne a été facturée
            invoiced_statut = "1"

        # Mise à jour du statut de facturation de la session
        MYSY_GV.dbname['session_formation'].update_one({'partner_owner_recid': my_partner['recid'],
                                                        'valide': '1',
                                                        '_id': ObjectId(str(diction['session_id']))
                                                        },
                                                       {'$set': {'invoiced_statut': invoiced_statut}})

        # Creation de l'historique dans les action 'courrier_template_tracking_history'
        local_qry = {'partner_owner_recid': str(my_partner['recid']), 'session_id': str(diction['session_id']),
                     'invoiced_ref': {'$in': tab_local_invoice_ref_interne}}

        # print(" ###  local_qry = ", local_qry)
        """ 
        for val in MYSY_GV.dbname['inscription'].find({'partner_owner_recid': str(my_partner['recid']),
                                                       'session_id': str(diction['session_id']),
                                                       'invoiced_ref': {'$in': tab_local_invoice_ref_interne}}):

            ref_facture = ""
            if ("invoiced_ref" in val.keys()):
                ref_facture = val['invoiced_ref']

            local_status, local_retval = module_editique.Editic_Log_History_Action_From_courrier_template_type_document_ref_interne(
                my_partner, "FACTURATION_SESSION", str(diction['session_id']), 'inscription',
                str(val['_id']),
                str(diction['courrier_template_id']),
                "Facture : " + str(ref_facture)
            )

            if (local_status is False):
                mycommon.myprint(" WARNING : Impossible de logguer l'historique l'inscription_id : " + str(val['_id']))
        """
        return_message = " La session a été correctement facturée.\nListe des factures : "
        for tmp in tab_local_invoice_ref_interne:
            return_message += "\n - " + str(tmp)

        return True, str(return_message), tab_local_invoice_ref_interne

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer et d'envoyer les factures par email ", False




"""
Creation des factures pour un client avec uniquement la liste des inscriptions concernées.

Ex : je veux facturer que 2 des 5 inscrits d'un client donné.

/!\ : Cette fonction se base sur le champ "facture_client_rattachement_id" de la collection "inscription"
"""


def Invoice_Partner_From_Session_By_Inscription_Id( diction):
    try:

        field_list_obligatoire = ['token',  'partner_client_id', 'session_id', 'tab_inscription_ids']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        # Verifier que la session est valide
        is_session_id_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_id_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session est invalide ")
            return False, "  L'identifiant de la session est invalide ", False

        # Verifier que ce client a bien des inscriptions valide pour cette session
        nb_valide_inscription_pr_client = MYSY_GV.dbname['inscription'].count_documents(
            {'facture_client_rattachement_id': str(diction['partner_client_id']),
             'session_id': str(diction['session_id']),
             'partner_owner_recid': str(my_partner['recid']),
             'status': '1',
             '_id':{'$in':diction['tab_inscription_ids']}
             },
        )

        if (nb_valide_inscription_pr_client <= 0):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  Aucune inscription valide pour ce client pour cette session  ")
            return False, "  Aucune inscription valide pour ce client pour cette session ", False

        partner_client_id_data = MYSY_GV.dbname['partner_client'].find_one(
            {'_id': ObjectId(str(diction['partner_client_id'])),
             'valide': '1',
             'locked': '0',
             'partner_recid': str(my_partner['recid'])})





        # Recuperation des données du stagiaire
        inscription_data = MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']),
                                                                   'status': '1',
                                                                   'partner_owner_recid': str(my_partner['recid']),
                                                                   'facture_client_rattachement_id':str(diction['partner_client_id']),
                                                               '_id':{'$in':diction['tab_inscription_ids']}})

        tab_inscrit_partial_data = []
        tab_apprenant = []
        tab_participant = []
        for val in inscription_data:
            tab_participant.append(val['_id'])

            node = {}
            node['inscription_id'] = str(val['_id'])


            """
                    update du 30/01/2024 
                    si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
                    En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
                    Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
                    """

            if ("apprenant_id" in val.keys() and val['apprenant_id']):
                tab_apprenant.append(ObjectId(str(val['apprenant_id'])))
                node['apprenant_id'] = str(val['apprenant_id'])
            else:
                node['apprenant_id'] = ""

            if( "invoice_split" in val.keys() ):
                node['invoice_split'] = val['invoice_split']
            else:
                node['invoice_split'] = ""

            tab_inscrit_partial_data.append(node)




        print(" ### tab_inscrit_partial_data = ", tab_inscrit_partial_data)
        print(" ### tab_apprenant = ", tab_apprenant)

        # Recuperations des info de la session de formation
        session_data = MYSY_GV.dbname['session_formation'].find_one({'_id': ObjectId(str(diction['session_id']))})

        tab_session = []
        tab_session.append(session_data['_id'])


        # Recuperation du titre de la formation
        class_data = MYSY_GV.dbname['myclass'].find({'internal_url': str(session_data['class_internal_url']),
                                                     'valide': '1',
                                                     'partner_owner_recid': str(my_partner['recid']),
                                                     'locked': '0'})


        price_by = "perstagiaire"
        if( "perstagiaire" in session_data.keys() ):
            price_by = session_data['perstagiaire']
            if( price_by not in MYSY_GV.TRAINING_PRICE) :
                mycommon.myprint(str(inspect.stack()[0][3]) + " Le prix par " + str(price_by) + " n'est valide. Les valeurs autorisées sont " + str(MYSY_GV.TRAINING_PRICE))
                return False, " Le prix par " + str(price_by) + " n'est valide. Les valeurs autorisées sont " + str(MYSY_GV.TRAINING_PRICE) + " ", False

        partner_invoice_header_data = {}

        list_partner_invoice_header_champ =  ['order_header_client_id', 'order_header_ref_interne', 'order_header_email_client', 'order_header_origin', 'order_header_ref_client', 'order_header_vendeur_id',
          'order_header_date_cmd', 'order_header_date_expiration', 'order_header_adr_fact_adresse', 'order_header_adr_fact_code_postal', 'order_header_adr_fact_ville', 'order_header_adr_fact_pays',
          'order_header_adr_liv_adresse', 'order_header_adr_liv_code_postal', 'order_header_adr_liv_ville', 'order_header_adr_liv_pays', 'valide', 'locked', 'date_update',
          'order_header_montant_reduction', 'order_header_tax', 'order_header_tax_amount', 'total_header_hors_taxe_after_header_reduction', 'total_header_hors_taxe_before_header_reduction',
          'total_header_toutes_taxes', 'total_lines_hors_taxe_after_lines_reduction', 'total_lines_hors_taxe_before_lines_reduction', 'total_lines_montant_reduction', 'invoice_header_ref_interne',
          'invoice_header_type', 'invoice_date', 'update_by']

        # PreRemplir les champs
        for val in list_partner_invoice_header_champ:
            partner_invoice_header_data[str(val)] = ""


        partner_invoice_header_data['order_header_client_id'] = str(partner_client_id_data['_id'])

        """
        Recuperation des conditions de paiement depuis le client
        """
        ction_paiement_code = ""
        ction_paiement_desc = ""
        ction_paiement_depart = "facture"
        ction_paiement_nb_jour = "0"

        if( "invoice_condition_paiement_id" in partner_client_id_data.keys() and partner_client_id_data['invoice_condition_paiement_id']):
            ction_paiement_data = MYSY_GV.dbname['base_partner_paiement_condition'].find_one({'partner_owner_recid':my_partner['recid'],
                                                                                              'valide':'1',
                                                                                              'locked':'0',
                                                                                              '_id':ObjectId(str(partner_client_id_data['invoice_condition_paiement_id']))})

            if( ction_paiement_data and "code" in ction_paiement_data.keys() and ction_paiement_data['code']):
                ction_paiement_code = ction_paiement_data['code']

            if (ction_paiement_data and "description" in ction_paiement_data.keys() and ction_paiement_data['description']):
                ction_paiement_desc = ction_paiement_data['description']

            if (ction_paiement_data and "nb_jour" in ction_paiement_data.keys() and ction_paiement_data['nb_jour'] and
                    "depart" in ction_paiement_data.keys() and ction_paiement_data['depart'] ):
                ction_paiement_nb_jour = ction_paiement_data['nb_jour']
                ction_paiement_depart = ction_paiement_data['depart']



        nb_jour_int = mycommon.tryInt(str(ction_paiement_nb_jour))
        today = datetime.today()
        date_echance = datetime.today()

        if (str(ction_paiement_depart) == "mois"):
            days_in_month = lambda dt: monthrange(dt.year, dt.month)[1]
            first_day_next_month = today.replace(day=1) + timedelta(days_in_month(today))
            date_echance = first_day_next_month + timedelta(days=nb_jour_int)

        else:
            date_echance = today + timedelta(days=nb_jour_int)

        date_echance = date_echance.strftime("%d/%m/%Y")
        partner_invoice_header_data['invoice_date_echeance'] = str(date_echance)
        partner_invoice_header_data['order_header_condition_paiement_code'] = str(ction_paiement_code)
        partner_invoice_header_data['order_header_condition_paiement_description'] = str(ction_paiement_desc)


        code_session = ""
        if( "code_session" in session_data.keys() ):
            code_session = session_data['code_session']
        partner_invoice_header_data['order_header_ref_interne'] = "Code_Session_"+str(code_session)

        order_header_email_client = ""
        if ("email" in partner_client_id_data.keys()):
            order_header_email_client = partner_client_id_data['email']
        partner_invoice_header_data['order_header_email_client'] = order_header_email_client

        order_header_origin = "session_id_"+str(session_data['_id'])
        partner_invoice_header_data['order_header_origin'] = order_header_origin

        order_header_adr_fact_adresse = ""
        if( "invoice_adresse" in partner_client_id_data.keys() ):
            order_header_adr_fact_adresse = partner_client_id_data['invoice_adresse']
        partner_invoice_header_data['order_header_adr_fact_adresse'] = order_header_adr_fact_adresse

        order_header_adr_fact_ville = ""
        if ("invoice_ville" in partner_client_id_data.keys()):
            order_header_adr_fact_ville = partner_client_id_data['invoice_adresse']
        partner_invoice_header_data['order_header_adr_fact_ville'] = order_header_adr_fact_ville

        order_header_adr_fact_code_postal = ""
        if ("invoice_code_postal" in partner_client_id_data.keys()):
            order_header_adr_fact_code_postal = partner_client_id_data['invoice_code_postal']
        partner_invoice_header_data['order_header_adr_fact_code_postal'] = order_header_adr_fact_code_postal

        order_header_adr_fact_pays = ""
        if ("invoice_pays" in partner_client_id_data.keys()):
            order_header_adr_fact_pays = partner_client_id_data['invoice_pays']
        partner_invoice_header_data['order_header_adr_fact_pays'] = order_header_adr_fact_pays

        order_header_montant_reduction = "0"
        partner_invoice_header_data['order_header_montant_reduction'] = order_header_montant_reduction


        # Calcul du Totol HT sans reduction
        total_ht = 0
        prix_session = 0
        session_price = 0
        if( "prix_session" not in session_data.keys() ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : La session n'a pas de prix valide")
            return False, " Facturation : La session n'a pas de prix valide ", False

        if( str(session_data['prix_session']).strip() == "" ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : La session n'a pas de prix valide (2) ")
            return False, " Facturation : La session n'a pas de prix valide (2) ", False

        prix_session = mycommon.tryFloat(str(session_data['prix_session']))


        if( str(price_by).strip() == "persession" ):
            total_ht = round(prix_session, 2)
        else:
            total_ht = round(prix_session * nb_valide_inscription_pr_client, 2)

        partner_invoice_header_data['total_header_hors_taxe_before_header_reduction'] = total_ht

        # Recupération de la TVA de l'entité qui facture
        taux_tva_statuts, taux_tva_retval = partner_base_setup.Get_Given_Partner_Basic_Setup({'token':str(diction['token']), 'config_name':'tva'})

        if( taux_tva_statuts is False ):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : Impossible de récupérer le taux de TVA ")
            return False, " Facturation : Impossible de récupérer le taux de TVA ", False

        tmp =  ast.literal_eval(taux_tva_retval[0])
        taux_tva_retval = tmp['config_value']
        print(" ### taux_tva_retval =  ", taux_tva_retval)
        tva_status, tva_value = mycommon.IsFloat(str(taux_tva_retval))
        if (tva_status is False):
            mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : Le taux de TVA est invalide ")
            return False, " Facturation : Le taux de TVA est invalide ", False

        partner_invoice_header_data['order_header_tax'] = taux_tva_retval
        partner_invoice_header_data['order_header_tax_amount'] = str(round(tva_value * total_ht/100, 2))
        partner_invoice_header_data['total_header_toutes_taxes'] = str(round(total_ht + (tva_value * total_ht)/100, 2))
        partner_invoice_header_data['invoice_header_type'] = "facture"

        # Récuperation de la sequence de l'objet "partner_invoice_header" dans la collection : "mysy_sequence"
        retval_sequence_invoice = MYSY_GV.dbname['mysy_sequence'].find_one(
            {'partner_invoice_header': 'partner_order_header',
             'valide': '1', 'partner_owner_recid': str(
                my_partner['recid'])})

        if (retval_sequence_invoice is None):
            # Il n'y pas de sequence pour le partenaire, on va aller chercher la sequence par defaut
            retval_sequence_invoice = MYSY_GV.dbname['mysy_sequence'].find_one(
                {'related_mysy_object': 'partner_invoice_header',
                 'valide': '1', 'partner_owner_recid': 'default'})

        if (retval_sequence_invoice is None or "current_val" not in retval_sequence_invoice.keys()):
            # Il n'y aucune sequence meme par defaut.

            mycommon.myprint(" Facture : Impossible de récupérer la sequence 'retval_sequence_invoice' ")
            return False, "Facture : Impossible de récupérer la sequence 'retval_sequence_invoice' ", False

        current_seq_value = str(retval_sequence_invoice['current_val'])
        new_sequence_value = int(mycommon.tryInt(current_seq_value)) + 1
        new_sequance_data_to_update = {'current_val': new_sequence_value}

        ret_val2 = MYSY_GV.dbname['mysy_sequence'].find_one_and_update(
            {'_id': ObjectId(str(retval_sequence_invoice['_id'])), 'valide': '1'},
            {"$set": new_sequance_data_to_update},
            return_document=ReturnDocument.AFTER,
            upsert=False,
        )
        invoice_date_time = str(datetime.now().strftime("%d/%m/%Y"))


        """
        Verifier qu'il n'y pas une facture du partenaire avec le meme ref interne
        """
        is_already_invoice_ref_exist = MYSY_GV.dbname['partner_invoice_header'].count_documents({'partner_invoice_header':str(my_partner['recid']),
                                                                                                 'valide':'1',
                                                                                                 'invoice_header_ref_interne':str(retval_sequence_invoice['prefixe'] + str(current_seq_value))})

        if( is_already_invoice_ref_exist > 0 ):
            mycommon.myprint(" Facture : Il existe déjà une facture avec la même ref. interne : "+str(retval_sequence_invoice['prefixe'] + str(current_seq_value)))
            return False, " Facture : Il existe déjà une facture avec la même ref. interne : "+str(retval_sequence_invoice['prefixe'] + str(current_seq_value)), False


        partner_invoice_header_data['invoice_header_ref_interne'] = retval_sequence_invoice['prefixe'] + str(current_seq_value)
        partner_invoice_header_data['invoice_header_type'] = "facture"
        partner_invoice_header_data['invoice_date'] = invoice_date_time
        partner_invoice_header_data['update_by'] = str(my_partner['_id'])
        partner_invoice_header_data['valide'] = "1"
        partner_invoice_header_data['locked'] = "0"
        partner_invoice_header_data['partner_owner_recid'] = str(my_partner['recid'])
        partner_invoice_header_data['date_update'] = str(datetime.now())


        print(" #### partner_invoice_header_data = ", partner_invoice_header_data)
        inserted_invoice_id = MYSY_GV.dbname['partner_invoice_header'].insert_one(partner_invoice_header_data).inserted_id
        if (not inserted_invoice_id):
            mycommon.myprint(" Facture : Impossible de créer l'entête de la facture ")
            return False, " Facture : Impossible de créer l'entête de la facture ", False

        new_invoice_id = inserted_invoice_id

        """
        Création des lignes de facture.
        Pour memo, dans la collection : partner_invoice_line
        order_line_formation = titre formation
        order_line_qty = nb participants
        order_line_comment =  la liste des personnes participans
        """

        partner_invoice_line_data = {}
        list_partner_invoice_line_champ = ['order_line_formation', 'order_line_qty', 'order_line_prix_unitaire', 'order_line_tax', 'order_line_tax_amount', 'order_line_montant_toutes_taxes',
             'order_line_montant_hors_taxes', 'order_line_type_reduction', 'order_line_type_valeur', 'order_line_montant_reduction', 'order_header_ref_interne',
             'order_line_comment', 'order_header_id', 'valide', 'locked', 'date_update', 'partner_owner_recid', 'invoice_header_ref_interne', 'invoice_line_type',
             'invoice_date', 'invoice_header_id']


        # PreRemplir les champs
        for val in list_partner_invoice_line_champ:
            partner_invoice_line_data[str(val)] = ""

        nb_participant_du_client = len(tab_apprenant)

        nom_prenom_email_participant = ""
        for val in tab_apprenant:
            local_apprenant = MYSY_GV.dbname['apprenant'].find_one({'_id':ObjectId(val), 'partner_owner_recid':str(my_partner['recid']),
                                                                    'valide':'1', 'locked':'0'})

            local_nom = ""
            local_prenom = ""
            local_email = ""
            if( "nom" in local_apprenant.keys() ):
                local_nom = local_apprenant['nom']

            if ("prenom" in local_apprenant.keys()):
                local_prenom = local_apprenant['prenom']

            if ("email" in local_apprenant.keys()):
                local_email = local_apprenant['email']

            nom_prenom_email_participant += local_nom+" "+local_prenom+" "+local_email+"\n"

        partner_invoice_line_data['order_line_formation'] = class_data[0]['internal_url']
        partner_invoice_line_data['order_line_qty'] = str(nb_participant_du_client)
        partner_invoice_line_data['order_line_prix_unitaire'] = str(prix_session)
        partner_invoice_line_data['order_line_montant_hors_taxes'] = str(total_ht)
        partner_invoice_line_data['order_line_comment'] = str(nom_prenom_email_participant)
        partner_invoice_line_data['invoice_header_id'] = str(inserted_invoice_id)
        partner_invoice_line_data['invoice_line_type'] = "facture"
        partner_invoice_line_data['invoice_header_ref_interne'] = partner_invoice_header_data['invoice_header_ref_interne']
        partner_invoice_line_data['update_by'] = str(my_partner['_id'])
        partner_invoice_line_data['valide'] = "1"
        partner_invoice_line_data['locked'] = "0"
        partner_invoice_line_data['partner_owner_recid'] = str(my_partner['recid'])

        print(" #### partner_invoice_line_data = ", partner_invoice_line_data)
        inserted_invoice_id = MYSY_GV.dbname['partner_invoice_line'].insert_one(
            partner_invoice_line_data).inserted_id
        if (not inserted_invoice_id):
            mycommon.myprint(" Facture : Impossible de créer les lignes  de la facture "+str(partner_invoice_header_data['invoice_header_ref_interne']))
            return False, " Facture : Impossible de créer les lignes  de la facture "+str(partner_invoice_header_data['invoice_header_ref_interne']), False


        """
        05/06/2024 Gestion E-Facture
        Apres la creation de la facture, on va aller créer le document securisé 
        """
        e_Invoice_Diction = {}
        e_Invoice_Diction['token'] = diction['token']
        e_Invoice_Diction['invoice_id'] = str(new_invoice_id)

        print(" ### e_Invoice_Diction= ", e_Invoice_Diction )
        local_E_Invoice_status, local_E_Invoice_retval = Invoice_Create_Secure_E_Document(e_Invoice_Diction)
        if( local_E_Invoice_status is False ):
            return True, "WARNING : L'email a été correctement envoyé ", str(
                partner_invoice_header_data['invoice_header_ref_interne']+"; mais impossible de créer la e-Facture Sécurisée (2).")

        return True, "L'email a été correctement envoyé ", str(partner_invoice_header_data['invoice_header_ref_interne'])

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer la convention par email ", False



"""
15/06/2024 :
Gestion des factures splitées sur une inscription :
Cette fonction prend une inscription_id, 

On créer les differentes factures avec les montants ou pourcentage de client designé dans le split
Ici, on ne fait pas de regroupement
"""

def Invoice_Splited_Partner_From_Session_By_Inscription_Id( diction):
    try:

        field_list_obligatoire = ['token',  'session_id', 'inscription_id']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "  La valeur '" + val + "' n'est pas presente dans liste", False

        my_token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                my_token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner, False

        # Verifier que la session est valide
        is_session_id_valide = MYSY_GV.dbname['session_formation'].count_documents(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        if (is_session_id_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de la session est invalide ")
            return False, "  L'identifiant de la session est invalide ", False

        """
        Verfifer que l'inscription est valide
        """
        inscription_valide = MYSY_GV.dbname['inscription'].count_documents({'session_id': str(diction['session_id']),
                                                                   'status': '1',
                                                                            '_id':ObjectId(str(diction['inscription_id'])),
                                                                   'partner_owner_recid': str(my_partner['recid']),
                                                                            "invoice_split": {'$ne': ''},
                                                                            'invoice_split': {'$exists': True},
                                                                            "invoiced": {'$ne': '1'},

                                                                            })
        if (inscription_valide != 1):
            mycommon.myprint(
                str(inspect.stack()[0][
                        3]) + "  L'identifiant de l'inscription est invalide  ")
            return False, "   L'identifiant de l'inscription est invalide  ", False


        # Recuperation des données du stagiaire
        inscription_data = MYSY_GV.dbname['inscription'].find_one({'session_id': str(diction['session_id']),
                                                                   'status': '1',
                                                                            '_id':ObjectId(str(diction['inscription_id'])),
                                                                   'partner_owner_recid': str(my_partner['recid']),
                                                                            "invoice_split": {'$ne': ''},
                                                                            'invoice_split': {'$exists': True},
                                                                   "invoiced": {'$ne': '1'},

                                                                            })



        # Verifier que le mode d'eclatement de la factue est soit : percent, soit fixe
        invoice_split = ""
        split_type = ""

        if ("invoice_split" in inscription_data.keys() and inscription_data['invoice_split'] and
                "split_type" in inscription_data['invoice_split'].keys() and inscription_data['invoice_split']['split_type']):
            if (str(inscription_data['invoice_split']['split_type']).lower() not in ['percent', 'fixe']):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + " Le mode de partage de la facture doit être 'percent' ou 'fixe' ")
                return False, "   Le mode de partage de la facture doit être 'percent' ou 'fixe'  ", False

            split_type = str(inscription_data['invoice_split']['split_type'])

        if ("invoice_split" in inscription_data.keys() and inscription_data['invoice_split'] and
                "tab_split" in inscription_data['invoice_split'].keys() and inscription_data['invoice_split']['tab_split']):

            invoice_split = inscription_data['invoice_split']

            for tmp in inscription_data['invoice_split']['tab_split']:
                if ("invoice_part" in tmp.keys()):
                    is_float_status, is_float_retaval = mycommon.IsFloat(str(tmp['invoice_part']))

                    if (is_float_status is False):
                        mycommon.myprint(
                            str(inspect.stack()[0][
                                    3]) + " La valeur de partage de la facture n'est pas un nombre decimal ")
                        return False, "  La valeur de partage de la facture n'est pas un nombre decimal   ", False

                    elif (is_float_retaval < 0):
                        mycommon.myprint(
                            str(inspect.stack()[0][
                                    3]) + " La valeur de partage de la facture est inférieure à 0  ")
                        return False, "  La valeur de partage de la facture est inférieure à 0    ", False


        # Créer une facture pour chaque client dans le split
        print(" ### pour l'inscription  = ", str(diction['inscription_id']) )

        tab_invoice = []
        tab_date_invoice = []


        for one_tab_split in invoice_split['tab_split'] :
            print(" ### one_tab_split = ", one_tab_split)
            print(" ### split_type = ", split_type)
            print(" ### invoice_part = ", one_tab_split['invoice_part'])
            split_invoice_part = one_tab_split['invoice_part']


            partner_client_id_data = MYSY_GV.dbname['partner_client'].find_one(
                {'_id': ObjectId(str(one_tab_split['partner_client'])),
                 'valide': '1',
                 'locked': '0',
                 'partner_recid': str(my_partner['recid'])})


            tab_inscrit_partial_data = []
            tab_apprenant = []
            tab_participant = []

            """
            update du 30/01/2024 
            si le participant à un apprenant_id, alors on recupere la valeur de l'apprenant id. On doit travailler sur cette dernier.
            En general, avant d'envoyer une convocation ou convention, l'inscription est validée et donc le dossier apprenant existe.
            Du coup si tout se passe bien, dans cette fonction, on travaillera tjrs avec l'apprenant_id
                """

            if ("apprenant_id" in inscription_data.keys() and inscription_data['apprenant_id']):
                tab_apprenant.append(ObjectId(str(inscription_data['apprenant_id'])))


            print(" ### tab_apprenant = ", tab_apprenant)

            # Recuperations des info de la session de formation
            session_data = MYSY_GV.dbname['session_formation'].find_one({'_id': ObjectId(str(diction['session_id']))})

            tab_session = []
            tab_session.append(session_data['_id'])


            # Recuperation du titre de la formation
            class_data = MYSY_GV.dbname['myclass'].find({'internal_url': str(session_data['class_internal_url']),
                                                         'valide': '1',
                                                         'partner_owner_recid': str(my_partner['recid']),
                                                         'locked': '0'})


            price_by = "perstagiaire"
            if( "perstagiaire" in session_data.keys() ):
                price_by = session_data['perstagiaire']
                if( price_by not in MYSY_GV.TRAINING_PRICE) :
                    mycommon.myprint(str(inspect.stack()[0][3]) + " Le prix par " + str(price_by) + " n'est valide. Les valeurs autorisées sont " + str(MYSY_GV.TRAINING_PRICE))
                    return False, " Le prix par " + str(price_by) + " n'est valide. Les valeurs autorisées sont " + str(MYSY_GV.TRAINING_PRICE) + " ", False

            partner_invoice_header_data = {}

            list_partner_invoice_header_champ =  ['order_header_client_id', 'order_header_ref_interne', 'order_header_email_client', 'order_header_origin', 'order_header_ref_client', 'order_header_vendeur_id',
              'order_header_date_cmd', 'order_header_date_expiration', 'order_header_adr_fact_adresse', 'order_header_adr_fact_code_postal', 'order_header_adr_fact_ville', 'order_header_adr_fact_pays',
              'order_header_adr_liv_adresse', 'order_header_adr_liv_code_postal', 'order_header_adr_liv_ville', 'order_header_adr_liv_pays', 'valide', 'locked', 'date_update',
              'order_header_montant_reduction', 'order_header_tax', 'order_header_tax_amount', 'total_header_hors_taxe_after_header_reduction', 'total_header_hors_taxe_before_header_reduction',
              'total_header_toutes_taxes', 'total_lines_hors_taxe_after_lines_reduction', 'total_lines_hors_taxe_before_lines_reduction', 'total_lines_montant_reduction', 'invoice_header_ref_interne',
              'invoice_header_type', 'invoice_date', 'update_by']

            # PreRemplir les champs
            for val in list_partner_invoice_header_champ:
                partner_invoice_header_data[str(val)] = ""


            partner_invoice_header_data['order_header_client_id'] = str(partner_client_id_data['_id'])

            """
            Recuperation des conditions de paiement depuis le client
            """
            ction_paiement_code = ""
            ction_paiement_desc = ""
            ction_paiement_depart = "facture"
            ction_paiement_nb_jour = "0"

            if( "invoice_condition_paiement_id" in partner_client_id_data.keys() and partner_client_id_data['invoice_condition_paiement_id']):
                ction_paiement_data = MYSY_GV.dbname['base_partner_paiement_condition'].find_one({'partner_owner_recid':my_partner['recid'],
                                                                                                  'valide':'1',
                                                                                                  'locked':'0',
                                                                                                  '_id':ObjectId(str(partner_client_id_data['invoice_condition_paiement_id']))})

                if( ction_paiement_data and "code" in ction_paiement_data.keys() and ction_paiement_data['code']):
                    ction_paiement_code = ction_paiement_data['code']

                if (ction_paiement_data and "description" in ction_paiement_data.keys() and ction_paiement_data['description']):
                    ction_paiement_desc = ction_paiement_data['description']

                if (ction_paiement_data and "nb_jour" in ction_paiement_data.keys() and ction_paiement_data['nb_jour'] and
                        "depart" in ction_paiement_data.keys() and ction_paiement_data['depart'] ):
                    ction_paiement_nb_jour = ction_paiement_data['nb_jour']
                    ction_paiement_depart = ction_paiement_data['depart']



            nb_jour_int = mycommon.tryInt(str(ction_paiement_nb_jour))
            today = datetime.today()
            date_echance = datetime.today()

            if (str(ction_paiement_depart) == "mois"):
                days_in_month = lambda dt: monthrange(dt.year, dt.month)[1]
                first_day_next_month = today.replace(day=1) + timedelta(days_in_month(today))
                date_echance = first_day_next_month + timedelta(days=nb_jour_int)

            else:
                date_echance = today + timedelta(days=nb_jour_int)

            date_echance = date_echance.strftime("%d/%m/%Y")
            partner_invoice_header_data['invoice_date_echeance'] = str(date_echance)
            partner_invoice_header_data['order_header_condition_paiement_code'] = str(ction_paiement_code)
            partner_invoice_header_data['order_header_condition_paiement_description'] = str(ction_paiement_desc)


            code_session = ""
            if( "code_session" in session_data.keys() ):
                code_session = session_data['code_session']
            partner_invoice_header_data['order_header_ref_interne'] = "Code_Session_"+str(code_session)

            order_header_email_client = ""
            if ("email" in partner_client_id_data.keys()):
                order_header_email_client = partner_client_id_data['email']
            partner_invoice_header_data['order_header_email_client'] = order_header_email_client

            order_header_origin = "session_id_"+str(session_data['_id'])
            partner_invoice_header_data['order_header_origin'] = order_header_origin

            order_header_adr_fact_adresse = ""
            if( "invoice_adresse" in partner_client_id_data.keys() ):
                order_header_adr_fact_adresse = partner_client_id_data['invoice_adresse']
            partner_invoice_header_data['order_header_adr_fact_adresse'] = order_header_adr_fact_adresse

            order_header_adr_fact_ville = ""
            if ("invoice_ville" in partner_client_id_data.keys()):
                order_header_adr_fact_ville = partner_client_id_data['invoice_adresse']
            partner_invoice_header_data['order_header_adr_fact_ville'] = order_header_adr_fact_ville

            order_header_adr_fact_code_postal = ""
            if ("invoice_code_postal" in partner_client_id_data.keys()):
                order_header_adr_fact_code_postal = partner_client_id_data['invoice_code_postal']
            partner_invoice_header_data['order_header_adr_fact_code_postal'] = order_header_adr_fact_code_postal

            order_header_adr_fact_pays = ""
            if ("invoice_pays" in partner_client_id_data.keys()):
                order_header_adr_fact_pays = partner_client_id_data['invoice_pays']
            partner_invoice_header_data['order_header_adr_fact_pays'] = order_header_adr_fact_pays

            order_header_montant_reduction = "0"
            partner_invoice_header_data['order_header_montant_reduction'] = order_header_montant_reduction


            # Calcul du Totol HT sans reduction
            total_ht = 0
            prix_session = 0
            session_price = 0
            if( "prix_session" not in session_data.keys() ):
                mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : La session n'a pas de prix valide")
                return False, " Facturation : La session n'a pas de prix valide ", False

            if( str(session_data['prix_session']).strip() == "" ):
                mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : La session n'a pas de prix valide (2) ")
                return False, " Facturation : La session n'a pas de prix valide (2) ", False

            prix_session = mycommon.tryFloat(str(session_data['prix_session']))


            if( str(price_by).strip() == "persession" ):
                total_ht = round(prix_session, 2)
            else:
                total_ht = round(prix_session * 1, 2)


            # Recupération de la TVA de l'entité qui facture
            taux_tva_statuts, taux_tva_retval = partner_base_setup.Get_Given_Partner_Basic_Setup({'token':str(diction['token']), 'config_name':'tva'})

            if( taux_tva_statuts is False ):
                mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : Impossible de récupérer le taux de TVA ")
                return False, " Facturation : Impossible de récupérer le taux de TVA ", False

            tmp =  ast.literal_eval(taux_tva_retval[0])
            taux_tva_retval = tmp['config_value']

            print(" ### taux_tva_retval =  ", taux_tva_retval)
            tva_status, tva_value = mycommon.IsFloat(str(taux_tva_retval))
            if (tva_status is False):
                mycommon.myprint(str(inspect.stack()[0][3]) + " Facturation : Le taux de TVA est invalide ")
                return False, " Facturation : Le taux de TVA est invalide ", False

            split_invoice_part_FLOAT = mycommon.tryFloat(str(split_invoice_part))

            if(split_type == "percent" ):
                split_invoice_part_FLOAT = round((split_invoice_part_FLOAT * total_ht)/100, 2)


            print(" ### dans la facturartion : split_type = ", str(split_type))
            print(" ### dans la facturartion : split_invoice_part_FLOAT = ", str(split_invoice_part_FLOAT))

            partner_invoice_header_data['total_header_hors_taxe_before_header_reduction'] = str(split_invoice_part_FLOAT)

            partner_invoice_header_data['order_header_tax'] = taux_tva_retval
            partner_invoice_header_data['order_header_tax_amount'] = str(round(tva_value * split_invoice_part_FLOAT/100, 2))
            partner_invoice_header_data['total_header_toutes_taxes'] = str(round(split_invoice_part_FLOAT + (tva_value * split_invoice_part_FLOAT)/100, 2))
            partner_invoice_header_data['invoice_header_type'] = "facture"

            text_comment = "Montant total HT = "+str(total_ht)
            if( str(split_type) == "percent"):
                text_comment = text_comment + "\nType partage facture : POURCENTAGE \nMontant de la facture = "+str(split_invoice_part)
            elif (str(split_type) == "fixe"):
                text_comment = text_comment + "\nType partage facture : MONTANT \nMontant de la facture = "+str(split_invoice_part)
            else :
                text_comment = text_comment + "\nType partage facture : INCONNU \nMontant de la facture = "+str(split_invoice_part)

            partner_invoice_header_data['order_header_comment'] = text_comment

            partner_invoice_header_data['annotation'] = text_comment

            partner_invoice_header_data['split_type'] = split_type
            partner_invoice_header_data['split_invoice_part'] = str(split_invoice_part)

            # Récuperation de la sequence de l'objet "partner_invoice_header" dans la collection : "mysy_sequence"
            retval_sequence_invoice = MYSY_GV.dbname['mysy_sequence'].find_one(
                {'partner_invoice_header': 'partner_order_header',
                 'valide': '1', 'partner_owner_recid': str(
                    my_partner['recid'])})

            if (retval_sequence_invoice is None):
                # Il n'y pas de sequence pour le partenaire, on va aller chercher la sequence par defaut
                retval_sequence_invoice = MYSY_GV.dbname['mysy_sequence'].find_one(
                    {'related_mysy_object': 'partner_invoice_header',
                     'valide': '1', 'partner_owner_recid': 'default'})

            if (retval_sequence_invoice is None or "current_val" not in retval_sequence_invoice.keys()):
                # Il n'y aucune sequence meme par defaut.

                mycommon.myprint(" Facture : Impossible de récupérer la sequence 'retval_sequence_invoice' ")
                return False, "Facture : Impossible de récupérer la sequence 'retval_sequence_invoice' ", False

            current_seq_value = str(retval_sequence_invoice['current_val'])
            new_sequence_value = int(mycommon.tryInt(current_seq_value)) + 1
            new_sequance_data_to_update = {'current_val': new_sequence_value}

            ret_val2 = MYSY_GV.dbname['mysy_sequence'].find_one_and_update(
                {'_id': ObjectId(str(retval_sequence_invoice['_id'])), 'valide': '1'},
                {"$set": new_sequance_data_to_update},
                return_document=ReturnDocument.AFTER,
                upsert=False,
            )
            invoice_date_time = str(datetime.now().strftime("%d/%m/%Y"))


            """
            Verifier qu'il n'y pas une facture du partenaire avec le meme ref interne
            """
            is_already_invoice_ref_exist = MYSY_GV.dbname['partner_invoice_header'].count_documents({'partner_invoice_header':str(my_partner['recid']),
                                                                                                     'valide':'1',
                                                                                                     'invoice_header_ref_interne':str(retval_sequence_invoice['prefixe'] + str(current_seq_value))})

            if( is_already_invoice_ref_exist > 0 ):
                mycommon.myprint(" Facture : Il existe déjà une facture avec la même ref. interne : "+str(retval_sequence_invoice['prefixe'] + str(current_seq_value)))
                return False, " Facture : Il existe déjà une facture avec la même ref. interne : "+str(retval_sequence_invoice['prefixe'] + str(current_seq_value)), False


            partner_invoice_header_data['invoice_header_ref_interne'] = retval_sequence_invoice['prefixe'] + str(current_seq_value)
            partner_invoice_header_data['invoice_header_type'] = "facture"
            partner_invoice_header_data['invoice_date'] = invoice_date_time
            partner_invoice_header_data['update_by'] = str(my_partner['_id'])
            partner_invoice_header_data['valide'] = "1"
            partner_invoice_header_data['locked'] = "0"
            partner_invoice_header_data['partner_owner_recid'] = str(my_partner['recid'])
            partner_invoice_header_data['date_update'] = str(datetime.now())


            print(" #### partner_invoice_header_data = ", partner_invoice_header_data)
            inserted_invoice_id = MYSY_GV.dbname['partner_invoice_header'].insert_one(partner_invoice_header_data).inserted_id
            if (not inserted_invoice_id):
                mycommon.myprint(" Facture : Impossible de créer l'entête de la facture ")
                return False, " Facture : Impossible de créer l'entête de la facture ", False

            new_invoice_id = inserted_invoice_id

            """
            Création des lignes de facture.
            Pour memo, dans la collection : partner_invoice_line
            order_line_formation = titre formation
            order_line_qty = nb participants
            order_line_comment =  la liste des personnes participans
            """

            partner_invoice_line_data = {}
            list_partner_invoice_line_champ = ['order_line_formation', 'order_line_qty', 'order_line_prix_unitaire', 'order_line_tax', 'order_line_tax_amount', 'order_line_montant_toutes_taxes',
                 'order_line_montant_hors_taxes', 'order_line_type_reduction', 'order_line_type_valeur', 'order_line_montant_reduction', 'order_header_ref_interne',
                 'order_line_comment', 'order_header_id', 'valide', 'locked', 'date_update', 'partner_owner_recid', 'invoice_header_ref_interne', 'invoice_line_type',
                 'invoice_date', 'invoice_header_id']


            # PreRemplir les champs
            for val in list_partner_invoice_line_champ:
                partner_invoice_line_data[str(val)] = ""

            nb_participant_du_client = len(tab_apprenant)

            nom_prenom_email_participant = ""
            for val in tab_apprenant:
                local_apprenant = MYSY_GV.dbname['apprenant'].find_one({'_id':ObjectId(val), 'partner_owner_recid':str(my_partner['recid']),
                                                                        'valide':'1', 'locked':'0'})

                local_nom = ""
                local_prenom = ""
                local_email = ""
                if( "nom" in local_apprenant.keys() ):
                    local_nom = local_apprenant['nom']

                if ("prenom" in local_apprenant.keys()):
                    local_prenom = local_apprenant['prenom']

                if ("email" in local_apprenant.keys()):
                    local_email = local_apprenant['email']

                nom_prenom_email_participant += local_nom+" "+local_prenom+" "+local_email+"\n"

            partner_invoice_line_data['order_line_formation'] = class_data[0]['internal_url']
            partner_invoice_line_data['order_line_qty'] = str(nb_participant_du_client)
            partner_invoice_line_data['order_line_prix_unitaire'] = str(prix_session)
            partner_invoice_line_data['order_line_montant_hors_taxes'] = str(total_ht)
            partner_invoice_line_data['order_line_comment'] = str(nom_prenom_email_participant)
            partner_invoice_line_data['invoice_header_id'] = str(inserted_invoice_id)
            partner_invoice_line_data['invoice_line_type'] = "facture"
            partner_invoice_line_data['invoice_header_ref_interne'] = partner_invoice_header_data['invoice_header_ref_interne']
            partner_invoice_line_data['update_by'] = str(my_partner['_id'])
            partner_invoice_line_data['valide'] = "1"
            partner_invoice_line_data['locked'] = "0"
            partner_invoice_line_data['partner_owner_recid'] = str(my_partner['recid'])

            print(" #### partner_invoice_line_data = ", partner_invoice_line_data)
            inserted_invoice_id = MYSY_GV.dbname['partner_invoice_line'].insert_one(
                partner_invoice_line_data).inserted_id
            if (not inserted_invoice_id):
                mycommon.myprint(" Facture : Impossible de créer les lignes  de la facture "+str(partner_invoice_header_data['invoice_header_ref_interne']))
                return False, " Facture : Impossible de créer les lignes  de la facture "+str(partner_invoice_header_data['invoice_header_ref_interne']), False

            tab_invoice.append(partner_invoice_header_data['invoice_header_ref_interne'])
            now = str(datetime.now())
            tab_date_invoice.append(str(now))

            """
            05/06/2024 Gestion E-Facture
            Apres la creation de la facture, on va aller créer le document securisé 
            """
            e_Invoice_Diction = {}
            e_Invoice_Diction['token'] = diction['token']
            e_Invoice_Diction['invoice_id'] = str(new_invoice_id)

            print(" ### e_Invoice_Diction= ", e_Invoice_Diction )
            local_E_Invoice_status, local_E_Invoice_retval = Invoice_Create_Secure_E_Document(e_Invoice_Diction)
            if( local_E_Invoice_status is False ):
                return True, "WARNING : L'email a été correctement envoyé ", str(
                    partner_invoice_header_data['invoice_header_ref_interne']+"; mais impossible de créer la e-Facture Sécurisée (2).")

        """
                    Mettre à jour les lignes associées à ce client pour dire que la ligne est facturé
                    """

        update_data = {}
        update_data['invoiced'] = "1"
        update_data['invoiced_ref'] = ", ".join(tab_invoice)
        update_data['invoiced_date'] =  ", ".join(tab_date_invoice)
        update_data['date_update'] = now
        update_data['update_by'] = str(my_partner['_id'])

        update = MYSY_GV.dbname['inscription'].update_one({'partner_owner_recid': str(my_partner['recid']),
                                                            'session_id': diction['session_id'],
                                                            "_id": ObjectId(str(diction['inscription_id']))
                                                            },
                                                           {'$set': update_data})

        """
                07/03/20204 : mettre un statut de facturation sur la session afin de voir
                tout de suite quel session est entièrement facturée ou partiellement.

                regles : 
                Si toutes les inscription associées à une session sont facturée ==> invoiced_statut de la session = 2
                Si au moins une inscription associée à une session est facturé ==> invoiced_statut de la session = 1
                Si non invoiced_statut de la session =0
                """
        nb_inscription_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'invoiced': '1'})

        nb_inscription_non_facture = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'invoiced': {'$ne': '1'}})

        nb_inscription_valide = MYSY_GV.dbname['inscription'].count_documents(
            {'partner_owner_recid': my_partner['recid'],
             'status': '1'})

        invoiced_statut = "0"
        if (nb_inscription_facture == nb_inscription_valide):
            # toutes les inscription valides ont été facturée
            invoiced_statut = "2"
        elif (nb_inscription_facture > 0):
            # Au moins une ligne a été facturée
            invoiced_statut = "1"

        # Mise à jour du statut de facturation de la session
        MYSY_GV.dbname['session_formation'].update_one({'partner_owner_recid': my_partner['recid'],
                                                        'valide': '1',
                                                        '_id': ObjectId(str(diction['session_id']))
                                                        },
                                                       {'$set': {'invoiced_statut': invoiced_statut}})

        return True, "L'email a été correctement envoyé ", str(tab_invoice)

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de génrer la facture partagée ", False








"""
05/06/2024 - Gestion des factures securisée
A la creation d'une facture, on va aller créer un document sécurisé dans la collection "e_document_signe"
en utilisant le modelè de courrier pdf

en suite, on ajoute à la collection "invoice_hader", l'_id du document securisé.
Par la suite lorsqu'un utisateur imprime ou reimprime une facture, le va aller regarder
si il y a un document securisé associé, si oui il recuper ce donc, si non, il imprime la facture comme cela se fait aujourd'hui

"""
def Invoice_Create_Secure_E_Document(diction):
    try:
        diction = mycommon.strip_dictionary(diction)

        """
        Verification des input acceptés
        """
        field_list = ['token', 'invoice_id']

        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list and val.startswith('my_') is False:
                mycommon.myprint(str(
                    inspect.stack()[0][3]) + "  Le champ  '" + val + "' n'est pas autorisé")
                return False, " Les informations fournies sont incorrectes"

        """
        Verification des champs obligatoires
        """
        field_list_obligatoire = ['token', 'invoice_id']

        """
        Verification de l'identité et autorisation de l'entité qui
        appelle cette API
        """
        token = ""
        if ("token" in diction.keys()):
            if diction['token']:
                token = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner


        """
        Verifier que la facture est valide
        """
        is_invoice_valide = MYSY_GV.dbname['partner_invoice_header'].count_documents({'_id':ObjectId(str(diction['invoice_id'])),
                                                                                      'valide':'1',
                                                                                      'locked':'0',
                                                                                      'partner_owner_recid':str(my_partner['recid'])})

        if( is_invoice_valide !=  1):
            mycommon.myprint(str(
                inspect.stack()[0][3]) + " L'identifiant de la facture est invalide ")
            return False, " L'identifiant de la facture est invalide "


        Order_header_data = MYSY_GV.dbname['partner_invoice_header'].find_one(
            {'_id': ObjectId(str(diction['invoice_id'])),
             'valide': '1',
             'locked': '0',
             'partner_owner_recid': str(my_partner['recid'])})


        """
        Recuperer du modèle de document
        """
        partner_document_PART_INVOICE_data = MYSY_GV.dbname['courrier_template'].find_one(
            {'partner_owner_recid': str(my_partner['recid']),
             'valide': '1', 'locked': '0', 'ref_interne': 'PART_INVOICE',
             'type_doc': 'pdf'})

        if (partner_document_PART_INVOICE_data is None):
            mycommon.myprint(str(
                inspect.stack()[0][3]) + " Aucun modèle de courrier 'PART_INVOICE' n'est configuré pour le partenaire ")
            return False, " Aucun modèle de courrier 'PART_INVOICE' n'est configuré pour le partenaire "



        e_document_id = ""
        # Recuperation des données du client
        if ("order_header_client_id" in Order_header_data.keys()):
            Order_header_client_data = MYSY_GV.dbname['partner_client'].find_one(
                {'_id': ObjectId(str(Order_header_data['order_header_client_id'])),
                 'partner_recid': str(my_partner['recid']), 'valide': '1', 'locked': '0'})

            if (Order_header_client_data is None):
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " - Le client est invalide")
                return False, " Le client est invalide"

        ### Ajout des données du client sur l'entete de la commande, exemple : le nom, email, etc
        if ("raison_sociale" in Order_header_client_data.keys()):
            Order_header_data['client_raison_sociale'] = Order_header_client_data['raison_sociale']

        if ("nom" in Order_header_client_data.keys()):
            Order_header_data['client_nom'] = Order_header_client_data['nom']

        if ("email" in Order_header_client_data.keys()):
            Order_header_data['client_email'] = Order_header_client_data['email']

        # Ajout d'un parametre pour le data time du jour de l'edition (c'est une data static qui peut servir pour l'horodatage
        Order_header_data['current_date_time'] = str(datetime.now().strftime("%d/%m/%Y, %H:%M:%S"))

        # Recuperation des details de lignes de : partner_invoice_line
        filt_class_partner_recid = {'partner_owner_recid': str(my_partner['recid'])}
        filt_order_header_order_id = {'invoice_header_id': str(diction['invoice_id'])}

        query = [{'$match': {'$and': [filt_order_header_order_id,
                                      {'partner_owner_recid': str(my_partner['recid'])}]}},
                 {'$lookup':
                     {
                         'from': 'myclass',
                         'localField': 'order_line_formation',
                         'foreignField': 'internal_url',
                         'pipeline': [{'$match': {'$and': [filt_class_partner_recid]}},
                                      {'$project': {'title': 1, 'domaine': 1,
                                                    'duration': 1,
                                                    'duration_unit': 1,
                                                    'external_code': 1}}],
                         'as': 'myclass_collection'
                     }
                 }
                 ]
        # print("#### Get_Given_Partner_Order_Lines_From_order_ref_interne  : query pip= ", query)
        val_tmp = 0
        Order_header_lines_data = []
        for retval in MYSY_GV.dbname['partner_invoice_line'].aggregate(query):
            if ('myclass_collection' in retval.keys() and len(retval['myclass_collection']) > 0):
                user = {}
                user['id'] = str(val_tmp)
                val_tmp = val_tmp + 1
                user['_id'] = retval['_id']
                user['order_line_formation'] = retval['order_line_formation']
                user['order_line_qty'] = retval['order_line_qty']
                user['order_line_prix_unitaire'] = retval['order_line_prix_unitaire']
                user['order_header_id'] = retval['order_header_id']
                user['order_header_ref_interne'] = retval['order_header_ref_interne']
                user['invoice_header_ref_interne'] = retval['invoice_header_ref_interne']
                user['invoice_line_type'] = retval['invoice_line_type']
                user['invoice_date'] = retval['invoice_date']

                user['valide'] = retval['valide']
                user['locked'] = retval['locked']

                if ("order_line_montant_reduction" in retval.keys()):
                    user['order_line_montant_reduction'] = retval['order_line_montant_reduction']
                else:
                    user['order_line_montant_reduction'] = "0"

                if ("order_line_montant_toutes_taxes" in retval.keys()):
                    user['order_line_montant_toutes_taxes'] = retval['order_line_montant_toutes_taxes']
                else:
                    user['order_line_montant_toutes_taxes'] = "0"

                if ("order_line_tax" in retval.keys()):
                    user['order_line_tax'] = retval['order_line_tax']
                else:
                    user['order_line_tax'] = ""

                if ("order_line_tax_amount" in retval.keys()):
                    user['order_line_tax_amount'] = retval['order_line_tax_amount']
                else:
                    user['order_line_tax_amount'] = "0"

                if ("order_line_type_reduction" in retval.keys()):
                    user['order_line_type_reduction'] = retval['order_line_type_reduction']
                else:
                    user['order_line_montant_reduction'] = ""

                if ("order_line_type_valeur" in retval.keys()):
                    user['order_line_type_valeur'] = retval['order_line_type_valeur']
                else:
                    user['order_line_type_valeur'] = "0"

                if ("order_line_montant_hors_taxes" in retval.keys()):
                    user['order_line_montant_hors_taxes'] = retval['order_line_montant_hors_taxes']
                else:
                    user['order_line_montant_hors_taxes'] = "0"

                user['date_update'] = retval['date_update']
                user['update_by'] = str(my_partner['_id'])
                user['partner_owner_recid'] = retval['partner_owner_recid']

                if ("order_line_comment" in retval.keys()):
                    user['order_line_comment'] = retval['order_line_comment']
                else:
                    user['order_line_comment'] = ""

                user['title'] = retval['myclass_collection'][0]['title']
                user['order_line_formation_external_code'] = retval['myclass_collection'][0]['external_code']

                if ("domaine" in retval['myclass_collection'][0].keys()):
                    user['domaine'] = retval['myclass_collection'][0]['domaine']
                else:
                    user['domaine'] = ""

                user['duration'] = retval['myclass_collection'][0]['duration']
                user['duration_unit'] = retval['myclass_collection'][0]['duration_unit']

                if (str(retval['myclass_collection'][0]['duration_unit']) == "heure"):
                    user['duration_concat'] = str(retval['myclass_collection'][0]['duration']) + " h"

                elif (str(retval['myclass_collection'][0]['duration_unit']) == "jour"):
                    user['duration_concat'] = str(retval['myclass_collection'][0]['duration']) + " j"

                elif (str(retval['myclass_collection'][0]['duration_unit']) == "semaine"):
                    user['duration_concat'] = str(retval['myclass_collection'][0]['duration']) + " s"

                elif (str(retval['myclass_collection'][0]['duration_unit']) == "mois"):
                    user['duration_concat'] = str(retval['myclass_collection'][0]['duration']) + " m"

                elif (str(retval['myclass_collection'][0]['duration_unit']) == "annee"):
                    user['duration_concat'] = str(retval['myclass_collection'][0]['duration']) + " a"

                elif (str(retval['myclass_collection'][0]['duration_unit']) == "user_rythme"):
                    user['duration_concat'] = str(retval['myclass_collection'][0]['duration']) + " u"

                else:
                    user['duration_concat'] = str(retval['myclass_collection'][0]['duration']) + " ?"

                Order_header_lines_data.append(user)

        if (len(Order_header_lines_data) <= 0):
            mycommon.myprint(str(inspect.stack()[0][
                                     3]) + " - Aucune ligne de détail pour cette facture ")
            return False, " Aucune ligne de détail pour cette facture "

        # print(" ### Order_header_lines_data =  ", Order_header_lines_data)

        contenu_doc_Template = jinja2.Template(str(partner_document_PART_INVOICE_data['contenu_doc']))

        # sourceHtml = contenu_doc_Template.render(params=Order_header_data)

        # print(" ### Order_header_data =  ", Order_header_data)
        # print(" ### Order_header_lines_data =  ", Order_header_lines_data)

        """
        Recuperation du dictionnaire des info 
        """
        tab_client = []
        tab_client.append(ObjectId(str(Order_header_client_data['_id'])))

        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = []
        new_diction['list_session_id'] = []
        new_diction['list_class_id'] = []
        new_diction['list_client_id'] = tab_client
        new_diction['list_apprenant_id'] = []

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)

        if (local_status is False):
            return local_status, local_retval

        convention_dictionnary_data = local_retval
        company_data = {
            "params": convention_dictionnary_data,
        }

        convention_dictionnary_data['order_header'] = Order_header_data
        convention_dictionnary_data['order_lines'] = Order_header_lines_data

        # sourceHtml = contenu_doc_Template.render(params=Order_header_data, param_order_lines=Order_header_lines_data, company_data=company_data)

        # sourceHtml = contenu_doc_Template.render(params_order_header=Order_header_data, params_order_lines=Order_header_lines_data, params=company_data['params'])

        sourceHtml = contenu_doc_Template.render(params=company_data['params'])

        orig_file_name = "Partner_Invoice_" + str(Order_header_data['invoice_header_ref_interne']) + ".pdf"
        outputFilename = str(MYSY_GV.TEMPORARY_DIRECTORY) + "/" + str(orig_file_name)

        # open output file for writing (truncated binary)
        resultFile = open(outputFilename, "w+b")

        # convert HTML to PDF
        pisaStatus = pisa.CreatePDF(
            src=sourceHtml,  # the HTML to convert
            dest=resultFile)  # file handle to receive result

        # close output file
        resultFile.close()



        new_model_courrier_with_code_tag = "	 <div style='width: 100%'> <div style = 'width: 100%; text-align: center;' >" \
                                           " <img style = 'height:60px; width:60px;'   src = '{{ params.mysy_qrcode_securite }}' > <br/>" \
                                           "  <nav  style = 'font-size: 10px; font-style: italic;' > Sécurisé  par MySy Training  Technology </nav>" \
                                           "  <br/> </div> </div>" + \
                                           str(sourceHtml)


        new_e_document_diction = {}
        new_e_document_diction['token'] = diction['token']
        new_e_document_diction['file_name'] = outputFilename
        toaddrs = "contact@mysy-training.com"
        new_e_document_diction['email_destinataire'] = str(toaddrs)
        new_e_document_diction['source_document'] = new_model_courrier_with_code_tag

        new_e_document_diction['type'] = "invoice"
        new_e_document_diction['related_collection'] = "partner_invoice_header"
        new_e_document_diction['related_collection_id'] = str(Order_header_data['_id'])

        if ("order_header_ref_interne" in Order_header_data.keys()):
            new_e_document_diction['file_cononical_name'] = str(Order_header_data['invoice_header_ref_interne'])
        else:
            new_e_document_diction['file_cononical_name'] = ""

        local_status_e_doc, local_retval_e_doc = E_Sign_Document.Create_E_Invoice(new_e_document_diction)

        if (local_status_e_doc is False):
            return local_status_e_doc, local_retval_e_doc

        e_Invoice_id = str(local_retval_e_doc)

        """
        Mettre à jour la facture avec une clé de signature interne car on va pas envoyer
        une demande pour créer la signature vu qu'on sur un processus interne
        """
        local_signature_key = mycommon.create_user_recid()
        retval = MYSY_GV.dbname['e_document_signe'].update_one({'_id':ObjectId(str(e_Invoice_id)),
                                                                'valide':'1',
                                                                'locked':'0',
                                                                'partner_owner_recid':str(my_partner['recid']),
                                                                'related_collection':'partner_invoice_header',
                                                                'related_collection_id':str(Order_header_data['_id'])},
                                                               {'$set':{'statut':'1',
                                                                        'secret_key_signature':str(local_signature_key)}})

        """
        On va auto signer la E-Facture qui a été créée
        """
        new_e_document_diction2 = {}
        new_e_document_diction2['token'] = str(diction['token'])
        new_e_document_diction2['e_doc_id'] = str(e_Invoice_id)
        new_e_document_diction2['secret_key_signature'] = str(local_signature_key)
        new_e_document_diction2['email_destinataire'] = "contact@mysy-training.com"
        new_e_document_diction2['user_ip'] = "127.0.0.1"

        print('laaa new_e_document_diction =  ', new_e_document_diction2)

        local_status_sign_e_doc, local_retval_sign_e_doc = E_Sign_Document.Create_E_Signature_For_E_Invoice(None, None, new_e_document_diction2)
        if( local_status_sign_e_doc is False ):
            return local_status_sign_e_doc, local_retval_sign_e_doc


        """
        Mettre à jour la facture pour indiquer 'e_document_signe_id
        """

        qry = {'_id':ObjectId(str(diction['invoice_id'])),
               'valide':'1',
               'locked':'0',
               'partner_owner_recid':str(my_partner['recid'])}

        update_data = {}
        update_data['e_document_signe_id'] = str(e_Invoice_id)

        ret_val2 = MYSY_GV.dbname['partner_invoice_header'].find_one_and_update(qry,
                                                                          {"$set": update_data},
                                                                                upsert=False,
                                                                          return_document=ReturnDocument.AFTER
                                                                          )


        # zzzzz
        return True, str(local_retval_e_doc)


    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de créer la E-Facture "



"""
Audite action session : 
Cette fonction permet de lister les inscrits pour qui
les actions ci-dessous n'ont pas été faite : 
- convocation
- convention
- etc
"""
def Audit_Session_Action_Inscrit(diction):
    try:

        field_list = ['token',  'session_id', ]
        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list  and val.startswith('my_') is False:
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " -  Le champ  '" + val + "' n'est pas autorisé, Creation partenaire annulée")
                return False, "Toutes les informations fournies ne sont pas valables"

        """
        Verification de la liste des champs obligatoires
        """
        field_list_obligatoire = ['token',  'session_id', ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, "Toutes les informations necessaires n'ont pas été fournies"

        # Recuperation du recid du partner
        mydata = {}
        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data({'token':str(diction['token'])})
        if (local_status is not True):
            return local_status, my_partner


        # Verifier la validité de la session
        is_valide_session_count = MYSY_GV.dbname['session_formation'].count_documents({'_id':ObjectId(str(diction['session_id'])),
                                                                                       'valide':'1',
                                                                                       'partner_owner_recid':str(my_partner['recid'])})

        if( is_valide_session_count != 1 ):
            mycommon.myprint(
                str(inspect.stack()[0][3]) + " L'identifiant de la session est invalide  ")
            return False, " L'identifiant de la session est invalide "

        session_data = MYSY_GV.dbname['session_formation'].find_one(
            {'_id': ObjectId(str(diction['session_id'])),
             'valide': '1',
             'partner_owner_recid': str(my_partner['recid'])})

        """
        Recuperer la listes des inscrit validé à cette session 
        """
        tab_email_inscrti_valide = []
        tab_inscrit = MYSY_GV.dbname['inscription'].find({'session_id':str(diction['session_id']),
                                                          'status':'1',
                                                          'partner_owner_recid':str(my_partner['recid'])})

        for email in tab_inscrit:
            if( "email" in email):
                tab_email_inscrti_valide.append(email['email'])

        #print(" Liste des email inscrit en tout = ", tab_email_inscrti_valide)

        """
        Recuperer la liste des documents tracké pour cette session 
        """
        diction_list_tracked_doc = {}
        diction_list_tracked_doc['token'] = str(diction['token'])
        diction_list_tracked_doc['related_collection'] = "session_formation"
        diction_list_tracked_doc['related_collection_recid'] = str(diction['session_id'])
        diction_list_tracked_doc['session_id'] = str(diction['session_id'])

        local_status, local_retval = module_editique.Get_Editable_Document_By_Partner_By_Collection(diction_list_tracked_doc)
        if( local_status is False ):
            return local_status, local_retval


        tab_statut_action = []

        local_id = 0
        for tmp_val in local_retval :

            document_type = ast.literal_eval(tmp_val)

            tab_inscrit_ok = []
            node = {}
            node['id'] = local_id
            node['courrier_template_nom'] = document_type['courrier_template_nom']
            node['courrier_template_ref_interne'] = document_type['courrier_template_nom']
            node['tab_user_statut'] = []

            local_id = local_id + 1

            for local_email in tab_email_inscrti_valide:
                email_exist = 0
                sub_node = {}


                for val in document_type['list_document_history_event'] :
                    if( val['local_target_collection_name'] == local_email ):

                        email_exist = 1
                        sub_node['_id'] = val['_id']
                        sub_node['email_inscrit'] = val['local_target_collection_name']
                        sub_node['date_update'] = val['date_update']
                        sub_node['local_update_by_email'] = val['local_update_by_email']
                        sub_node['statut'] = "1"

                if( email_exist == 0 ):

                    sub_node['email_inscrit'] = local_email
                    sub_node['date_update'] = ""
                    sub_node['local_update_by_email'] = ""
                    sub_node['statut'] = "0"

                node['tab_user_statut'].append(sub_node)


            tab_statut_action.append(node)

        RetObject = []
        RetObject.append(mycommon.JSONEncoder().encode(tab_statut_action))

        return True, RetObject
    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, "Impossible de réaliser l'audit de la session"


"""
Cette fonction permet d'envoyer une demande a un client
pour mettre à jour la liste de ses apprenant qui sont inscrit à une session.

Cette fonction s'applique uniquement au preinscrit (status = 0)
use case : 
lorqu'un client valide un devis, les places sont preinscrit sur la session avec des nom par defaut
(devis_nom1, devis_nom2, etc).
Le gestionnaire de formation, peut ensuire utiliser cette fonction pour demander au client de saisir ou valider
les noms et emails definitifs.


/!\ : On traite toute la liste d'un client, pas de traitement partiel

"""
def Prepare_request_presinscription_data_validation(diction):
    try:
        """
       Verification de la liste des champs obligatoires
       """
        field_list_obligatoire = ['token', 'list_inscription_id', 'session_id']
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " Les informations fournies ne sont pas correctes"

        mytoken = ""
        if ("token" in diction.keys()):
            if diction['token']:
                mytoken = diction['token']

        local_status, my_partner = mycommon.Check_Connexion_And_Return_Partner_Data(diction)
        if (local_status is not True):
            return local_status, my_partner

        partner_recid = my_partner['recid']

        list_client_id = []

        list_inscription_id = []
        if ("list_inscription_id" in diction.keys()):
            if diction['list_inscription_id']:
                list_inscription_id = str(diction['list_inscription_id']).replace(",", ";").split(";")

        for inscription_id in list_inscription_id:
            """
            # Verification que l'inscription existe et qu'elle est valide et qu'elle est au statut 0 (preinscrit) 

            """
            ret_val2_count = MYSY_GV.dbname['inscription'].count_documents(
                {'_id': ObjectId(str(inscription_id)), 'partner_owner_recid': str(my_partner['recid']),
                'session_id':diction['session_id'] },
            )

            if (ret_val2_count != 1):
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + "L'identifiant de l'inscription " + str(
                    inscription_id) + " n'est pas valide ")
                return False, "  L'identifiant de l'inscription " + str(inscription_id) + " n'est pas valide "

            inscription_id_data = MYSY_GV.dbname['inscription'].find_one(
                {'_id': ObjectId(str(inscription_id)), 'partner_owner_recid': str(my_partner['recid']), 'session_id':diction['session_id']
                 },
            )

            if ("status" not in inscription_id_data.keys() or inscription_id_data['status'] != "0"):
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " Toutes les inscriptions doivent être au statut : 'preinscription' ")
                return False, " Toutes les inscriptions doivent être au statut : preinscription "



            # On enregistre le client dans la table "list_client_id"
            if ("client_rattachement_id" in inscription_id_data.keys() and inscription_id_data[
                'client_rattachement_id'] and inscription_id_data['client_rattachement_id'] not in list_client_id):
                list_client_id.append(str(inscription_id_data['client_rattachement_id']))

            # Verifier que la session de formation concernée est valide
            is_valide_session = MYSY_GV.dbname['session_formation'].count_documents(
                {'_id': ObjectId(str(inscription_id_data['session_id'])),
                 'partner_owner_recid': str(my_partner['recid']),
                 'valide': '1'})

            if (is_valide_session != 1):
                mycommon.myprint(str(inspect.stack()[0][
                                         3]) + " L'identifiant de la session de formation  " + str(
                    inscription_id_data['session_id']) + " n'est pas valide ")
                return False, "  L'identifiant de la session de formation  " + str(
                    inscription_id_data['session_id']) + " n'est pas valide  "

        mycommon.myprint_debug(" list_client_id = " + str(list_client_id))



        # A present les controles sont ok sur la liste on peut valide la liste des inscription
        warning_msg = ""
        is_warning = ""

        for client_id in list_client_id:
            mycommon.myprint_debug(" Traitement du client client_id = " + str(client_id))

            new_local_diction = {}
            new_local_diction['client_id'] = str(client_id)
            new_local_diction['session_id'] = str(diction['session_id'] )
            new_local_diction['partner_recid'] = str(my_partner['recid'])
            new_local_diction['token'] = str(diction['token'])

            local_send_status, local_retval = Send_Email_request_presinscription_data_validation(new_local_diction)
            if (local_send_status is False):
                is_warning = "1"
                warning_msg = warning_msg + "\n" + str(local_retval)


        if (is_warning == "1"):
            return True, str(warning_msg)

        return True, "Les demandes  de mise à jour ont été envoyées"

    except  Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        print(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer les demandes de mise à jour"


"""
Cette fonction envoi l'email de demande de validation/maj 
de la liste des preinscrits à une session de formation
"""
def Send_Email_request_presinscription_data_validation(diction):
    try:
        """
       Verification de la liste des champs obligatoires
       """
        field_list_obligatoire = ['client_id', 'session_id', 'partner_recid', 'token' ]
        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " -  : La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " Les informations fournies ne sont pas correctes"



        # A present les controles sont ok sur la liste on peut valide la liste des inscription
        warning_msg = ""
        is_warning = ""



        # Recuperation des données des stagiaires
        tab_stagiaire = []
        for inscription_id_data in MYSY_GV.dbname['inscription'].find({'session_id': str(diction['session_id']), 'partner_owner_recid': str(diction['partner_recid']), 'status': '1', 'client_rattachement_id': str(diction['client_id'])}):

            if (inscription_id_data is None):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  L'identifiant du stagiaire est invalide  ")
                return False, "   L'identifiant du stagiaire est invalide   "
            tab_stagiaire.append(inscription_id_data['_id'])


        # Recuperation des données du client
        tab_client = []
        for client_id_data in MYSY_GV.dbname['partner_client'].find(
                {'_id': ObjectId(str(diction['client_id'])), 'partner_recid': str(diction['partner_recid']),
                 'valide': '1', 'locked':'0'}):

            if (client_id_data is None):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  L'identifiant du client est invalide  ")
                return False, "   L'identifiant du client est invalide   "
            tab_client.append(client_id_data['_id'])

        # Recuperation des contacts de communucation du client
        local_diction = {}
        local_diction['token'] = diction['token']
        local_diction['_id'] = str(diction['client_id'])

        local_status, partner_client_contact_communication = partner_client.Get_Partner_Client_Communication_Contact(local_diction)
        if( local_status is False ):
            return local_status, partner_client_contact_communication


        tab_email_contact_client_destinataire = []
        for tmp_val in partner_client_contact_communication:
            tmp_val_JSON = ast.literal_eval(tmp_val)
            if( "email" in tmp_val_JSON.keys() and tmp_val_JSON['email']):
                tab_email_contact_client_destinataire.append(str(tmp_val_JSON['email']))

        # Recuperation des données de la session
        tab_session = []
        class_internal_url = ""
        for session_id_data in MYSY_GV.dbname['session_formation'].find(
                {'_id': ObjectId(str(diction['session_id'])), 'partner_owner_recid': str(diction['partner_recid']),
                 'valide': '1', }):

            if (session_id_data is None):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  L'identifiant de la session est invalide  ")
                return False, "   L'identifiant de la session est invalide   "
            tab_session.append(session_id_data['_id'])
            class_internal_url = session_id_data['class_internal_url']

        # Recuperation des données de la formation
        tab_class = []
        for class_id_data in MYSY_GV.dbname['myclass'].find(
                {'internal_url': str(class_internal_url), 'partner_owner_recid': str(diction['partner_recid']),
                 'valide': '1', }):

            if (class_id_data is None):
                mycommon.myprint(
                    str(inspect.stack()[0][
                            3]) + "  L'identifiant de la formation est invalide  ")
                return False, "   L'identifiant de la formation est invalide   "
            tab_class.append(class_id_data['_id'])


        new_diction = {}
        new_diction['token'] = diction['token']
        new_diction['list_stagiaire_id'] = tab_stagiaire
        new_diction['list_session_id'] = tab_session
        new_diction['list_class_id'] = tab_class
        new_diction['list_client_id'] = tab_client
        new_diction['list_apprenant_id'] = []
        new_diction['list_sequence_session_id'] = []

        local_status, local_retval = mycommon.Get_Dictionnary_data_For_Template(new_diction)
        convention_dictionnary_data = local_retval

        convention_dictionnary_data['attendeelist_url'] = MYSY_GV.CLIENT_URL_BASE+"UpadateAttendeeList/"+str(diction['client_id'])+"/"+str(diction['session_id'])+"/"+str(diction['partner_recid'])+"/"

        body = {
            "params": convention_dictionnary_data,
        }
        """
        Recuperation du modele de courrier
        """
        # field_list = ['token', "courrier_template_id", "courrier_template_ref_interne", "partner_recid"]
        new_local_diction = {}
        new_local_diction['courrier_template_id'] = ""
        new_local_diction['courrier_template_ref_interne'] = "PARTICIPANT_LIST_UPDATE"
        new_local_diction['partner_recid'] = str(diction['partner_recid'])


        status_courrier_template_data , retval_courrier_template_data = mycommon.Get_Partner_Courrier_Model(new_local_diction)
        if( status_courrier_template_data is False):
            return status_courrier_template_data , retval_courrier_template_data

        courrier_template_data = retval_courrier_template_data



        ## Creation du  mail au format email
        corps_mail_Template = jinja2.Template(str(courrier_template_data['contenu_doc']))
        sourceHtml = corps_mail_Template.render(params=body["params"])
        html_mime = MIMEText(sourceHtml, 'html')

        # Traitement du sujet du mail
        sujet_mail_Template = jinja2.Template(str(courrier_template_data['sujet']))
        sujetHtml = sujet_mail_Template.render(params=body["params"])

        # Creation de l'email à enoyer
        msg = MIMEMultipart("alternative")

        # Recuperation des donnes smpt
        local_stpm_status, partner_SMTP_COUNT_smtpsrv, partner_own_smtp_value, partner_SMTP_COUNT_password, partner_SMTP_COUNT_user, partner_SMTP_COUNT_From_User, partner_SMTP_COUNT_port = mycommon.Get_Partner_SMTP_Param(
            diction['partner_recid'])

        if (local_stpm_status is False):
            return local_stpm_status, partner_own_smtp_value

        msg.attach(html_mime)
        msg['From'] = partner_SMTP_COUNT_From_User
        msg['Bcc'] = 'contact@mysy-training.com'
        msg['Subject'] = sujetHtml
        # msg['to'] = "billardman01@hotmail.com"
        toaddrs = ", ".join(tab_email_contact_client_destinataire)
        msg['to'] = str(toaddrs)

        smtpserver = smtplib.SMTP(partner_SMTP_COUNT_smtpsrv, partner_SMTP_COUNT_port)

        smtpserver.ehlo()
        smtpserver.starttls()
        smtpserver.login(partner_SMTP_COUNT_user, partner_SMTP_COUNT_password)

        val = smtpserver.send_message(msg)
        smtpserver.close()
        print(" Email envoyé " + str(val))

        return True, "Les demandes  de mise à jour ont été envoyées"

    except  Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        print(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible d'envoyer les demandes de mise à jour"



"""
Cette fonction permet à un client de mettre à jour 
la liste de ses partipants à une session de formation.

"""
def Client_Update_Liste_Attendee_No_Token(diction):
    try:
        diction = mycommon.strip_dictionary(diction)

        """
        Verification des input acceptés
        """
        field_list = ['client_id', 'session_id', 'list_attendee_data', 'partner_recid']

        incom_keys = diction.keys()
        for val in incom_keys:
            if val not in field_list and val.startswith('my_') is False:
                mycommon.myprint(str(
                    inspect.stack()[0][3]) + "  Le champ  '" + val + "' n'est pas autorisé")
                return False, " Les informations fournies sont incorrectes"

        """
        Verification des champs obligatoires
        """
        field_list_obligatoire = ['client_id', 'session_id', 'list_attendee_data', 'partner_recid']

        for val in field_list_obligatoire:
            if val not in diction:
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ")
                return False, " Les informations fournies sont incorrectes"


        """
        Verifier que le client est valide 
        """
        is_client_valide_data = MYSY_GV.dbname['partner_client'].find_one({'_id':ObjectId(str(diction['client_id'])),
                                                                           'valide':'1',
                                                                           'locked':'0',
                                                                           'partner_recid':str(diction['partner_recid'])})
        if(is_client_valide_data is None ):
            mycommon.myprint(str(
                inspect.stack()[0][3]) + " L'identifiant du client n'est pas valide")
            return False, "  L'identifiant du client n'est pas valide "

        """
               Verifier que la session est valide 
               """
        is_session_valide_data = MYSY_GV.dbname['session_formation'].find_one({'_id': ObjectId(str(diction['session_id'])),
                                                                           'valide': '1',
                                                                               'partner_owner_recid': str(
                                                                               diction['partner_recid'])})
        if (is_session_valide_data is None):
            mycommon.myprint(str(
                inspect.stack()[0][3]) + " L'identifiant de la session  n'est pas valide")
            return False, "  L'identifiant  de la session n'est pas valide "


        JSON_attendee_response = ast.literal_eval(diction['list_attendee_data'])


        for val in JSON_attendee_response:
            # Verifier que les données envoyées sont valide avant de mettre à jour tous les participants
            is_valide_attendee = MYSY_GV.dbname['inscription'].count_documents({'_id':ObjectId(str(val['_id'])),
                                                                                'status':'0',
                                                                                'session_id':str(diction['session_id']),
                                                                                'client_rattachement_id':str(diction['client_id']),
                                                                                'partner_owner_recid':str(diction['partner_recid'])})

            if(is_valide_attendee <= 0 ):
                mycommon.myprint(str(
                    inspect.stack()[0][3]) + " Le participant "+str(val['_id'])+" n'est pas valide ou n'est pas au statut PREINSCRIT")
                return False, "  Le participant "+str(val['_id'])+" n'est pas valide ou n'est pas au statut PREINSCRIT"

            if ("civilite" in val.keys() and str(val['civilite']).lower().strip() not in MYSY_GV.CIVILITE):
                mycommon.myprint(
                    str(inspect.stack()[0][3]) + "La civlité " +str(val['civilite']).lower().strip() + " n'est pas valide. Les valeurs autorisées sont "+str(MYSY_GV.CIVILITE))
                return False, " La civlité " +str(val['civilite']).lower().strip() + " n'est pas valide. Les valeurs autorisées sont "+str(MYSY_GV.CIVILITE)

            # Verifier la validité du mail
            if ("email" in val.keys()):
                local_email = str(val['email']).strip()
                if (mycommon.isEmailValide(local_email) is False):
                    mycommon.myprint(
                        str(inspect.stack()[0][3]) + " - l'adresse email " + str(local_email) + " n'est pas valide")
                    return False, "- l'adresse email " + str(local_email) + " n'est pas valide "

        """
        A présent toutes les données sont valide, on fait la mise à jour
        """
        for val in JSON_attendee_response:
            # Verifier qu'on mettre à jour tous les participants
            update_data = {}
            update_data['date_update'] = str(datetime.now())
            update_data['update_by'] = "Client"

            update_data['nom'] = str(val['nom'])
            update_data['prenom'] = str(val['prenom'])

            if( "civilite" in val.keys() and str(val['civilite']).lower().strip() in MYSY_GV.CIVILITE):
                update_data['civilite'] = str(val['civilite']).lower().strip()

            # Verifier la validité du mail
            if ("email" in val.keys() ):
                local_email = str(val['email']).strip()
                if (mycommon.isEmailValide(local_email)):
                    update_data['email'] = local_email
                else:
                    mycommon.myprint(
                        str(inspect.stack()[0][3]) + " - l'adresse email " + str(local_email) + " n'est pas valide")
                    return False, "- l'adresse email " + str(local_email) + " n'est pas valide "


            update_attendee_data = MYSY_GV.dbname['inscription'].find_one_and_update({'_id':ObjectId(str(val['_id'])),
                                                                                'status':'0',
                                                                                'session_id':str(diction['session_id']),
                                                                                'client_rattachement_id':str(diction['client_id']),
                                                                                'partner_owner_recid':str(diction['partner_recid'])},
                                                                                   {"$set": update_data},
                                                                                   upsert=False,
                                                                                   return_document=ReturnDocument.AFTER
                                                                                   )

        return True, "Les mises à jour ont été correctement faites"

    except  Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        print(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno))
        return False, " Impossible de mettre à jour la liste des participants"



