Browse Source

support Free contract + pay all 0 invoices

ash 1 year ago
parent
commit
8136d7a180
3 changed files with 298 additions and 84 deletions
  1. 135 84
      main.py
  2. BIN
      msedgedriver.exe
  3. 163 0
      zero_invoice.py

+ 135 - 84
main.py

@@ -7,6 +7,9 @@ logger = logging.getLogger(logger_name)
 import requests,json
 import datetime
 import argparse
+import sys
+
+CONF_FILE_PATH = 'references.conf'
 
 
 init_logging()
@@ -51,13 +54,22 @@ logger.info(" --- --------------------------------------------------------------
 logger.info(" --- ---------------------------------- CONF PHASE ---------------------------------- ---")
 logger.info(" --- -------------------------------------------------------------------------------- ---")
 logger.info("Reading Reference ConfIguration File")
+
+#check if file exist
+from os.path import exists
+if not exists(CONF_FILE_PATH) :
+    logger.critical("'reference.conf' not found ! check if the file exist or you execute the script in the right folder. \
+                    \n To build a 'reference.conf' check the example in the folder.")
+    sys.exit(1)
+
 import configparser
 config = configparser.ConfigParser()
 config.optionxform = str # to make the read Case Sensitive 
-config.read('references.conf')
+config.read(CONF_FILE_PATH)
 
-references_dict = config["list"]
-stripe_api_key = config["credentials"]["stripe_api_key"]
+#readign structure of the file
+references_dict   = config["list"]
+stripe_api_key    = config["credentials"]["stripe_api_key"]
 dolibarr_url      = config["credentials"]["dolibarr_url"]
 dolibarr_username = config["credentials"]["dolibarr_username"]
 dolibarr_password = config["credentials"]["dolibarr_password"]
@@ -73,7 +85,7 @@ if config.has_option("credentials", "contact_mail") :
 
 logger.debug("contact mail in configuration is : " + contact_mail)
 
-logger.debug("Reading Stripe subscriptions references with CRM link to  subscription contract")
+logger.debug("Reading CRM contract reference with link to Stripe subscriptions references")
 for reference in  references_dict:
     logger.debug("link reference found : " + reference  + " -> " + references_dict[reference])
 
@@ -109,8 +121,10 @@ class crm_linked_invoice :
 
 class invoice_link :
 
-    stripe_subscription_reference : str
     contract_number               : int
+    stripe_subscription_reference : str
+    is_stripe_link                : bool = True
+    
 
     stripe_customer_ref    : str
     stripe_customer_name   : str
@@ -131,62 +145,68 @@ class invoice_link :
 invoice_links = set()
 
 
-for reference in  references_dict:
-
-    logger.info("retrieving Subscription data of " + reference)
-    url = "https://api.stripe.com/v1/subscriptions/" + reference
-    logger.debug("testing url : '" + url + "'")
-    r = requests.get(url, headers=headers)
- 
-    assert r.status_code == 200
-    subscription_json_data = json.loads(r.text)
-    logger.debug("Subscription Data found : ")
-    logger.debug(json.dumps(subscription_json_data, indent=4 , ensure_ascii=False))
-
-    if subscription_json_data["status"] != "active" :
-        logger.info("subscription not active, going to next")
-        continue
-
-    #retrieving customer data 
-    latest_invoice_reference = subscription_json_data["latest_invoice"]
-    logger.debug("latest invoice reference found : " + latest_invoice_reference)
-    url = "https://api.stripe.com/v1/invoices/" + latest_invoice_reference
-    r = requests.get(url, headers=headers)
-    assert r.status_code == 200
-
-    latest_invoice_json_data = json.loads(r.text)
-    logger.debug("latest invoice Data found : ")
-    logger.debug(json.dumps(latest_invoice_json_data, indent=4, ensure_ascii=False))
-
-    #TODO verfiy invoice status to "paid"
-
+for reference in references_dict:
+    
     link = invoice_link()
+    link.contract_number = reference
 
-
-    link.stripe_subscription_reference = reference
-    link.contract_number        = references_dict[reference]
-    link.stripe_paid_amount     = float(float(latest_invoice_json_data["amount_paid"]) / 100 )
-    link.stripe_epoch_date      = latest_invoice_json_data["created"]
-    link.stripe_customer_name   = latest_invoice_json_data["customer_name"]
-    link.stripe_customer_mail   = latest_invoice_json_data["customer_email"]
-    link.stripe_invoice_url     = latest_invoice_json_data["hosted_invoice_url"]
-    link.stripe_invoice_number  = latest_invoice_json_data["number"]
-
-
-    logger.info("latest invoice customer info : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
-    logger.info("latest invoice paid amount   : " + str(link.stripe_paid_amount) + latest_invoice_json_data["currency"])
-    logger.info("latest invoice payment date  : " + str(datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))))
-    logger.info("latest invoice url : " + link.stripe_invoice_url )
+    if references_dict[reference] != "0" :
+        logger.info("retrieving Subscription data for reference :  " + references_dict[reference] + " ")
+        url = "https://api.stripe.com/v1/subscriptions/" + references_dict[reference]
+        logger.debug("testing url : '" + url + "'")
+        r = requests.get(url, headers=headers)
+    
+        assert r.status_code == 200
+        subscription_json_data = json.loads(r.text)
+        logger.debug("Subscription Data found : ")
+        logger.debug(json.dumps(subscription_json_data, indent=4 , ensure_ascii=False))
+
+        if subscription_json_data["status"] != "active" :
+            logger.info("subscription not active, going to next")
+            continue
+
+        #retrieving customer data 
+        latest_invoice_reference = subscription_json_data["latest_invoice"]
+        logger.debug("latest invoice reference found : " + latest_invoice_reference)
+        url = "https://api.stripe.com/v1/invoices/" + latest_invoice_reference
+        r = requests.get(url, headers=headers)
+        assert r.status_code == 200
+
+        latest_invoice_json_data = json.loads(r.text)
+        logger.debug("latest invoice Data found : ")
+        logger.debug(json.dumps(latest_invoice_json_data, indent=4, ensure_ascii=False))
+        
+        link.stripe_subscription_reference = references_dict[reference]
+        link.stripe_paid_amount     = float(float(latest_invoice_json_data["amount_paid"]) / 100 )
+        link.stripe_epoch_date      = latest_invoice_json_data["created"]
+        link.stripe_customer_name   = latest_invoice_json_data["customer_name"]
+        link.stripe_customer_mail   = latest_invoice_json_data["customer_email"]
+        link.stripe_invoice_url     = latest_invoice_json_data["hosted_invoice_url"]
+        link.stripe_invoice_number  = latest_invoice_json_data["number"]
+
+
+        logger.info("latest invoice customer info : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
+        logger.info("latest invoice paid amount   : " + str(link.stripe_paid_amount) + latest_invoice_json_data["currency"])
+        logger.info("latest invoice payment date  : " + str(datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))))
+        logger.info("latest invoice url : " + link.stripe_invoice_url )
+        logger.info(" ----------- Subscription Data OK")
+        
+    elif references_dict[reference] == "0" :
+        logger.info("contract number : " + str(link.contract_number) + " is associated with no stripe subscriptions")
+        link.is_stripe_link = False
+        logger.info(" ----------- Free Contract OK")
+        
+    else :
+        logger.warning("unknown formatting for the contract : " + reference)
+        continue   
 
     # subscription reference - dolibarr contract reference - name - mail - amount - date of payment. 
 
 
