Browse Source

first working version

Sylhaf 2 years ago
commit
e1ec9c1509
6 changed files with 251 additions and 0 deletions
  1. 8 0
      .gitignore
  2. 49 0
      app_logging.py
  3. 164 0
      main.py
  4. BIN
      msedgedriver.exe
  5. 6 0
      references_example.conf
  6. 24 0
      requirements.txt

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+logs
+logs/**
+logs\**
+__pycache__
+__pycache__/**
+__pycache__\**
+references.conf
+test_stripe.py

+ 49 - 0
app_logging.py

@@ -0,0 +1,49 @@
+from distutils.debug import DEBUG
+import logging
+from logging.handlers import RotatingFileHandler
+
+global logger_name
+logger_name = "slash-dolistripe"
+__is_init__ = False
+
+
+
+
+
+def init() :
+
+    global __is_init__
+    if __is_init__ :
+        return
+    #create log folder at execution path"
+    import os
+    if not os.path.exists('logs'):
+        os.makedirs('logs')
+
+    # Create a custom logger
+    logger = logging.getLogger(logger_name)
+
+    #TODO create Categories here
+    # of this style : logger = logging.getLogger(logger_name + ".persistence")
+
+    # Create handlers
+    c_handler = logging.StreamHandler()
+    f_handler = RotatingFileHandler('logs/slash-dolistripe.log',encoding = "UTF-8",backupCount=5,maxBytes=20000000) # 2mo per log
+    c_handler.setLevel(logging.DEBUG)
+    f_handler.setLevel(logging.DEBUG)
+    f_handler.doRollover()
+
+    # Create formatters and add it to handlers
+    c_format = logging.Formatter('[%(asctime)s] - %(name)s - %(levelname)s - %(message)s')
+    f_format = logging.Formatter('[%(asctime)s] - %(name)s - %(levelname)s - %(message)s')
+    c_handler.setFormatter(c_format)
+    f_handler.setFormatter(f_format)
+
+    # Add handlers to the logger
+    logger.addHandler(c_handler)
+    logger.addHandler(f_handler)
+
+    logger.setLevel(logging.DEBUG)
+
+    logger.debug("logging engine started")
+    __is_init__ = True

+ 164 - 0
main.py

@@ -0,0 +1,164 @@
+
+from asyncio.windows_events import NULL
+from time import sleep
+from app_logging import logger_name, init as init_logging
+import logging
+logger = logging.getLogger(logger_name)
+import requests,json
+import datetime
+import argparse
+
+
+init_logging()
+
+logger.debug(__name__)
+
+
+logger.info("")
+logger.info("")
+logger.info("")
+logger.info("-------         welcome to Slash-Dolistripe        -------")
+
+# Method to read config file settings
+logger.info(" --- -------------------------------------------------------------------------------- ---")
+logger.info(" --- ---------------------------------- ARG PHASE ----------------------------------- ---")
+logger.info(" --- -------------------------------------------------------------------------------- ---")
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-v","--verbosity", help="increase output verbosity",action="store_true")
+parser.add_argument("-d","--dry", help="perform a dry run",action="store_true")
+parser.add_argument("-m","--mail", help="send invoice per mail to client (you can add a contact mail copy in the reference.conf file)",action="store_true")
+parser.add_argument("-p","--planned", help="trigger planned work to ",action="store_true")
+
+args = parser.parse_args()
+if args.verbosity:
+    logger.info("Args : Debug Verbose Enabled")
+    logger.setLevel(logging.DEBUG)
+else :
+    logger.setLevel(logging.INFO)
+
+args = parser.parse_args()
+if args.dry:
+    logger.info("Args : Dry Run Enabled")
+
+args = parser.parse_args()
+if args.mail:
+    logger.info("Args : send invoice per Mails Enabled")
+
+
+logger.info(" --- -------------------------------------------------------------------------------- ---")
+logger.info(" --- ---------------------------------- CONF PHASE ---------------------------------- ---")
+logger.info(" --- -------------------------------------------------------------------------------- ---")
+logger.info("Reading Reference ConfIguration File")
+import configparser
+config = configparser.ConfigParser()
+config.optionxform = str # to make the read Case Sensitive 
+config.read('references.conf')
+
+dolibarr_url      = config["credentials"]["dolibarr_url"]
+dolibarr_username = config["credentials"]["dolibarr_username"]
+dolibarr_password = config["credentials"]["dolibarr_password"]
+
+if args.planned :
+    dolibarr_planned_work_key = config["credentials"]["planned_work_key"]
+    dolibarr_planned_work_cron_job_id = config["credentials"]["cron_job_id"]
+
+contact_mail = None
+
+if config.has_option("credentials", "contact_mail") :
+    contact_mail = config["credentials"]["contact_mail"]
+
+logger.debug("contact mail in configuration is : " + contact_mail)
+
+
+logger.info(" --- -------------------------------------------------------------------------------- ---")
+logger.info(" --- -------------------------------- READ CRM PHASE -------------------------------- ---")
+logger.info(" --- -------------------------------------------------------------------------------- ---")
+
+from selenium import webdriver
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.common.by import By
+import logging
+from selenium.webdriver.remote.remote_connection import LOGGER
+LOGGER.setLevel(logging.CRITICAL)
+
+
+logger.info("CRM Login")
+driver = webdriver.Edge()
+driver.get(dolibarr_url + "/index.php")
+assert "Identifiant" in driver.title
+
+
+driver.find_element(By.NAME,"username").send_keys(str(dolibarr_username))
+pass_field = driver.find_element(By.NAME,"password")
+pass_field.send_keys(str(dolibarr_password))
+pass_field.send_keys(Keys.RETURN)
+logger.info(driver.title)
+assert "Accueil" in driver.title
+
+logger.info("login successful")
+
+current_date = datetime.datetime.now()
+
+driver.get(dolibarr_url + "//compta/facture/list.php?leftmenu=customers_bills")
+
+
+# iterating over links63
+logger.info("iterating over invoice")
+invoices_table = driver.find_elements(By.XPATH,"//div[contains(@id,'contrat-lines-container')]/div")
+
+
+invoices_table = driver.find_element(By.CLASS_NAME,'liste')
+invoices_entries = invoices_table.find_elements(By.CLASS_NAME,"oddeven")
+
+class invoice_to_process : 
+    link   : str
+    name   : str
+    amount : float
+
+invoices_to_process = set()
+
+logger.info("checkin all invoices to found unpaid ones with amount 0...")
+for invoice_entry in invoices_entries :
+    
+    inv = invoice_to_process()
+    logger.debug("----------")
+    item = invoice_entry.find_element(By.CLASS_NAME,"classfortooltip")
+    inv.name = item.text
+    inv.link = item.get_attribute("href")
+
+    logger.debug("invoice name : " + inv.name)
+    logger.debug("invoice link : " + inv.link)
+
+    inv.amount = float(invoice_entry.find_element(By.CLASS_NAME,"amount").text.replace(',','.').replace(' ',''))
+    logger.debug("invoice amount : " + str(inv.amount))
+    status = invoice_entry.find_element(By.CLASS_NAME,"badge-status").text
+    logger.debug("status : " + str(status))
+    if str(status) == "Impayée" and inv.amount == 0.0:
+        logger.info("## An invoice is suitable for processing ! :  " + inv.name)
+        invoices_to_process.add(inv)
+
+    logger.debug("----------")
+
+if args.dry:
+    driver.close()
+    logger.info("dry run enabled, exiting here...")
+    exit(0)
+
+logger.info("processing target invoices...")
+invoice : invoice_to_process
+for invoice in invoices_to_process :
+    logger.info("processing invoice " + invoice.name + "...")
+    driver.get(invoice.link + "&action=paid")
+    buttons = driver.find_element(By.CLASS_NAME,"ui-dialog-buttonset")
+    logger.debug(buttons.get_attribute("innerHTML"))
+    buttons.find_element(By.XPATH,'button[contains(text(), "Oui")]').click()
+
+
+
+
+exit(0)
+
+
+

BIN
msedgedriver.exe


+ 6 - 0
references_example.conf

@@ -0,0 +1,6 @@
+[credentials]
+dolibarr_username = user
+dolibarr_password = pass
+#optionnal  : 
+contact_mail = contact@entity.com
+

+ 24 - 0
requirements.txt

@@ -0,0 +1,24 @@
+async-generator==1.10
+attrs==22.1.0
+certifi==2022.6.15
+cffi==1.15.1
+charset-normalizer==2.1.0
+dolibarr==0.1.20
+dolipy==0.1.2
+h11==0.13.0
+idna==3.3
+outcome==1.2.0
+pycparser==2.21
+pydantic==1.9.2
+PySocks==1.7.1
+python-dotenv==0.15.0
+requests==2.28.1
+selenium==4.4.3
+sniffio==1.2.0
+sortedcontainers==2.4.0
+stripe==4.1.0
+trio==0.21.0
+trio-websocket==0.9.2
+typing_extensions==4.3.0
+urllib3==1.26.11
+wsproto==1.1.0