import csv import re import json import os import logging import shutil import subprocess import textwrap import time import uuid import tkinter from datetime import datetime import pickle import tkinter.filedialog from tkinter import * from tkinter import font from tkinter import messagebox from tkinter.ttk import Progressbar from tkinter.scrolledtext import ScrolledText from threading import * from dbfread import DBF from class_invoices import * from class_addresses import * from auto_update import * from class_debitors import * from version import * from custom_popup import * from cl_attriblink import cl_AttribLink src_dir = os.getenv('APPDATA') + '\\Attrib2Biz\\src' dest_dir = os.getenv('APPDATA') + '\\Attrib2Biz\\output' temp_dir = os.getenv('APPDATA') + '\\Attrib2Biz\\.temp' file_path ="" #os.path.dirname(__file__) Elements = [] export_csv = ""; con_csv = ""; if not os.path.exists(src_dir): os.makedirs(src_dir) if not os.path.exists(dest_dir): os.makedirs(dest_dir) if not os.path.exists(temp_dir): os.makedirs(temp_dir) class TextHandler(logging.Handler): """This class allows you to log to a Tkinter Text or ScrolledText widget""" def __init__(self, text): # run the regular Handler __init__ logging.Handler.__init__(self) # Store a reference to the Text it will log to self.text = text def emit(self, record): msg = self.format(record) print("pass 1 " + msg) def append(): self.text.configure(state='normal') self.text.insert(tkinter.END, msg + '\n') self.text.configure(state='disabled') # Autoscroll to the bottom self.text.yview(tkinter.END) # This is necessary because we can't modify the Text from other threads self.text.after(0, append) class ClercAttrib2Biz(): def __init__(self): self.count_facture = 0 self.a_index = {"invoice": [], "intervention": [], "patient": [], "debtor": [], "articles": [], "global": []} self.addresses = cls_Addresses() self.o_debs = cls_debitors(filepath=temp_dir) self.index_counter = 0 self.bs_counter = 70 self.a_listings = {} self.export_filename = "export.csv" self.fenetre = Tk() self.prompt = None self.b_prompt_open = False self.config = {} self.check_addresses = BooleanVar(self.fenetre) self.delete_after_parse = BooleanVar(self.fenetre) self.export_one_file = BooleanVar(self.fenetre) self.winbiz_folder_path = StringVar(self.fenetre) #Défault value for config self.check_addresses.set(True) self.export_one_file.set(True) self.delete_after_parse.set(False) self.winbiz_folder_path.set(temp_dir) self.load_config() self.draw_mainWindows() self.read_addresses() self.timer = self.fenetre.after(1000, self.main_timer_fn) self.fenetre.mainloop() def save_config(self, event=None): self.config["cfg_check_adresses"] = self.check_addresses.get() self.config["cfg_delete_after_parse"] = self.delete_after_parse.get() self.config["cfg_export_one_file"] = self.export_one_file.get() self.config["winbiz_folder_path"] = self.winbiz_folder_path.get() with open(os.path.join(temp_dir,"config.dat"),"wb") as pickle_file: pickle.dump(self.config,pickle_file, pickle.HIGHEST_PROTOCOL) def load_config(self): if not os.path.exists(os.path.join(temp_dir, "config.dat")): self.save_config() with open(os.path.join(temp_dir, "config.dat"), "rb") as pickle_file: self.config = pickle.load(pickle_file) self.check_addresses.set(self.read_config_element("cfg_check_adresses", True)) self.delete_after_parse.set(self.read_config_element("cfg_delete_after_parse", False)) self.export_one_file.set(self.read_config_element("cfg_export_one_file", True)) self.winbiz_folder_path.set(self.read_config_element("winbiz_folder_path", temp_dir)) def add_statistique(self, stat_name, stat_value): a_stats = None if "a_stats" in self.config: a_stats = self.config[""] def read_config_element(self,name, default): val = self.config.get(name) if val is not None: return val else: return default def main_timer_fn(self): #print("Pass tick timer main app") self.timer = self.fenetre.after(5000, self.main_timer_fn) self.nb_facture_var.set(self.count_facture) self.count_src() def draw_mainWindows(self): #Init de la fenêtre principale self.fenetre.iconbitmap("./logo_clerc_03X_icon.ico") self.fenetre.title("Clerc Attrib2Biz v" + str(VERSION)) self.fenetre.resizable(True, True) self.fenetre.geometry("700x475") self.fenetre.columnconfigure(0, weight=3) self.fenetre.columnconfigure(1, weight=1) self.fenetre.rowconfigure(3,weight=1) lbf_2 = LabelFrame(self.fenetre, text="Nb de facture") lbf_2.grid(row=1, column=0, sticky='W',padx=5, pady=5) lbf_3 = LabelFrame(self.fenetre, text="Options") lbf_3.grid(row=0, column=8, sticky='WE',padx=5, pady=5) #GUI Nb Files lbf_1 = LabelFrame(self.fenetre, text="Nb de fichiers") lbf_1.grid(row=0, column=0, sticky='WE', padx=5, pady=5) self.nb_files_var = IntVar() Label(lbf_1, textvariable=self.nb_files_var, anchor="e", font=('Helvetica', 13, 'bold')).grid(row=0, column=0, sticky="W", pady=5) Label(lbf_1, text="fichiers .json dans le dossier source", anchor="w" ).grid(row=0,column=1, sticky="W", pady=5) f1 = Frame(lbf_1) f1.grid(row=1, column=1) bt_open_src = Button(f1, text="Ouvrir le dossier source", command=self.open_src) bt_open_src.grid(row=1, column=0, padx=5, pady=5) self.bt_clear_src = Button(f1, text="Vider le dossier source", command=self.clear_src, state="disabled") self.bt_clear_src.grid(row=1, column=2, padx=5, pady=5) # GUI Nb facture lbf_2 = LabelFrame(self.fenetre, text="Nb de Facture", width=250) lbf_2.grid(row=1, column=0, sticky='WE', padx=5, pady=5) lbf_2.columnconfigure(2, weight=1) self.nb_facture_var = IntVar() Label(lbf_2, textvariable=self.nb_facture_var, anchor="e", font=('Helvetica', 13, 'bold')).grid(row=0, column=0, sticky="W", pady=5) Label(lbf_2, text="factures trouvées sur l'ensemble des fichiers", anchor="w").grid(row=0, column=1, sticky="W", pady=5) f2 = Frame(lbf_2) f2.grid(row=1, column=1) self.bt_parse = Button(f2, text="Lancer la conversion", command=self.start_parsing, state="disabled") self.bt_parse.grid(row=1, column=0,columnspan=2, padx=5, pady=5, sticky="W") bt_open_dest = Button(f2, text="Ouvrir le dossier de sortie", command=self.open_dest) bt_open_dest.grid(row=1, column=2, padx=5, pady=5, sticky="W") self.txt_progress = LabelFrame(self.fenetre, text="Progress") self.txt_progress.grid(row=2, column=0, sticky='WE', columnspan=2, padx=5, pady=5) self.txt_progress.columnconfigure(0,weight=1) self.progress_bar = Progressbar(self.txt_progress, orient=HORIZONTAL, mode='determinate') self.progress_bar.grid(sticky='WE') self.frame_logs = LabelFrame(self.fenetre, text="Log") self.frame_logs.grid(row=3, column=0, sticky='WENS', columnspan=2, padx=5, pady=5) self.frame_logs.columnconfigure(0, weight=1) self.frame_logs.rowconfigure(0, weight=1) st = ScrolledText( self.frame_logs, state='disabled', height=10) st.grid(row=0, column=0, sticky="WENS", pady=5) text_handler = TextHandler(st) self.logger = logging.getLogger() self.logger.addHandler(text_handler) lbf_3 = LabelFrame(self.fenetre, text="Options") lbf_3.grid(row=0, column=1, rowspan=2, sticky='NSEW', padx=5, pady=5) Checkbutton(lbf_3, text="Vérifier les Adresses", variable=self.check_addresses, onvalue=True, offvalue=False, command=self.save_config).grid(row=0, sticky='W') Checkbutton(lbf_3, text="Supprimer fichiers après conversion", variable= self.delete_after_parse, onvalue=True, offvalue=False, command=self.save_config).grid(row=1, sticky='W') Checkbutton(lbf_3, text="Export en 1 seul fichier", variable=self.export_one_file, onvalue=True, offvalue=False, command=self.save_config).grid(row=3, sticky='W') textEntry = Entry(lbf_3, textvariable=self.winbiz_folder_path, validate="focusout", validatecommand=self.save_config) textEntry.grid(row=5, sticky="WE", columnspan=3) textEntry.bind("", self.save_config) menu_bar = Menu(self.fenetre) menu_param = Menu(menu_bar, tearoff=0) menu_param.add_command(label="Affichage des débiteurs", command=self.o_debs.print_all_debs) menu_bar.add_cascade(label="Paramètre", menu=menu_param) self.fenetre.config(menu=menu_bar) self.var_file_count = StringVar(self.fenetre) self.running = False self.thread = Thread(target=self.start_parsing) auto_update = auto_updater() auto_update.temp_dir = temp_dir + '\\' update_available = auto_update.new_update_available() if update_available == True: self.logger.warning(f"Nouvelle version du script disponible. {auto_update.dl_version}") if messagebox.askyesno(title="Nouvelle version trouvée", message=f"Une nouvelle version a été trouvée, vous pouvez l'installer dès maintenant !", ): subprocess.call(temp_dir + "\\clerc_update.exe") self.fenetre.destroy() elif update_available == "ERROR": messagebox.showerror(title="ERREUR téléchargement", message="Erreur lors du téléchargement de la nouvelle mise à jour") def disable_prompt(self): self.prompt.destroy() self.b_prompt_open = False def read_addresses(self): if os.path.exists(os.path.join(self.winbiz_folder_path.get(),"adresses.dbf")): shutil.copyfile(os.path.join(self.winbiz_folder_path.get(),"adresses.dbf"), os.path.join(temp_dir,"adresses.dbf")) shutil.copyfile(os.path.join(self.winbiz_folder_path.get(), "adresses.FPT"),os.path.join(temp_dir, "adresses.FPT")) else: messagebox.showerror(title="Impossible d'ouvrir le fichier d'adresse existantes", message=f"Le fichier {self.winbiz_folder_path.get()} est introuvable. Aucune vérification des adresses possible") file_addresses_path = os.path.join(temp_dir, f"adresses.dbf") if os.path.exists(file_addresses_path): for record in DBF(file_addresses_path, load=True): o_addresse = cls_addresse() o_addresse.AVS = str(record["AD_CODE"]) o_addresse.lastName =str( record["AD_NOM"]) o_addresse.firstName = str(record["AD_PRENOM"]) if record["AD_NAISSAN"] is not None: o_addresse.birth = datetime.strptime(str(record["AD_NAISSAN"]), "%Y-%m-%d").strftime("%d.%m.%Y") else: o_addresse.birth = "" o_addresse.street =str( record["AD_RUE_2"]) o_addresse.street_cpl =str( record["AD_RUE_1"]) o_addresse.npa =str( record["AD_NPA"]) o_addresse.city = str(record["AD_VILLE"]) o_addresse.addr_2 = str(record["AD_ADR2"]).replace("\r\n","\n") self.addresses.add_addresse(o_addresse) logging.warning(f"Chargement du fichier adresses.dbf terminé ! {len(self.addresses.items)} adresses trouvées ") else: messagebox.showwarning(title="Impossible d'ouvrir le fichier d'adresse existantes", message=f"Le fichier {file_addresses_path} est introuvable. Aucune vérification des adresses possible") def start_parsing(self): dir = src_dir self.count_facture = 0 x = 0 self.export_filename = f"bizexdoc_export_Attrib_v{datetime.now().year}{datetime.now().month}{datetime.now().day}{datetime.now().hour}{datetime.now().minute}.csv" if self.export_one_file.get() and os.path.exists(os.path.join(dest_dir, self.export_filename)): try: os.remove(os.path.join(dest_dir, self.export_filename)) except: messagebox.showerror(title="Erreur fichier déjà ouvert", message=f"Le fichier {self.export_filename} est déjà ouvert. Veuillez le fermer et recommencer") return print(f"remove {self.export_filename} => {os.path.join(dest_dir, self.export_filename)}") self.progress_bar["value"] = 0 for filename in os.listdir(dir): x += 1 if ".json" in filename: print(filename) with open(dir + "/" + filename, encoding="utf-8") as f: data = json.load(f) bRet = self.parseFile(data, filename) if bRet: messagebox.showinfo(title="Fin de conversion", message="La conversion s'est terminée avec succès") self.progress_bar["value"] = x / len(os.listdir(dir)) * 100 self.fenetre.update_idletasks() self.nb_facture_var.set(self.count_facture) if self.delete_after_parse.get(): os.remove(dir + "/" + filename) def ifNotNull(self,value, ret=""): if value == None: return ret return value.strip() def trim_all_data(self, data): if self.attrib_link.patient_lastname is not None: self.attrib_link.patient_lastname = self.attrib_link.patient_lastname.strip() if self.attrib_link.patient_firstname is not None: self.attrib_link.patient_firstname = self.attrib_link.patient_firstname.strip() if self.attrib_link.patient_street is not None: self.attrib_link.patient_street = self.attrib_link.patient_street.strip() if self.attrib_link.debtor_lastname is not None: self.attrib_link.debtor_lastname = self.attrib_link.debtor_lastname.strip() if self.attrib_link.debtor_firstname is not None: self.attrib_link.debtor_firstname = self.attrib_link.debtor_firstname.strip() if self.attrib_link.debtor_street is not None: self.attrib_link.debtor_street = self.attrib_link.debtor_street.strip() if self.attrib_link.comments is not None: self.attrib_link.comments = self.attrib_link.comments.strip().replace("\n",'') if self.attrib_link.debtor_code is not None: self.attrib_link.debtor_code = self.attrib_link.debtor_code.strip() self.attrib_link.debtor_code = re.sub(r'[^0-9]', '', self.attrib_link.debtor_code) if "name" in data.Debtor.keys() and self.attrib_link.debtor_name is not None: self.attrib_link.debtor_name = self.attrib_link.debtor_name.replace("(facturation)","") self.attrib_link.debtor_name = self.attrib_link.debtor_name.strip() def check_required_field_by_code(self, code, data): required_fields = { 1: ["firstname", "lastname", "street"], # facturation au patient code = 1 } if code in required_fields: for field in required_fields[code]: if data.Patient[field] is None or data.Debtor[field] is None: messagebox.showerror( title="Erreur Critique", message=f"Les informations de la facture [{self.attrib_link.invoice_ID}] comportent trop d'erreurs. Arrêt du processus.", ) return False return True def compare_old_and_new_adresses(self, new, old, code): print(f"compare_old_and_new_adresses ") if self.ifNotNull(new['insurance_policy_number']).replace('.','') != old.AVS: print("compare adresse exit avs") return False if self.ifNotNull(new['lastname']).replace('.','') != old.lastName: print("compare adresse exit lastname") return False if self.ifNotNull(new['firstname']).replace('.','') != old.firstName: print("compare adresse exit firstname") return False if datetime.strptime(self.ifNotNull(new['birthdate']), "%Y-%m-%d").strftime("%d.%m.%Y") != old.birth: print("compare adresse exit date") return False if f"{self.ifNotNull(new['street']).strip()} {self.ifNotNull(new['street_number']).strip()}" != f"{self.ifNotNull(old.street).strip()}": print(f"compare adresse exit ({self.ifNotNull(new['street']).strip()} {self.ifNotNull(new['street_number']).strip()}) != [" + self.ifNotNull(old.street).strip()+"]") return False if f"{self.ifNotNull(new['postal_code'])} {self.ifNotNull(new['city'])}" != f"{self.ifNotNull(old.npa)} {self.ifNotNull(old.city)}": print("compare adresse exit city") return False if new["addr_2"] != old.addr_2 and int(code) < 100: if not new["selfish"]: print(f"Exit selfish") return False if old.addr_2 != "None": print(f"Exit addr_2 = {old.addr_2}/{new['selfish']}") print(old.addr_2 != "None") return False print("return True (same)") return True def parseFile(self, data, filename): self.attrib_link = cl_AttribLink() self.progress_bar["value"] = 0 self.index_counter += 1 lines = [] self.count_facture += len(data["invoices"]) self.a_listings["to_check"] = [] if self.index_counter == 1: csv_col = cls_Col(True) csv_col.data[0] = "V:" + VERSION csv_col.data[5] = f"" lines.append(csv_col.data) cur_invoice_index = 0 total_facture_total = 0 total_facture_amount = 0 for ele in data["invoices"]: cur_invoice_index += 1 self.progress_bar["value"] = cur_invoice_index / self.count_facture * 100 b_address_update = True data = cls_Invoice() data.parse_item(ele) self.trim_all_data(data) self.attrib_link.parse_data(data) print(f"Code débiteur => {self.attrib_link.invoice_ID}: {self.attrib_link.debtor_code}") if self.attrib_link.debtor_code is None or '.' in self.attrib_link.debtor_code: print("ERROR code débiteur #1") messagebox.showerror(title="Erreur code débiteur", message=f"Le code débiteur de la facture {self.attrib_link.invoice_ID} est faux: [{self.attrib_link.debtor_code}], merci de le corriger ") inp_popup = Input_popup(self.fenetre, default=self.attrib_link.debtor_code, factureID=self.attrib_link.invoice_ID, fip=self.attrib_link.FIP) self.attrib_link.debtor_code = str(inp_popup.show()) if self.attrib_link.debtor_code is None or '.' in self.attrib_link.debtor_code: messagebox.showerror(title="Erreur Critique", message=f"Les informations de la factures [{self.attrib_link.invoice_ID}] comportes trop d'erreur arrêt du processus ") return False if int(self.attrib_link.debtor_code) == 1: if not self.check_required_field_by_code(self.attrib_link.debtor_code, data): messagebox.showerror(title="Erreur Critique", message=f"Les informations de la factures [{self.attrib_link.invoice_ID}] comportes trop d'erreur arrêt du processus ") return False if self.attrib_link.debtor_code != "1" and self.attrib_link.debtor_code != None and int(self.attrib_link.debtor_code) < 100: if self.attrib_link.FIP not in self.a_listings["to_check"]: self.a_listings["to_check"].append(self.attrib_link.FIP) #self.logger.warning(f"Vérifier facture N°: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP} {self.attrib_link.debtor_code} {self.attrib_link.debtor_lastname} {self.attrib_link.debtor_firstname}") if int(self.attrib_link.debtor_code) == 1: b_check_debitor = False if self.attrib_link.debtor_lastname == None: b_check_debitor = True self.attrib_link.debtor_lastname = "" messagebox.showerror(title="Erreur", message=f"Le débiteur n'a pas de nom, vérifier la facture ATTRIB et vérifier le code débiteur: facture N°: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP}") if self.attrib_link.debtor_firstname == None: b_check_debitor = True self.attrib_link.debtor_firstname = "" messagebox.showerror(title="Erreur", message=f"Le débiteur n'a pas de prénom, vérifier la facture ATTRIB et vérifier le code débiteur: facture N°: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP}") if b_check_debitor: debitor_popup = Check_debitor_popup(self.fenetre, data.Debtor, self.o_debs.get_names_by_code(self.attrib_link.debtor_code), self.attrib_link.invoice_ID, self.attrib_link.FIP, object=self.o_debs) self.attrib_link.debtor_code = debitor_popup.show() if self.attrib_link.patient_lastname + self.attrib_link.patient_firstname != self.attrib_link.debtor_lastname + self.attrib_link.debtor_firstname: self.a_listings["to_check"].append(self.attrib_link.FIP) print(self.attrib_link.patient_addr_complement) self.logger.warning(f"Débiteur différents: facture N°: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP}") if int(self.attrib_link.debtor_code) >= 100: if "name" not in data.Debtor.keys(): self.attrib_link.debtor_name = "Invalide" self.logger.warning( f"Débiteur > 100 sans nom, vérifier la facture ATTRIB et vérifier le code débiteur: facture N°: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP}") messagebox.showerror(title="Erreur", message=f"Débiteur > 100 sans nom de débiteur ou d'établissement, vérifier la facture ATTRIB et vérifier le code débiteur: facture N°: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP}") self.a_listings["to_check"].append(self.attrib_link.FIP) if not self.o_debs.is_in_debitor_name(code=self.attrib_link.debtor_code,search_name=self.attrib_link.debtor_name): #messagebox.showerror(title="Erreur code débiteur erroné", message=f"Information débiteur incohérente: facture N°: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP}.\nCode débiteur: {self.attrib_link.debtor_code}\nNom du débiteur: {data.Debtor['name']}. \nAurait dû être: {self.o_debs.get_names_by_code(self.attrib_link.debtor_code)}") debitor_popup = Check_debitor_popup(self.fenetre,data.Debtor,self.o_debs.get_names_by_code(self.attrib_link.debtor_code),self.attrib_link.invoice_ID,self.attrib_link.FIP, object=self.o_debs) self.attrib_link.debtor_code = debitor_popup.show() if self.attrib_link.debtor_code is None or '.' in self.attrib_link.debtor_code: messagebox.showerror(title="Erreur Critique", message=f"Les informations de la factures [{self.attrib_link.invoice_ID}] comportes trop d'erreur arrêt du processus ") return False if self.attrib_link.patient_AVS == None: self.attrib_link.patient_AVS = f"{uuid.uuid4()}"[:15] if self.check_addresses.get(): addresses_exist = self.addresses.get_item_by_AVS(self.attrib_link.patient_AVS.replace(".", "")) if addresses_exist is not None and not self.compare_old_and_new_adresses(new=data.Patient, old=addresses_exist, code=self.attrib_link.debtor_code): popup = Check_addresses_popup(self.fenetre, item_1=data.Patient, item_2=addresses_exist, debitor=data.Debtor, factureID=self.attrib_link.invoice_ID) b_address_update = popup.show() mode_selected = "Attrib" if b_address_update else "Winbiz" self.logger.warning( f"Adresse connue données {mode_selected} conservées: {self.attrib_link.invoice_ID} / {self.attrib_link.FIP} {self.attrib_link.debtor_code} {self.attrib_link.debtor_lastname} {self.attrib_link.debtor_firstname}") print(f"Result Popup: {b_address_update}") else: b_address_update = True #self.draw_test(item_1=data.Patient, item_2=addresses_exist) #messagebox.showerror(title="AVS Trouvé", message=f"Le code AVS de l'adresse {self.attrib_link.patient_AVS} est déjà existant: [{self.attrib_link.debtor_code}], merci de le corriger ") b_HRF = False sHRF = "" b_VSL = False facture_total = 0 for article in data.Articles: if 'price' in article: facture_total += float(self.attrib_link.get_value_from_attrib(data=article,obj="article",key="price")) if "code" in article.keys(): temp_code = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="code") if temp_code == "HRF": b_HRF = True sHRF = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_1").replace("h", ":") print(f"pass HRF => {self.attrib_link.FIP} = {b_HRF}") #self.logger.warning(f"HRF: {self.attrib_link.invoice_ID}") elif "VSL" in temp_code: b_VSL = True #print(f"Lecture de la facture #{cur_invoice_index} {self.attrib_link.get_value_from_attrib(data=ele,obj="invoice",key="id")} montant total = {self.attrib_link.get_value_from_attrib(data=ele,obj="invoice",key="total_price")} <> {facture_total}") if float(self.attrib_link.get_value_from_attrib(data=ele,obj="invoice",key="total_price")) != facture_total: print(f"ERROR NOT SAME PRICE") total_facture_total += facture_total total_facture_amount += float(self.attrib_link.get_value_from_attrib(data=ele,obj="invoice",key="total_price")) for article in data.Articles: self.bs_counter += 1 csv_col = cls_Col(True) print(article) if "code" in article.keys() and self.attrib_link.get_value_from_attrib(data=article,obj="article",key="code") == "HRF": break ##Donnée globales csv_col.data[0] = self.attrib_link.invoice_ID # N° document csv_col.data[1] = 20 # Type of document 20 = facture débiteur csv_col.data[2] = datetime.strptime(self.attrib_link.invoice_date, "%Y-%m-%d").strftime("%d.%m.%Y") # Date du document csv_col.data[4] = self.attrib_link.FIP # Référence csv_col.data[10] = "" # Compte collectif du tiers = csv_col.data[139] = "COND-30" # Conditions de payement à 30, COND-30 selon config dans winbiz if "without_transportation" in data.Intervention.keys() and data.Intervention["without_transportation"] == True: csv_col.data[15] = "Sans transport" print(f"->NO DEST** {self.attrib_link.invoice_ID}") elif self.attrib_link.intervention_dest_name == None and self.attrib_link.intervention_dest_street == None and self.attrib_link.intervention_dest_city == None: csv_col.data[15] = "Sans destination" print(f"->NO DEST sans destination transmis à autruit** {self.attrib_link.invoice_ID}") else: con = "" concat_str = "" if self.attrib_link.intervention_dest_name is not None: concat_str += con + self.attrib_link.intervention_dest_name con = ", " if self.attrib_link.intervention_dest_street is not None: concat_str += con + self.attrib_link.intervention_dest_street + " " + self.ifNotNull(self.attrib_link.intervention_dest_street_number) con = ", " concat_str += con + self.attrib_link.intervention_dest_postal_code + " " + self.attrib_link.intervention_dest_city csv_col.data[15] = concat_str # Adresse DEST con = "" concat_str = "" if self.attrib_link.intervention_site_name is not None: concat_str += con + self.attrib_link.intervention_site_name con = ", " if self.attrib_link.intervention_site_street is not None: concat_str += con + self.ifNotNull(self.attrib_link.intervention_site_street) + " " + self.ifNotNull(self.attrib_link.intervention_site_street_number) con = ", " concat_str += con + self.ifNotNull(self.attrib_link.intervention_site_postal_code) + " " + self.ifNotNull(self.attrib_link.intervention_site_city) csv_col.data[18] = concat_str # Adresse PEC print(f'debug FIP:{self.attrib_link.FIP} id:{self.attrib_link.invoice_ID} code: {self.attrib_link.debtor_code}') if int(self.attrib_link.debtor_code) < 100: csv_col.data[19] = self.attrib_link.patient_AVS.replace(".","") csv_col.data[22] = self.attrib_link.patient_lastname csv_col.data[23] = self.attrib_link.patient_firstname csv_col.data[24] = self.attrib_link.patient_addr_complement csv_col.data[25] = self.ifNotNull(self.attrib_link.patient_street) + " " + self.ifNotNull(self.attrib_link.patient_street_number) csv_col.data[26] = self.attrib_link.patient_postal_code csv_col.data[27] = self.attrib_link.patient_city if self.attrib_link.patient_country != "Suisse": csv_col.data[29] = self.attrib_link.patient_country csv_col.data[41] = "Monsieur" if self.attrib_link.patient_gender == "Masculin" else "Madame" csv_col.data[44] = csv_col.data[41] #csv_col.data[46] = 0 # Maj adresse if b_address_update: csv_col.data[46] = 0 # Maj adresse ajoute ou met à jour l'adresse print("mise à jour de l'adresse") else: csv_col.data[46] = 1 # Maj adresse, ajoute, mais ne met pas à jour l'adresse si elle existe print("NON mise à jour de l'adresse") if int(self.attrib_link.debtor_code) > 1: csv_col.data[42] = self.attrib_link.patient_addr_2.replace("\n", "#chr(13)##chr(10)#") #Adresse de livraison else: csv_col.data[19] = self.attrib_link.debtor_code # Code adresse à récupérer dans recherche d'adresse automatisée csv_col.data[22] = self.attrib_link.debtor_lastname csv_col.data[23] = self.attrib_link.debtor_firstname csv_col.data[24] = self.attrib_link.debtor_addr_complement csv_col.data[25] = self.ifNotNull(self.attrib_link.debtor_street) + " " + self.ifNotNull(self.attrib_link.debtor_street_number) csv_col.data[26] = self.attrib_link.debtor_postal_code csv_col.data[27] = self.attrib_link.debtor_city if self.attrib_link.debtor_country != "Suisse": csv_col.data[29] = self.attrib_link.debtor_country csv_col.data[46] =1 # Maj adresse, ajoute, mais ne met pas à jour l'adresse si elle existe if "name" in data.Debtor.keys(): csv_col.data[21] = self.ifNotNull(self.attrib_link.debtor_name) else: csv_col.data[21] = '' if self.attrib_link.patient_birthdate is not None: csv_col.data[40] = datetime.strptime(self.attrib_link.patient_birthdate, "%Y-%m-%d").strftime("%d.%m.%Y") else: csv_col.data[40] = "" messagebox.showerror(title="Erreur date de naissance", message=f"La date de naissance de la facture {self.attrib_link.invoice_ID} ({self.attrib_link.patient_lastname}) est faux: [{self.attrib_link.patient_birthdate}], merci de le corriger ") csv_col.data[48] = 0 ## Données prestations if "code" in article.keys(): csv_col.data[49] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="code") # Code de prestations csv_col.data[50] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_1") if self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_2") != None: csv_col.data[50] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_1") + "#chr(13)##chr(10)#" + self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_2") # Descriptions prestations csv_col.data[52] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="quantity") # Quantité csv_col.data[53] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="unit_price") # prix unitaire csv_col.data[54] = self.ifNotNull(self.attrib_link.get_value_from_attrib(data=article,obj="article",key="unit")) # unité csv_col.data[56] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="price") # prix total else: csv_col.data[49] = "0" csv_col.data[50] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_1") if self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_2") != None: csv_col.data[50] = self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_1") + " / " + self.attrib_link.get_value_from_attrib(data=article,obj="article",key="line_2") # Descriptions prestations csv_col.data[52] = 0 csv_col.data[53] = 0 csv_col.data[56] = 0 # prix total compte_number = None if b_VSL: compte_number = 3019 else: if self.attrib_link.intervention_base == "Uvrier": compte_number = 3018 else: if self.attrib_link.intervention_type == "P1": compte_number = 3001 if self.attrib_link.intervention_team == "Planification" else 3011 if self.attrib_link.intervention_type == "P2": compte_number = 3002 if self.attrib_link.intervention_team == "Planification" else 3012 if self.attrib_link.intervention_type == "P3": compte_number = 3003 if self.attrib_link.intervention_team == "Planification" else 3013 if self.attrib_link.intervention_type == "S1": compte_number = 3004 if self.attrib_link.intervention_team == "Planification" else 3014 if self.attrib_link.intervention_type == "S2": compte_number = 3005 if self.attrib_link.intervention_team == "Planification" else 3015 if self.attrib_link.intervention_type == "S3": compte_number = 3005 if self.attrib_link.intervention_team == "Planification" else 3015 csv_col.data[59] = compte_number csv_col.data[60] = 0 csv_col.data[61] = 0 csv_col.data[63] = 0 csv_col.data[69] = "Libre" csv_col.data[70] = "BS - " + str(self.bs_counter) csv_col.data[72] = "BU - 73 - aff" csv_col.data[73] = 0 #Objet facture csv_col.data[108] = self.ifNotNull(self.attrib_link.patient_lastname) + " " + self.ifNotNull(self.attrib_link.patient_firstname) csv_col.data[115] = self.attrib_link.patient_addr_complement csv_col.data[109] = datetime.strptime(self.attrib_link.patient_birthdate, "%Y-%m-%d").strftime("%d.%m.%Y") if self.attrib_link.patient_birthdate is not None else "" if self.attrib_link.patient_AVS is not None and '-' not in self.attrib_link.patient_AVS: csv_col.data[109] += ", " + self.attrib_link.patient_AVS csv_col.data[110] = self.attrib_link.patient_insurance_name date_PEC = datetime.strptime(self.attrib_link.intervention_start_time, "%Y-%m-%dT%H:%M:%S%z").strftime( "%d.%m.%Y") csv_col.data[51] = date_PEC # Date de ligne articles csv_col.data[111] =date_PEC # Date PEC csv_col.data[111] += " - " + datetime.strptime(self.attrib_link.intervention_start_time, "%Y-%m-%dT%H:%M:%S%z").strftime( "%H:%M") # Heure START PEC if b_HRF: csv_col.data[111] += "-" + sHRF else: csv_col.data[111] += "-" + datetime.strptime(self.attrib_link.intervention_end_time, "%Y-%m-%dT%H:%M:%S%z").strftime( "%H:%M") # Heure END PEC csv_col.data[112] = self.ifNotNull(self.attrib_link.patient_street) + " " + self.ifNotNull(self.attrib_link.patient_street_number) csv_col.data[113] = self.ifNotNull(self.attrib_link.patient_postal_code) + " " + self.ifNotNull(self.attrib_link.patient_city) if self.attrib_link.patient_country != "Suisse": csv_col.data[113] += ", " + self.attrib_link.patient_country csv_col.data[107] = self.attrib_link.FIP csv_col.data[107] += " - " + self.attrib_link.comments if self.attrib_link.comments is not None else "" csv_col.data[116] = self.ifNotNull(self.attrib_link.patient_category) + " - " + self.ifNotNull(self.attrib_link.intervention_type) csv_col.data[118] = self.attrib_link.km csv_col.data[135] = self.attrib_link.intervention_base if self.attrib_link.debtor_code in ["100", "101", "158"]: temp_facture_model = "EBILL" # code présentation de facture elif b_VSL: temp_facture_model = "VSL" # code présentation de facture else: temp_facture_model = 3 print(f"code = {self.attrib_link.debtor_code}, model: {temp_facture_model}") csv_col.data[136] = temp_facture_model #code présentation de facture csv_col.data[146] = 3 #Code méthode de payement définit à 3 csv_col.data[168] = 1 if self.attrib_link.intervention_team == "Planification" else 2 csv_col.data[169] = self.attrib_link.patient_AVS lines.append(csv_col.data) csv_col = None print(f"total_facture_total = {total_facture_total} <> {total_facture_amount}") if not self.export_one_file.get(): with open(os.path.join(dest_dir, f"bizexdoc_" + filename.replace(".json", "") + ".csv"), "w+", errors='replace', newline='') as csv_file: wr = csv.writer(csv_file, delimiter=';') for cdr in lines: wr.writerow(cdr) else: self.save_file(lines,fileName=self.export_filename) return True def addToIndexs(self,obj,data,cat, key): #self.a_index[cat].append({"key":key,"index":len(obj.data)}) if key not in self.a_index[cat]: self.a_index[cat].append(key) self.a_index["global"].append(key) obj.data.append(data) def save_file(self,lines, fileName = "src.csv"): with open(os.path.join(dest_dir, fileName), "a+", errors='replace',newline='') as csv_file: wr = csv.writer(csv_file, delimiter=';') for cdr in lines: wr.writerow(cdr) def reset_export_file(self): if os.path.exists(os.path.join(dest_dir, f"src.csv")): os.remove(os.path.join(dest_dir, f"src.csv")) def open_export_file(self): print("RUN EXCEL with open") os.startfile(os.path.join(dest_dir, f"src.csv")) shutil.copy(file_path + "convert.xlsm", os.path.join(dest_dir, f"convert.xlsm")) time.sleep(1) os.startfile(os.path.join(dest_dir, f"convert.xlsm")) def open_src(self): os.startfile(file_path + src_dir) def open_dest(self): os.startfile(file_path + dest_dir) def clear_src(self): file_in_src = os.listdir(src_dir) for file in file_in_src: os.remove(src_dir + "\\" + file) print(f"Supression du fichier: {file}") self.count_src() def clear_dest(self): file_in = os.listdir(dest_dir) for file in file_in: os.remove(dest_dir + "\\" + file) print(f"Supression du fichier: {file}") def count_src(self): file_in_src = os.listdir(src_dir) count = len(file_in_src) if count > 0: self.bt_clear_src["state"] = "normal" self.bt_parse["state"] = "normal" else: self.bt_clear_src["state"] = "disabled" self.bt_parse["state"] = "disabled" if not self.running: self.nb_files_var.set(count) app = ClercAttrib2Biz()