845 lines
42 KiB
Python
845 lines
42 KiB
Python
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("<KeyRelease>", 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] = "<AUTO>" # Compte collectif du tiers = <AUTO>
|
|
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() |