-
-
     invoice_links.add(link)
 
 
-    logger.info(" ----------- Subscription Data OK")
+
     
 
 logger.info("Stripe Phase OK")
@@ -281,15 +301,15 @@ for link in invoice_links :
             current_invoice.unpaid = True
     
         logger.info("-----")
-        logger.info("CRM linked invoice for customer : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
+        if link.is_stripe_link :
+            logger.info("CRM linked invoice for customer : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
         logger.info("CRM linked invoice url : " + current_invoice.url)
         logger.info("CRM linked invoice date : " + str(current_invoice.amount))
         logger.info("CRM linked invoice amount : " + str(current_invoice.date))
         logger.info("CRM linked invoice unpaid ?  : " + str(current_invoice.unpaid))
         logger.info("-----")
     
-
-        stripe_date  = datetime.datetime.fromtimestamp(link.stripe_epoch_date)
+      
 
         #checking if new invoice is needed for the month
         if current_invoice.date.year == current_date.year :
@@ -297,19 +317,36 @@ for link in invoice_links :
                 logger.debug("invoice link does not need a new invoice for this month")
                 link.crm_needs_new_invoice = False #TODO check all month since last invoice
 
+
+        if link.is_stripe_link :
+            stripe_date  = datetime.datetime.fromtimestamp(link.stripe_epoch_date)
         #checking if invoice is eligible to update
-        if current_invoice.date.year == stripe_date.year :
-            if current_invoice.date.month == stripe_date.month :
-                if current_invoice.date.day <= stripe_date.day :
-
-                    if current_invoice.unpaid : 
-                        
-                        if link.stripe_paid_amount == current_invoice.amount :
-
-                            logger.info("Current crm invoice is unpaid, and corresponding to same month and amount as stripe payment")
-                            logger.info(" ## Target invoice FOUND !  ##")
-                            link.crm_needs_update = True
-                            link.crm_target_invoice = current_invoice
+            if current_invoice.date.year == stripe_date.year :
+                if current_invoice.date.month == stripe_date.month :
+                    if current_invoice.date.day <= stripe_date.day :
+
+                        if current_invoice.unpaid : 
+                            
+                            if link.stripe_paid_amount == current_invoice.amount :
+
+                                logger.info("Current crm invoice is unpaid, and corresponding to same month and amount as stripe payment")
+                                logger.info(" ## Target invoice FOUND !  ##")
+                                link.crm_needs_update = True
+                                link.crm_target_invoice = current_invoice
+        else :
+            #for free contract
+            if current_invoice.date.year == current_date.year :
+                if current_invoice.date.month == current_date.month :
+                    if current_date.day >= current_invoice.date.day :
+
+                        if current_invoice.unpaid : 
+                
+                            if current_invoice.amount == 0:
+
+                                logger.info("current crm invoice is unpaid for a 0 amount (Free Contract)")
+                                logger.info(" ## Target invoice FOUND !  ##")
+                                link.crm_needs_update = True
+                                link.crm_target_invoice = current_invoice
             
 
 
@@ -332,13 +369,17 @@ for link in invoice_links :
             action = True
             logger.info(" ## ------------------------------- ##")
             logger.info("@@ INVOICE UPDATE PENDING : ")
-            logger.info("stripe invoice customer info : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
-            logger.info("stripe invoice paid amount   : " + str(link.stripe_paid_amount) + " " + latest_invoice_json_data["currency"])
-            logger.info("stripe invoice payment date  : " + str(datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))))
+
             logger.info("CRM linked invoice url : " + link.crm_target_invoice.url)
             logger.info("CRM linked invoice date : " + str(link.crm_target_invoice.amount))
             logger.info("CRM linked invoice amount : " + str(link.crm_target_invoice.date))
             logger.info("CRM linked invoice unpaid ?  : " + str(link.crm_target_invoice.unpaid))
+            if link.is_stripe_link :
+                logger.info("stripe invoice customer info : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
+                logger.info("stripe invoice paid amount   : " + str(link.stripe_paid_amount) + " " + latest_invoice_json_data["currency"])
+                logger.info("stripe invoice payment date  : " + str(datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))))
+            else :
+                logger.info("Invoice is from FREE CONTRACT")
             logger.info(" ## ------------------------------- ##")
         
         if link.crm_needs_new_invoice :
