""" Ce fichier permet de gerer les factures client et les methodes de payement """ import pymongo from pymongo import MongoClient import json from bson import ObjectId import re from datetime import datetime import prj_common as mycommon import secrets import inspect import sys, os import shutil import csv import pandas as pd from pymongo import ReturnDocument from math import isnan import GlobalVariable as MYSY_GV import email_mgt as email from dateutil import tz import pytz from xhtml2pdf import pisa import jinja2 import ftplib import pysftp from flask import send_file from dateutil.relativedelta import relativedelta import class_mgt as class_mgt import strype_payement as Stripe import code_promo_mgt as code_promo_mgt import time class JSONEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, ObjectId): return str(o) return json.JSONEncoder.default(self, o) def get_invoice_by_customer(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'existe pas, Creation formation annulée") return False, " Impossible de récupérer les factures" ''' 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 la liste des arguments ") return False, " Impossible de récupérer les factures" # recuperation des paramettre my_token = "" user_recid = "" if ("token" in diction.keys()): if diction['token']: my_token = diction['token'] user_recid = "None" # Verification de la validité du token/mail dans le cas des user en mode connecté if (len(str(my_token)) > 0): retval = mycommon.check_partner_token_validity("", my_token) if retval is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - La session de connexion n'est pas valide") return False, " Impossible de récupérer les factures" # Recuperation du recid de l'utilisateur user_recid = mycommon.get_parnter_recid_from_token(my_token) if user_recid is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de récupérer le token de l'utilisateur") return False, " Impossible de récupérer les factures" if (len(str(my_token)) <= 0): mycommon.myprint(str(inspect.stack()[0][3]) + " - Le token est vide") return False, " Impossible de récupérer les factures" RetObject = [] coll_facture = MYSY_GV.dbname['factures'] for retVal in coll_facture.find({'client_recid':user_recid, 'valide': '1'}, {'invoice_nom':1, 'total_ht':1, 'total_tva':1, 'total_ttc':1, 'order_id':1, 'invoice_id':1, 'due_date':1, 'invoice_date':1, } )\ .sort([("invoice_date", pymongo.DESCENDING), ("invoice_id", pymongo.DESCENDING), ]): user = retVal if ("_id" in user.keys()): user['class_id'] = user.pop('_id') user['lien_pdf'] = str(MYSY_GV.INVOICE_FTP_DIRECTORY)+str("invoice_")+str(user['invoice_id'])+str(".pdf") RetObject.append(JSONEncoder().encode(user)) #print(" les facture du client = "+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 les factures" ''' Cette API enregistre la methode de payement. /!\ un client a une seule mode de payement ceci est enregistré dans la collection : payement_mode ''' def add_payement_mode(diction): try: field_list = ['token', 'pwd', 'secret', 'nom_carte', 'num_carte', 'date_exp_carte', 'cvv_carte', 'nom_compte', 'iban', 'bic','type' ] 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 formation annulée") return False, "Impossible d'ajouter le mode de payement" ''' 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', 'pwd', 'secret', 'type'] 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 la liste des arguments ") return False, "Impossible d'ajouter le mode de payement" # recuperation des paramettre my_token = "" user_recid = "" my_pwd = "" my_secret = "" if ("pwd" in diction.keys()): if diction['pwd']: my_pwd = diction['pwd'] if ("secret" in diction.keys()): if diction['secret']: my_secret = diction['secret'] if ("token" in diction.keys()): if diction['token']: my_token = diction['token'] # Verification de la validité du pwd et du secret coll_tmp = MYSY_GV.dbname['partnair_account'] tmp_count = coll_tmp.count_documents({'pwd': str(my_pwd), 'active': '1', 'secret_key':str(my_secret)}) if (tmp_count <= 0): return False, "Les identifiants sont incorrectes" tmp_account = coll_tmp.find({'pwd': str(my_pwd), 'active': '1', 'secret_key':str(my_secret)}) """ Stripe : recuperation du stripe_account_id """ partner_stripe_account_id = tmp_account[0]['stripe_account_id'] """ Recuperation du PaymentMethod s'il en a un : 'stripe_paymentmethod_id' """ stripe_paymentmethod_id = "" if ("stripe_paymentmethod_id" in tmp_account[0].keys()): if tmp_account[0]['stripe_paymentmethod_id']: stripe_paymentmethod_id = tmp_account[0]['stripe_paymentmethod_id'] user_recid = "None" # Verification de la validité du token/mail dans le cas des user en mode connecté if (len(str(my_token)) > 0): retval = mycommon.check_partner_token_validity("", my_token) if retval is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - La session de connexion n'est pas valide") return False, "Impossible d'ajouter le mode de payement" # Recuperation du recid de l'utilisateur user_recid = mycommon.get_parnter_recid_from_token(my_token) if user_recid is False: mycommon.myprint( str(inspect.stack()[0][3]) + " - Impossible de récupérer le token de l'utilisateur") return False, "Impossible d'ajouter le mode de payement" if (len(str(my_token)) <= 0): mycommon.myprint(str(inspect.stack()[0][3]) + " - Le token est vide") return False, "Impossible d'ajouter le mode de payement" coll_name = MYSY_GV.dbname['payement_mode'] new_data = {} new_data['valide'] = "1" now = datetime.now() new_data['date_update'] = str(now) # initialisation pr nettoyer l'existant (faire comme un delete) new_data['nom_carte'] = "" new_data['num_carte'] = "" new_data['date_exp_carte'] = "" new_data['cvv_carte'] = "" new_data['nom_compte'] = "" new_data['iban'] = "" new_data['bic'] = "" if ("nom_carte" in diction.keys()): if diction['nom_carte']: new_data['nom_carte'] = diction['nom_carte'] if ("num_carte" in diction.keys()): if diction['num_carte']: new_data['num_carte'] = diction['num_carte'] if ("type" in diction.keys()): if diction['type']: new_data['type'] = diction['type'] if ("date_exp_carte" in diction.keys()): if diction['date_exp_carte']: new_data['date_exp_carte'] = diction['date_exp_carte'] if ("cvv_carte" in diction.keys()): if diction['cvv_carte']: new_data['cvv_carte'] = diction['cvv_carte'] if ("nom_compte" in diction.keys()): if diction['nom_compte']: new_data['nom_compte'] = diction['nom_compte'] if ("iban" in diction.keys()): if diction['iban']: new_data['iban'] = diction['iban'] if ("nom_compte" in diction.keys()): if diction['nom_compte']: new_data['nom_compte'] = diction['nom_compte'] if ("bic" in diction.keys()): if diction['bic']: new_data['bic'] = diction['bic'] print("mode de payement data :"+str(new_data)) #print("str(new_data['num_carte']) = "+str(new_data['num_carte']) +" len = "+str(len(str(new_data['num_carte']))) ) #print("str(new_data['nom_carte']) = " + str(new_data['nom_carte']) +" len ="+str(len(str(new_data['nom_carte']))) ) if( str(new_data['type']) == "cb"): if( len(str(new_data['num_carte'])) <= 0 or len(str(new_data['nom_carte'])) <= 0 or len(str(new_data['date_exp_carte'])) <= 0 or len(str(new_data['cvv_carte'])) <= 0 ): mycommon.myprint( str(inspect.stack()[0][3]) + " : Les données de la carte sont incorrectes") return False, "Impossible d'ajouter le mode de paiement. Les données de la carte sont incorrectes" # SI stripe_paymentmethod_id est vide alors on créé la carte print(" #### Creation de la carte dans STRIPE") tmp_tab = str(new_data['date_exp_carte']).split("/") if( len(tmp_tab) != 2): mycommon.myprint( str(inspect.stack()[0][3]) + " : La date d'expiration de la carte est incorrecte") return False, "Impossible d'ajouter le mode de paiement. Les données de la carte sont incorrectes" new_stripe_card = {} new_stripe_card['customerid'] = partner_stripe_account_id new_stripe_card['number'] = new_data['num_carte'] new_stripe_card['name'] = new_data['nom_carte'] new_stripe_card['exp_month'] = str(tmp_tab[0]) new_stripe_card['exp_year'] = str(tmp_tab[1]) new_stripe_card['cvc'] = new_data['cvv_carte'] new_stripe_card['stripe_paymentmethod_id'] = stripe_paymentmethod_id print("### la carte à créer est "+str(new_stripe_card)) local_status, created_stripe_cart = Stripe.create_update_payment_card(new_stripe_card) if( local_status is False): mycommon.myprint( str(inspect.stack()[0][3]) + " : Impossible de créer la carte de payement dans Stripe") return False, "Impossible d'ajouter le mode de paiement." #print(" ### la carte est créée : "+str(created_stripe_cart)) if (str(new_data['type']) == "sepa"): if (len(str(new_data['iban'])) <= 0 or len(str(new_data['bic']) <= 0) or len(str(new_data['nom_compte'])) <= 0): mycommon.myprint( str(inspect.stack()[0][3]) + " : Les données bancaires sont incorrectes") return False, "Impossible d'ajouter le mode de paiement. Les données bancaires sont incorrectes" new_data['stripe_paymentmethod_id'] = str(created_stripe_cart.id) ret_val = coll_name.find_one_and_update( {'client_recid': user_recid, 'valide': '1'}, {"$set": new_data}, upsert=True, return_document=ReturnDocument.AFTER ) if (ret_val['_id'] is False): mycommon.myprint( str(inspect.stack()[0][3]) + " : Impossible d'ajouter le mode de payement") return False, "Impossible d'ajouter le mode de payement" ''' Une fois que le mode de payement est ajouté, alors valide le compte partenaire en mettant : ispending à 0 ''' coll_name = MYSY_GV.dbname['partnair_account'] data_update = {'date_update':str(datetime.now()), 'ispending':'0', 'stripe_paymentmethod_id':str(created_stripe_cart.id)} ret_val = coll_name.find_one_and_update( {'recid': user_recid, }, {"$set": data_update}, upsert=False, return_document=ReturnDocument.AFTER ) if (ret_val is None or ret_val['_id'] is False): mycommon.myprint( str(inspect.stack()[0][3]) + " : Impossible de mettre à jour (ispending':'1') le compte partenaire") return False, "Impossible d'ajouter le mode de payement" return True, " Le mode de payement a bien ete ajouté" 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'ajouter le mode de payement" ''' Cette fonction recupere le mode de payement par defaut d'un client ''' def get_payement_mode(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'existe pas, Creation formation annulée") return False, " Impossible de récupérer les mode de payement", "" ''' 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 la liste des arguments ") return False, " Impossible de récupérer les mode de payement", "" # recuperation des paramettre my_token = "" user_recid = "" if ("token" in diction.keys()): if diction['token']: my_token = diction['token'] user_recid = "None" # Verification de la validité du token/mail dans le cas des user en mode connecté if (len(str(my_token)) > 0): retval = mycommon.check_partner_token_validity("", my_token) if retval is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - La session de connexion n'est pas valide") return False, " Impossible de récupérer les mode de payement", "" # Recuperation du recid de l'utilisateur user_recid = mycommon.get_parnter_recid_from_token(my_token) if user_recid is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de récupérer le token de l'utilisateur") return False, " Impossible de récupérer les mode de payement", "" if (len(str(my_token)) <= 0): mycommon.myprint(str(inspect.stack()[0][3]) + " - Le token est vide") return False, " Impossible de récupérer les mode de payement", "" """ Recuperation du stripe_account_id """ stripe_account_id = mycommon.get_parnter_stripe_account_id_from_recid(user_recid) if stripe_account_id is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de récupérer le stripe_account_id de l'utilisateur") return True, " Impossible de récupérer les modes de payement", "0" """ Recuperation de clé de payement stripe : stripe_paymentmethod_id """ stripe_paymentmethod_id = mycommon.get_parnter_stripe_stripe_paymentmethod_id_from_recid(user_recid) if stripe_paymentmethod_id is False: mycommon.myprint( str(inspect.stack()[0][3]) + " - Impossible de récupérer le stripe_paymentmethod_id de l'utilisateur") return False, " Impossible de récupérer les modes de payement", "" RetObject_stripe = [] tmp_diction = {} tmp_diction['stripe_account_id'] = str(stripe_account_id) #print("### recherche mod de payement stripe_account_id = "+str(stripe_account_id) ) localStatus, payment_cards, nb_carte = Stripe.get_customer_payement_cards(tmp_diction) if( localStatus and nb_carte != "0" ): RetObject_stripe.append(payment_cards[0]) return True, RetObject_stripe, nb_carte 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 les mode de payement", "0" """ Cette fonction crée une facture dans la collecion : factures. /!\ : update du 09/07/2023 : Apres la creation de la commande, une recap de la facture à venir est envoyé, ceci à titre d'information. Le système recupere les informations depuis la plateforme strype. MAIS pour eviter des soucis de sychronisation, c'est à dire récupérer les infos, alors que strype n'a pas fini sa mise à jour, on a desynchroniser complement la finalisation de la commande et l'envoie des données de facture. Pour ce faire, un va positionner un flag sur le partenaire pour dire : "send_pre_invoice_data" à '1', "date_time_pre_invoice_created" à date_time_now. Ensuite un job va tourner (frequence à definir) pour traiter tous les client dont : "send_pre_invoice_data" = 1 et "date_time_pre_invoice_created" <= now() - 5 min. Ainsi on laisse 5 minutes à strype pour bien se mettre à jour """ """ 14/04/2025 : desormais les pack ne sont pas liés au nombre de formation Donc pour eviter des blocages coté LMS sur le nombre de formation, on va TOUJOURS forcer le nombre de formation à 999 """ def createOrder(diction): try: order_id = "" nb_line = 0 num_facture = "" ''' field_list = ['token', 'nb_product', 'periodicite', 'discount_code', 'discount_type', 'discount_valeur', 'discount_qty'] 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", False ''' ''' 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', 'nb_product', 'periodicite'] 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 la liste des arguments ") return False, " Impossible de créer la facture", False # recuperation des paramettre my_token = "" user_recid = "" if ("token" in diction.keys()): if diction['token']: my_token = diction['token'] discount_code = "" if ("discount_code" in diction.keys()): if diction['discount_code']: discount_code = diction['discount_code'] discount_type = "" if ("discount_type" in diction.keys()): if diction['discount_type']: discount_type = diction['discount_type'] discount_valeur = "" if ("discount_valeur" in diction.keys()): if diction['discount_valeur']: discount_valeur = diction['discount_valeur'] discount_qty = "" if ("discount_qty" in diction.keys()): if diction['discount_qty']: discount_qty = diction['discount_qty'] user_recid = "None" # Verification de la validité du token/mail dans le cas des user en mode connecté if (len(str(my_token)) > 0): retval = mycommon.check_partner_token_validity("", my_token) if retval is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - La session de connexion n'est pas valide") return False, " Impossible de créer la facture", False # Recuperation du recid de l'utilisateur user_recid = mycommon.get_parnter_recid_from_token(my_token) if user_recid is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de récupérer le token de l'utilisateur") return False," Impossible de créer la facture", False if (len(str(my_token)) <= 0): mycommon.myprint(str(inspect.stack()[0][3]) + " - Le token est vide") return False, " Impossible de créer la facture", False new_data = {} if ("nb_product" in diction.keys()): if diction['nb_product']: nb_line = mycommon.tryInt(diction['nb_product']) periodicite = "mois" if ("periodicite" in diction.keys()): if diction['periodicite']: new_data['periodicite'] = diction['periodicite'] periodicite = diction['periodicite'] if ("end_date" in diction.keys()): if diction['end_date']: new_data['end_date'] = diction['end_date'] # Recuperation des données client new_data['client_recid'] = user_recid coll_part_account = MYSY_GV.dbname['partnair_account'] # print(" myquery pr demo_account = " + str(myquery)) tmp_count = coll_part_account.count_documents({'recid':user_recid, 'active':'1'}) if (tmp_count <= 0): mycommon.myprint( str(inspect.stack()[0][3]) + "Aucune donnée client, Impossible de créer la commande ") return False, "Aucune donnée client, Impossible de créer la commande ", None new_data['nb_product'] = str(nb_line) i = 0 total_ht = 0 nb_formation = "" while (i < nb_line): #print("PRODUIT N° " + str((i + 1)) + " : " ) line_dict = json.loads(diction[str(i)]) #print(" line = "+str(line_dict)+" -- le type est : "+str(type(line_dict))) #print(" code = "+str( line_dict['code'] )) #print(" prix = " + str(line_dict['prix'])) #print(" qty = " + str(line_dict['qty'])) #print(" les produits du pack = " + str(line_dict['pack_products'])) montant_line = mycommon.tryFloat(str(line_dict['prix'])) * mycommon.tryInt(str(line_dict['qty'])) #print(" montant_line = " + str(montant_line)) total_ht = total_ht + montant_line new_data_item = {} new_data_item['code'] = line_dict['code'] new_data_item['prix'] = line_dict['prix'] new_data_item['qty'] = line_dict['qty'] """ 14/04/2025 : desormais les pack ne sont pas liés au nombre de formation Donc pour eviter des blocages coté LMS sur le nombre de formation, on va TJRS FORCER la valeur de nb_formation à 999 """ nb_formation = "999" new_data_item['pack_products'] = line_dict['pack_products'] new_data_item['amount'] = str(montant_line) row = "item_"+str(i) new_data[str(row)] = new_data_item i = i + 1 """ Recalcul du HT en incluant les remise, si remise """ montant_remise = 0 if( discount_code ) : local_status, local_discount_valeur = mycommon.IsFloat(discount_valeur) if( local_status is False): mycommon.myprint( str(inspect.stack()[0][ 3]) + " La valeur de la remise n'est pas correcte ") return False, "La valeur de la remise n'est pas correcte ", None local_status, local_discount_qty = mycommon.IsInt(discount_qty) if (local_status is False): mycommon.myprint( str(inspect.stack()[0][ 3]) + " la valeur de 'discount_qty' n'est pas correcte ") return False, "la valeur de 'discount_qty' n'est pas correcte ", None local_my_data = {} local_my_data['token'] = my_token local_my_data['code_promo'] = discount_code local_status, isValide_code_promo = code_promo_mgt.Is_Valide_Code_Promo(local_my_data) if (local_status is False): mycommon.myprint( str(inspect.stack()[0][ 3]) + " Le code remise est invalide ") return False, " Le code remise est invalide ", None # Si le type de promo est 'percent' if( discount_type == 'percent'): tmp_val1 = mycommon.tryFloat(str(discount_valeur))/100 montant_remise = mycommon.tryFloat(str(total_ht)) * mycommon.tryFloat(tmp_val1) total_ht = str(mycommon.tryFloat(str(total_ht)) - mycommon.tryFloat(str(montant_remise))) #print(" #### percent : montant_remise = ", montant_remise) #print(" #### percent : total_ht = ", total_ht) elif( discount_type == "fix"): montant_remise = mycommon.tryFloat(str(discount_valeur)) total_ht = str(mycommon.tryFloat(str(total_ht)) - mycommon.tryFloat(str(montant_remise))) #print(" #### fix : montant_remise = ", montant_remise) #print(" #### fix : total_ht = ", total_ht) if(mycommon.tryFloat(str(total_ht)) < 0 ): mycommon.myprint( str(inspect.stack()[0][ 3]) + " La remise est supérieure au montant HT. ") return False, " La remise est supérieure au montant HT ", None new_data['discount_code'] = str(discount_code) new_data['discount_type'] = str(discount_type) new_data['discount_valeur'] = str(discount_valeur) new_data['discount_montant_remise'] = str(montant_remise) new_data['total_ht'] = total_ht new_data['total_tva'] = mycommon.tryFloat(str(total_ht)) * MYSY_GV.TVA_TAUX new_data['total_ttc'] = mycommon.tryFloat(str(total_ht)) + mycommon.tryFloat(str(new_data['total_tva'])) total_ttc_float = mycommon.tryFloat(str(new_data['total_ttc'])) #print("str(new_data['total_ttc']) === " + str(total_ttc_float)) if (tmp_count <= 0 and total_ttc_float > 0): mycommon.myprint( str(inspect.stack()[0][3]) + " Aucune donnée de payement, Impossible de créer la commande ") return False, " Aucune donnée de payement, Impossible de créer la commande ", None part_account = coll_part_account.find({'recid':user_recid, 'active':'1'}) """ /!\ : Si le montant à facturé à superieur à 0 alors on force le controle des données de facturation (email, adresse, etc) """ #print(" ### TOTAL TTC à FACTURER = "+str(total_ttc_float)) invoice_nom_check = "" if ("invoice_nom" in part_account[0].keys()): if part_account[0]['invoice_nom']: invoice_nom_check = part_account[0]['invoice_nom'] if(len(invoice_nom_check.strip()) == 0 and total_ttc_float > 0 ): mycommon.myprint( str(inspect.stack()[0][3]) + " Les données de facturation sont incompletes. Verifiez la raison sociale ") return False, "Les données de facturation sont incompletes. Verifiez la raison sociale ", None new_data['invoice_nom'] = invoice_nom_check invoice_adr_city_check = "" if ("invoice_adr_city" in part_account[0].keys()): if part_account[0]['invoice_adr_city']: invoice_adr_city_check = part_account[0]['invoice_adr_city'] if (len(invoice_adr_city_check.strip()) == 0 and total_ttc_float > 0 ): mycommon.myprint( str(inspect.stack()[0][ 3]) + " Les données de facturation sont incompletes. Verifiez la ville ") return False, "Les données de facturation sont incompletes. Verifiez la ville ", None new_data['invoice_adr_city'] = invoice_adr_city_check invoice_adr_country_check = "" if ("invoice_adr_country" in part_account[0].keys()): if part_account[0]['invoice_adr_country']: invoice_adr_country_check = part_account[0]['invoice_adr_country'] if (len(invoice_adr_country_check.strip()) == 0 and total_ttc_float > 0 ): mycommon.myprint( str(inspect.stack()[0][ 3]) + " Les données de facturation sont incompletes. Verifiez le pays ") return False, "Les données de facturation sont incompletes. Verifiez le pays ", None new_data['invoice_adr_country'] = invoice_adr_country_check invoice_adr_street_check = "" if ("invoice_adr_street" in part_account[0].keys()): if part_account[0]['invoice_adr_street']: invoice_adr_street_check = part_account[0]['invoice_adr_street'] if (len(invoice_adr_street_check.strip()) == 0 and total_ttc_float > 0 ): mycommon.myprint( str(inspect.stack()[0][ 3]) + " Les données de facturation sont incompletes. Verifiez l'adresse ") return False, "Les données de facturation sont incompletes. Verifiez l'adresse ", None new_data['invoice_adr_street'] = invoice_adr_street_check invoice_adr_zip_check = "" if ("invoice_adr_zip" in part_account[0].keys()): if part_account[0]['invoice_adr_zip']: invoice_adr_zip_check = part_account[0]['invoice_adr_zip'] if (len(invoice_adr_zip_check.strip()) == 0 and total_ttc_float > 0 ): mycommon.myprint( str(inspect.stack()[0][ 3]) + " Les données de facturation sont incompletes. Verifiez le code postal ") return False, "Les données de facturation sont incompletes. Verifiez le code postal ", None new_data['invoice_adr_zip'] = invoice_adr_zip_check invoice_email_check = "" if ("invoice_email" in part_account[0].keys()): if part_account[0]['invoice_email']: invoice_email_check = part_account[0]['invoice_email'] if (len(invoice_email_check.strip()) == 0 ): mycommon.myprint( str(inspect.stack()[0][ 3]) + " Les données de facturation sont incompletes. Verifiez l'adresse email ") return False, "Les données de facturation sont incompletes. Verifiez l'adresse email ", None new_data['invoice_email'] = invoice_email_check invoice_vat_num_check = "" if ("invoice_vat_num" in part_account[0].keys()): if part_account[0]['invoice_vat_num']: invoice_vat_num_check = part_account[0]['invoice_vat_num'] if ("invoice_telephone" in part_account[0].keys()): if part_account[0]['invoice_telephone']: new_data['invoice_telephone'] = part_account[0]['invoice_telephone'] new_data['invoice_vat_num'] = invoice_vat_num_check # Recuperation ds données de payement coll_part_payment = MYSY_GV.dbname['payement_mode'] local_count = coll_part_payment.count_documents({'client_recid': user_recid, 'valide': '1'}) if (local_count <= 0): mycommon.myprint( str(inspect.stack()[0][3]) + "Aucun mode de payement pour ce client, Impossible de créer la commande ") return False, "Aucun mode de payement pour ce client ", None part_account = coll_part_payment.find({'client_recid': user_recid, 'valide': '1'}) #print(" new_data['total_ht'] = "+str(new_data['total_ht']) + " -- new_data['total_tva'] " # +str(new_data['total_tva'])+ " -- new_data['total_ttc'] = "+ str(new_data['total_ttc'])) """ # Les données de payement ne sont utilisées que si le montant > 0 ou si il y a un code promo. En effet on peut avoir montant TTC = 0 par qu'on a benéficié d'un code promo à 100% """ if (total_ttc_float > 0 or len(str(discount_code)) > 2 ): if ("type" in part_account[0].keys()): if part_account[0]['type']: new_data['type_payment'] = part_account[0]['type'] if ("bic" in part_account[0].keys()): if part_account[0]['bic']: new_data['bic_payment'] = part_account[0]['bic'] if ("cvv_carte" in part_account[0].keys()): if part_account[0]['cvv_carte']: new_data['cvv_carte_payment'] = part_account[0]['cvv_carte'] if ("date_exp_carte" in part_account[0].keys()): if part_account[0]['date_exp_carte']: new_data['date_exp_carte_payment'] = part_account[0]['date_exp_carte'] if ("iban" in part_account[0].keys()): if part_account[0]['iban']: new_data['iban_payment'] = part_account[0]['iban'] if ("nom_carte" in part_account[0].keys()): if part_account[0]['nom_carte']: new_data['nom_carte_payment'] = part_account[0]['nom_carte'] if ("nom_compte" in part_account[0].keys()): if part_account[0]['nom_compte']: new_data['nom_compte_payment'] = part_account[0]['nom_compte'] if ("num_carte" in part_account[0].keys()): if part_account[0]['num_carte']: new_data['num_carte_payment'] = part_account[0]['num_carte'] new_data['valide'] = "1" now = datetime.now() new_data['date_update'] = str(now) new_data['order_date'] = str(now.strftime("%d/%m/%Y %H:%M:%S")) new_data['order_id'] = "MySy_00"+str(mycommon.create_order_id()) print(" ### la line à facturer est ::::: "+str(new_data)) # Enregistrement de la commande dans la systeme coll_order = MYSY_GV.dbname['sales_order'] ret_val = coll_order.insert_one(new_data) if ret_val and ret_val.inserted_id: #print( "str(new_data['invoice_email']) = "+str(new_data['invoice_email'])) # Envoie de l'email local_status, message = email.SalesOrderConfirmationEmail(str(new_data['invoice_email']), new_data ) if( local_status is False): mycommon.myprint( str(inspect.stack()[0][3]) + "Impossible de créer la commande "+str(message)) return False, "Impossible de créer la commande ", None # Mise à jour du partenaire (ajouter le nouveau pack) """ /!\ Important : Si le partenaire avait deja une date d'arret de son abonnement, la creation d'une nouvelle commande entraine automatiquement l'annulation de l'arret de l'abonnement. """ if ("pack" in diction.keys()): if diction['pack']: mypack = diction['pack'] coll_partner = MYSY_GV.dbname['partnair_account'] ret_val = coll_partner.find_one_and_update( {'recid': str(user_recid), }, {"$set": {'pack_service':str(mypack), 'nb_formation':str(nb_formation), 'abonnement_periodicite': str(periodicite), 'end_date_abonnement':''}}, upsert=False, return_document=ReturnDocument.AFTER ) # Apres la creation du nouvel abonnement, il faut aller cloturer l'ancien abonnement actif today_tmp = datetime.today().date() ret_val_cloture = coll_order.update_many({'order_id': {'$ne' : str(new_data['order_id'])}, 'valide': '1', 'client_recid':str(user_recid)}, {"$set": {'end_date': str(today_tmp), 'date_update': str(datetime.now())}}, ) # Facturation de la commande print(" ######## lancement de la facturation total_ttc_float = " + str(total_ttc_float)) # Recuperation des données du partenaire local_status_01, my_partner_01 = mycommon.get_partner_data_from_recid(user_recid) if (local_status_01 is False): mycommon.myprint(str(inspect.stack()[0][3]) + " - impossible de récupérer les données du partenaire") return False, str(inspect.stack()[0][3]) + " - impossible de récupérer les données du partenaire. " #print(" ### my_partner_01 = ", str(my_partner_01)) if ("stripe_subscription_id" in my_partner_01.keys()): new_data['stripe_subscription_id'] = my_partner_01['stripe_subscription_id'] #print(" ### new_data = ", str(new_data)) """ Ici creation de la facture mysy Regle : On ne crée et envoie que la première facture du client. Toutes les autres mises à jour ou changement d'abonnement ne seront envoyé qu'apres payement dans stripe. """ # Verifier si on a deja une 1ere facture has_stripe_invoice = False if( "stripe_account_id" in my_partner_01.keys() ): has_stripe_invoice = Stripe.Stripe_Customer_Has_Invoice(my_partner_01['stripe_account_id']) if( has_stripe_invoice is False ): print(" ### Il s'agit de la 1ere facturation pour le client ") if (total_ttc_float > 0 or len(str(discount_code)) > 2): local_invoice_status, local_invoice_retval = CreateInvoice(new_data) if( local_invoice_status == False): return False, local_invoice_retval, None """else: # La facture a bien été créée, on va mettre à jour le client/partenaire avec les champ : "send_pre_invoice_data" à '1', "date_time_pre_invoice_created" à date_time_now. ret_val_local = MYSY_GV.dbname['partnair_account'].find_one_and_update({"recid":str(user_recid), 'active':'1', 'locked':'0'}, {"$set": {'send_pre_invoice_data':'1', 'datetime_pre_invoice_created':str(datetime.now())}}, upsert=False, return_document=ReturnDocument.AFTER ) """ else: print(" ### Il NE s'agit PAS de la 1ere facturation du client ",str(my_partner_01['stripe_account_id'])) # La facture a bien été créée, on va mettre à jour le client/partenaire avec les champ : "send_pre_invoice_data" à '1', "date_time_pre_invoice_created" à date_time_now. ret_val_local = MYSY_GV.dbname['partnair_account'].find_one_and_update( {"recid": str(user_recid), 'active': '1', 'locked': '0'}, {"$set": {'send_pre_invoice_data': '1', 'datetime_pre_invoice_created': str(datetime.now())}}, upsert=False, return_document=ReturnDocument.AFTER ) # Apres la facturation, on met à jour le display_ranking des formations du partenaire. tmp_diction = {"partnaire_recid":str(user_recid), "new_pack_name":str(mypack)} class_mgt.UpdataPartnerRankingClass(tmp_diction) return True, "la commande été correctement créee", str(new_data['order_id']) else: mycommon.myprint( str(inspect.stack()[0][3]) + "Impossible de créer la commande ") return False, "Impossible de créer la commande ", None 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 la facture", False """ Cette fontion récupérer l'id de la dernière facture """ def Get_Last_Invoice_ID(): try: last_invoice_id = 0 coll_invoice = MYSY_GV.dbname["factures"] tmp_val = coll_invoice.count_documents({}) if( tmp_val ): last_invoice_id = tmp_val return True, last_invoice_id 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 l'ID de la dernière facture " """ Cette fonction créer une facture """ def CreateInvoice(diction): try: field_list_obligatoire = ['client_recid', 'invoice_nom', 'order_id', 'order_date', 'total_ttc','total_tva', 'total_ht','item_0'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint(str(inspect.stack()[0][ 3]) + " - Le champ '" + val + "' n'existe pas, Creation facture annulée") return False, " Impossible de créer la facture (1)" order_id = "" status_tmp, last_invoice_id = Get_Last_Invoice_ID() if( status_tmp is False): mycommon.myprint(str(inspect.stack()[0][ 3]) + " Impossible de récupérer Get_Last_Invoice_ID ") return False, " Impossible de créer la facture (2)" #print(" ######## last_invoice_id = "+str(last_invoice_id)) last_invoice_id = last_invoice_id + 1 now = datetime.now() prefix = "FACT_"+str(now.year)[-2:]+str(now.month)+str("000") Order_Invoice = str(prefix)+str(last_invoice_id) diction['invoice_id']= str(Order_Invoice) diction['due_date'] = str(now.strftime("%d/%m/%Y")) diction['invoice_date'] = str(now.strftime("%d/%m/%Y")) diction['printed'] = "0" #print(" collection de facture = "+str(diction)) coll_facture = MYSY_GV.dbname['factures'] ret_val = coll_facture.insert_one(diction) if ret_val and ret_val.inserted_id: #print("str(new_data['invoice_email']) = " + str(diction['invoice_email'])) #print("str(diction['invoice_id']) = " + str(diction['invoice_id'])) # Envoie de l'email de la facture PrintAndSendInvoices(str(diction['invoice_id'])) #email.SalesOrderConfirmationEmail(str(new_data['invoice_email']), new_data) """ Mise à jour de la commande avec le prochaine date de facturation """ next_invoice_date = "" if ("next_invoice_date" in diction.keys()): if diction['next_invoice_date']: local_status, tmp_date = mycommon.TryToDateYYYMMDD(diction['next_invoice_date']) #print(" apres conversion tmp_date = "+str(tmp_date)) if( local_status and str(diction['periodicite']).lower() == "mensuel" ): next_invoice_date = (tmp_date + relativedelta(months=+1)).date() #print(" ### next_invoice_date = "+str(next_invoice_date)) elif (local_status is False): #print(" ### IMPOSSIBLE DE FACTURER la COMMANDE") return False, "" if (local_status and str(diction['periodicite']).lower() == "annuel"): next_invoice_date = (tmp_date + relativedelta(years=+1)).date() #print(" ### next_invoice_date = " + str(next_invoice_date)) elif (local_status is False): #print(" ### IMPOSSIBLE DE FACTURER la COMMANDE") return False, "" elif( str(diction['periodicite']).lower() == "mensuel" ): next_invoice_date = datetime.today().date() + relativedelta(months=+1) elif (str(diction['periodicite']).lower() == "annuel"): next_invoice_date = datetime.today().date() + relativedelta(years=+1) print(" ####### prochaine facturation de la commande : "+str(diction['order_id'])+" LE : "+str(next_invoice_date)) coll_orders = MYSY_GV.dbname['sales_order'] ret_val_order = coll_orders.find_one_and_update( {'order_id': str(diction['order_id']) }, {"$set": {'next_invoice_date': str(next_invoice_date)}}, upsert=False, return_document=ReturnDocument.AFTER ) if ("id" not in ret_val_order.keys()): mycommon.myprint( " Impression de mettre à jour la date de la prochaine facturation de la commande : ° " + str(diction['order_id']) + " ") return True, " ok" 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 facturer la commande N° "+str(order_id)+" " """ Cette fonction recupere toutes le factures non imprimé (c'est a dire que le pdf n'est pas créé) 1 - Crée le fichier PDF 2 - Envoie l'email avec la facture """ def PrintAndSendInvoices(invoice_id=None): try: i = 0 query = {'printed':'0'} if ( invoice_id and len(str(invoice_id)) > 0): query = {'printed':'0','factures':str(invoice_id) } coll_invoice = MYSY_GV.dbname['factures'] for val in coll_invoice.find({'printed':'0'}): i = i +1 if( convertHtmlToPdf( val ) is False ): mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible d'imprimer la facture N° "+ str(val['invoice_id'])) else: mycommon.myprint(" Impression facture N° " +str(val['invoice_id'])+" OK") ret_val = coll_invoice.find_one_and_update( {'invoice_id': str(val['invoice_id'])}, {"$set": {'printed':'1'}}, upsert=False, return_document=ReturnDocument.AFTER ) if (ret_val['_id'] is False): mycommon.myprint(" Impression facture N° " +str(val['invoice_id'])+" ==> Impossible de mettre à jour la facture") return True, str(i)+" Factures ont été traitées" 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'imprimer les factures " def convertHtmlToPdf(diction): try: field_list_obligatoire =['invoice_nom', 'invoice_adr_street', 'invoice_adr_zip', 'invoice_adr_city', 'invoice_adr_country', 'invoice_id', 'invoice_date', 'due_date', 'order_id', 'item_0', 'total_ht', 'total_tva', 'total_ttc','client_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 la liste des arguments ") return False, " Impossible d'imprimer les factures" #print(" ### convertHtmlToPdf diction =", diction) discount_code = "" if ("discount_code" in diction.keys()): if diction['discount_code']: discount_code = diction['discount_code'] discount_type = "" if ("discount_type" in diction.keys()): if diction['discount_type']: discount_type = diction['discount_type'] discount_valeur = "" if ("discount_valeur" in diction.keys()): if diction['discount_valeur']: discount_valeur = diction['discount_valeur'] discount_montant_remise = "" if ("discount_montant_remise" in diction.keys()): if diction['discount_montant_remise']: discount_montant_remise = diction['discount_montant_remise'] discount_display_text = "" if (discount_type == "fix"): discount_display_text = " -" + str(discount_valeur) + " €" elif (discount_type == "percent"): discount_display_text = " -" + str(discount_valeur) + " %" templateLoader = jinja2.FileSystemLoader(searchpath="./") templateEnv = jinja2.Environment(loader=templateLoader) TEMPLATE_FILE = "Template/invoice.html" template = templateEnv.get_template(TEMPLATE_FILE) # This data can come from database query body = { "data": { "client_name": str(diction['invoice_nom']), "client_address": str(diction['invoice_adr_street']), "client_zip_ville": str(diction['invoice_adr_zip'])+" "+str(diction['invoice_adr_city']), "client_pays": str(diction['invoice_adr_country']), "invoice_id": str(diction['invoice_id']), "invoice_date": str(diction['invoice_date']), "due_date": str(diction['due_date']), "orign_order": str(diction['order_id']), "packs": str(diction['item_0']['code']), "qty": str(diction['item_0']['qty']), "unit_price": str(diction['item_0']['prix']), "montant": str(diction['item_0']['amount']), "total_ht": str(diction['total_ht']), "tva": str(diction['total_tva']), "total_ttc": str(diction['total_ttc']), "discount_montant_remise": discount_montant_remise, "discount_code": discount_code, "discount_type": discount_type, "discount_valeur": discount_valeur, "discount_display_text": discount_display_text, } } #print(" ### body = ", body) sourceHtml = template.render(json_data=body["data"]) orig_file_name = "invoice_"+str(diction['invoice_id'])+".pdf" outputFilename = str(MYSY_GV.INVOICE_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() #mycommon.myprint(str(inspect.stack()[0][3]) +" debut envoie de la factureeee "+diction['invoice_id']) email.SendInvoiceEmail(str(diction['invoice_email']), diction ) # On deplace la facture vers le serveur ftp mycommon.myprint( str(inspect.stack()[0][3]) + " On va deplacer de la facture vers " + str( MYSY_GV.INVOICE_FTP_LOCAL_STORAGE_DIRECTORY)) print(" ### GRRRR remoteFilePath = ", str(MYSY_GV.INVOICE_FTP_LOCAL_STORAGE_DIRECTORY)+str(orig_file_name)) cnopts = pysftp.CnOpts() cnopts.hostkeys = None with pysftp.Connection(host=MYSY_GV.MYSY_FTP_HOST, username=MYSY_GV.MYSY_FTP_LOGIN, password=MYSY_GV.MYSY_FTP_PWD, cnopts=cnopts) as session: print("Connection successfully established ... ") localFilePath = outputFilename remoteFilePath = str(MYSY_GV.INVOICE_FTP_LOCAL_STORAGE_DIRECTORY)+str(orig_file_name) print(" DEBUT DEPLACEMENT DE " + str(localFilePath) + " VERS " + str(remoteFilePath) + " AVANTTT TRAITEMENT") # Use put method to upload a file session.put(localFilePath, remoteFilePath) # Switch to a remote directory mycommon.myprint( str(inspect.stack()[0][3])+" DEPLACEMENT DE "+str(localFilePath)+" VERS "+str(remoteFilePath)+" EST OKKKK") # return True on success and False on errors print(pisaStatus.err, type(pisaStatus.err)) """ update du 09/07/23 - test envoie info prochaine facture local_diction = {} local_diction['recid'] = diction['client_recid'] email.Strip_Get_Customer_Upcoming_Invoice(local_diction) """ return True except Exception as e: mycommon.myprint( str(inspect.stack()[0][3]) +"Exception when calling SMTPApi->send_transac_email: %s\n" % 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 """ Cette fonction la facture PDF d'un client """ def GetCustomerInvoice(diction): try: ''' 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', 'invoiceid'] 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 la liste des arguments ") return False, " Impossible de récupérer la facture" user_recid = "None" my_token = "" my_invoiceid = "" my_invoice_name = "" if ("token" in diction.keys()): if diction['token']: my_token = diction['token'] if ("invoiceid" in diction.keys()): if diction['invoiceid']: my_invoiceid = diction['invoiceid'] my_invoice_name = 'invoice_'+str(my_invoiceid)+".pdf" #print(" on cherche la facure :"+my_invoice_name) # Verification de la validité du token/mail dans le cas des user en mode connecté if (len(str(my_token)) > 0): retval = mycommon.check_partner_token_validity("", my_token) if retval is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - La session de connexion n'est pas valide") return False, " Impossible de récupérer la formation" # Recuperation du recid de l'utilisateur user_recid = mycommon.get_parnter_recid_from_token(my_token) if user_recid is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de récupérer le token de l'utilisateur") return False, " Impossible de récupérer la formation" if (len(str(my_token)) <= 0): mycommon.myprint(str(inspect.stack()[0][3]) + " - Le token est vide") return False, " Impossible de récupérer la facture" # Recuperation de la facture depuis le serveur sFTP cnopts = pysftp.CnOpts() cnopts.hostkeys = None with pysftp.Connection(host=MYSY_GV.MYSY_FTP_HOST, username=MYSY_GV.MYSY_FTP_LOGIN, password=MYSY_GV.MYSY_FTP_PWD, cnopts=cnopts) as session: #print("Connection successfully established ... ") session.chdir(MYSY_GV.INVOICE_FTP_LOCAL_STORAGE_DIRECTORY) #print('our current working directory is: ', session.pwd) session.get(my_invoice_name, './temp_direct/'+str(my_invoice_name)) if os.path.exists("./temp_direct/"+str(my_invoice_name)): path = "./temp_direct/"+str(my_invoice_name) return send_file(path, as_attachment=True) 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, "KO" """ Cette fonction va créer toutes les facture pour les commandes dont la date facturation est arrivée à échéance (colonne : next_invoice_date) IMPORTANT : Meme la facturation n'est pas faire le meme jours, on qu'on a un retard de X jours, ceci n'est pas grave tant que X < 1 mois. - le end date doit etre vide Car à la facturation, la date prochaine facturation se mettra à date de dernière facture + 1 mois ou un 1 an. """ """ Cette fonction créer une facture """ def AutoamticCreateInvoice(): try: field_list_obligatoire = ['client_recid', 'invoice_nom', 'order_id', 'order_date', 'total_ttc','total_tva','total_ht','item_0'] coll_order = MYSY_GV.dbname['sales_order'] today = datetime.today().date() i = 0 for diction in coll_order.find({"next_invoice_date" : { '$lte' : str(today) }, 'end_date': { '$exists': False }},{'_id':0}): i = i +1 print(' Facturation de la commande '+str(diction['order_id'])+" -- "+str(diction['next_invoice_date'])) CreateInvoice(diction) return True, str(i)+" Factures ont été créées" 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 les factures automatiques"