accounting-micro-automation / reconciliate_and_upload.py
Adr740's picture
Update reconciliate_and_upload.py
75d91e5 verified
from uploader import save_logs
import os
import pandas as pd
from rapidfuzz import process, fuzz
from random import randint
from time import sleep
#other imports (google API)
def fuzzy_match(row, choices, scorer, cutoff):
match = process.extractOne(row['Libellé d\'opération'], choices, scorer=scorer, score_cutoff=cutoff)
if match:
return match[0]
return "missing receipt"
def reconciliate_and_upload(data_path,
name_of_csv,
folder_to_save_processed_reciepts,
folder_to_save_reconciled_data,
name_of_raw_transcripts = "transcript_raw.txt",
name_of_output = "[AI Generated] Output.xlsx" ):
with open(f"{data_path}/{name_of_raw_transcripts}") as file:
transcripts = eval(file.read())
imgs = []
path_to_pdfs =data_path
for root, dirs, files in os.walk(path_to_pdfs):
for file in files:
if file.endswith('.png'):
print(os.path.join(root, file))
imgs.append({"path": os.path.join(root, file)})
pass
list_transcripts_evaled = []
objects = []
for i,t in enumerate(transcripts):
content = eval(t["content"].replace('null', '-1'))
try:
obk = save_logs(imgs[i]["path"], f"P{i+1}.png", folder_to_save_processed_reciepts)
except:
print("sleeping a bit innit")
breakpoint()
sleep(randint(30,40))
obk = save_logs(imgs[i]["path"], f"P{i+1}.png", folder_to_save_processed_reciepts)
objects.append(obk)
print("uploaded image!")
try:
list_transcripts_evaled.append({
"path": imgs[i]["path"],
"name_of_supplier" :content["name_of_supplier"],
"amount":content["amount"],
"currency":content["currency"],
"date": content["date"]})
except:
breakpoint()
urls = []
for ob in objects:
url = "https://drive.google.com/file/d/" + ob["id"]
urls.append(url)
df_app = pd.DataFrame(list_transcripts_evaled)
float_regex = r'[-+]?\d*\.\d+|\d+'
df_app['amount'] = df_app['amount'].astype(str).str.extract(f'({float_regex})', expand=False)
# Replace spaces and commas, then handle N/A and convert to float
df_app['amount'] = (
df_app['amount']
.str.replace(" ", "", regex=False)
.str.replace(",", ".", regex=False)
.str.replace("N/A", "-1", regex=False)
.astype(float)
)
df_app["date"] = pd.to_datetime(df_app['date'], format="%d/%m/%Y", errors='coerce')
df_app["url"] = urls
df_app = df_app.drop_duplicates(["name_of_supplier", "amount", "date"]).reset_index(drop=True)
print(data_path)
print("NAME OF CSV: ", name_of_csv)
df_opp_app = pd.read_csv(f"{data_path}/{name_of_csv}",skiprows=3)
# df_opp_app["Débit"] = df_opp_app["Débit"].str.replace(" ", "").str.replace(",", ".").astype("float")
df_opp_app['Débit'] = df_opp_app['Débit'].astype(str).str.extract(f'({float_regex})', expand=False)
# Replace spaces and commas, then handle N/A and convert to float
df_opp_app['Débit'] = (
df_opp_app['Débit']
.str.replace(" ", "", regex=False)
.str.replace(",", ".", regex=False)
.str.replace("N/A", "-1", regex=False)
.astype(float)
)
# df_opp_app["Crédit"] = df_opp_app["Crédit"].str.replace(" ", "").str.replace(",", ".").astype("float")
df_opp_app['Crédit'] = df_opp_app['Crédit'].astype(str).str.extract(f'({float_regex})', expand=False)
# Replace spaces and commas, then handle N/A and convert to float
df_opp_app['Crédit'] = (
df_opp_app['Crédit']
.str.replace(" ", "", regex=False)
.str.replace(",", ".", regex=False)
.str.replace("N/A", "-1", regex=False)
.astype(float)
)
df_opp_app["Date"] = pd.to_datetime(df_opp_app['Date'], format="%d/%m/%Y", errors='coerce')
merged_df_app = pd.merge(df_opp_app, df_app, left_on=['Débit'], right_on=['amount'], how='left').drop(columns=["currency", "date","path"]).rename(columns={"name_of_supplier": "Nom fournisseur facture"})
merged_df_app["Nom fournisseur facture"] = merged_df_app["Nom fournisseur facture"].fillna("* Facture manquante *")
# Merge on amount (Débit and amount)
merged_df_app = pd.merge(df_opp_app, df_app, left_on='Débit', right_on='amount', how='left', suffixes=('_ops', '_df'))
# Apply fuzzy matching
raw_choices = df_app['name_of_supplier'].tolist()
choices = []
for r in raw_choices:
choices.append(r.upper())
merged_df_app['fuzzy_matched_supplier'] = merged_df_app.apply(lambda row: fuzzy_match(row, choices, fuzz.WRatio, 80), axis=1)
merged_df_app = merged_df_app.drop_duplicates(subset=["Date", "Valeur", "Libellé d'opération", "Débit"])
# Identify residuals in df that were not matched
df_residuals_app = df_app[~df_app['name_of_supplier'].isin(merged_df_app['name_of_supplier'])]
# Replace original supplier column with fuzzy_matched_supplier and drop the name_of_supplier column from df
merged_df_app['name_of_supplier'] = merged_df_app['fuzzy_matched_supplier']
# merged_df_app.drop(columns=['name_of_supplier', 'fuzzy_matched_supplier'], inplace=True)
merged_df_app.drop(columns=["name_of_supplier", "currency", "date", "path", "fuzzy_matched_supplier"], inplace=True)
df_residuals_app.drop(columns=["path"], inplace=True)
merged_df_app['url'] = merged_df_app['url'].apply(lambda x: f'=HYPERLINK("{x}", "Voir Facture")' if pd.notna(x) else '')
df_residuals_app['url'] = df_residuals_app['url'].apply(lambda x: f'=HYPERLINK("{x}", "Voir Facture")' if pd.notna(x) else '')
with pd.ExcelWriter(name_of_output) as writer:
merged_df_app.to_excel(writer, sheet_name='Données réconciliées', index=False)
df_residuals_app.to_excel(writer, sheet_name='Résidus et transactions introuvables', index=False)
id_output = save_logs(name_of_output, name_of_output , folder_to_save_reconciled_data)
return id_output