@@ -371,27 +412,37 @@ for link in invoice_links :
         if link.crm_needs_update :
 
             logger.info(" ## ------ Creating Payment ------ ##")
-            logger.info("stripe invoice customer info : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
-            logger.info("stripe invoice paid amount   : " + str(link.stripe_paid_amount) + " " + latest_invoice_json_data["currency"])
-            logger.info("stripe invoice payment date  : " + str(datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))))
             logger.info("CRM linked invoice url : " + link.crm_target_invoice.url)
             logger.info("CRM linked invoice date : " + str(link.crm_target_invoice.amount))
             logger.info("CRM linked invoice amount : " + str(link.crm_target_invoice.date))
             logger.info("CRM linked invoice unpaid ?  : " + str(link.crm_target_invoice.unpaid))
+            if link.is_stripe_link :
+                logger.info("stripe invoice customer info : " + link.stripe_customer_name + " - " + link.stripe_customer_mail)
+                logger.info("stripe invoice paid amount   : " + str(link.stripe_paid_amount) + " " + latest_invoice_json_data["currency"])
+                logger.info("stripe invoice payment date  : " + str(datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))))
+            else :
+                logger.info("Invoice is from FREE CONTRACT")
 
  
 
-            driver.get(link.crm_target_invoice.url)
-            pay_url = driver.find_element(By.XPATH, "//*[text()='Saisir règlement']").get_attribute("href")
-            driver.get(pay_url)
-            stripe_payment_date = datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))
-            date_string = format(stripe_payment_date,'{%d/%m/%Y}')
-            driver.find_element(By.ID, "re").send_keys(date_string)
-            driver.find_element(By.NAME, "comment").send_keys("Payment treated by automation - Slash-DoliStripe \n stripe invoice URL : " + link.stripe_invoice_url )
-            driver.find_element(By.CLASS_NAME,'AutoFillAmout').click()
-            driver.find_element(By.NAME, "num_paiement").send_keys(link.stripe_invoice_number)
-            driver.find_element(By.XPATH,'//input[@value="Payer"]').click()
-            driver.find_element(By.XPATH,'//input[@value="Valider"]').click()
+            
+            if link.is_stripe_link :
+                driver.get(link.crm_target_invoice.url)
+                pay_url = driver.find_element(By.XPATH, "//*[text()='Saisir règlement']").get_attribute("href")
+                driver.get(pay_url)
+                stripe_payment_date = datetime.datetime.fromtimestamp(int(link.stripe_epoch_date))
+                date_string = format(stripe_payment_date,'{%d/%m/%Y}')
+                driver.find_element(By.ID, "re").send_keys(date_string)
+                driver.find_element(By.NAME, "comment").send_keys("Payment treated by automation - Slash-DoliStripe \n stripe invoice URL : " + link.stripe_invoice_url )
+                driver.find_element(By.CLASS_NAME,'AutoFillAmout').click()
+                driver.find_element(By.NAME, "num_paiement").send_keys(link.stripe_invoice_number)
+                driver.find_element(By.XPATH,'//input[@value="Payer"]').click()
+                driver.find_element(By.XPATH,'//input[@value="Valider"]').click()
+            else : 
+                driver.get(link.crm_target_invoice.url + "&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()
 
             if args.mail:
                 logger.info("sending email to client...")

BIN
msedgedriver.exe


+ 163 - 0
zero_invoice.py

@@ -0,0 +1,163 @@
+
+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)
+
+