Utilisation de Python pour l'automatisation d'un processus création de bons de commande

Tags :
  • POK
  • 2024-2025
  • temps 1
  • Python
Auteurs :
  • Charles Cook

Création d'un outils en Python permettant d'automatiser la création de bons de commande.

Aucun prérequis

Au cours de mon stage de césure en tant qu'assistant supply planner, une tâche redondante et chronophage était le passage des commande mensuelles. L'objectif de ce processus est de, à partir de l'état actuel des stocks, les commandes en cours et les minimum stock, créer un bon de commande par manufacture.

Tâches

Sprints

L'objectif de ce POK est la création automatisée de bon de commande par manufacture à partir de de l'état des stocks actuels, regroupant, par référence et taille, la quantité souhaitées, le prix unitaire ainsi que le prix du lot commandé, et la date de livraison souhaitée.

Sprint 1

Liste des taches que l'on pense faire. On coche si la tache est réalisée. A la fin du sprint on fait une petite étude post-mortem pour voir ce qui s'est passé et les ajustement à faire pour le prochain sprint, pok.

Sprint 2

Horodatage

Date Heures passées Indications
Vendredi 13/09 2H (Re)Découverte du langage Python
Lundi 16/09 2H (Re)Découverte du langage Python
Lundi 16/09 1H Création de la base de données fictive
Lundi 16/09 2H Début du code
Mardi 17/09 1H Suite code
Mercredi 18/09 2H 1ère version des feuilles propositions de commande
Fin du premier sprint
Jeudi 26/09 2H30 Finalisation des feuilles propositions de commande
Jeudi 03/10 2H Code de la partie génération des bons de commande
Samedi 05/10 2H Code de la partie génération des bons de commande
Jeudi 10/10 2H Code de la partie Historisation
Vendredi 11/10 1H Rédaction du rapport

Sommaire

Contexte :

Pour ce POK, nous nous placons dans le cas d'une entreprise qui vend des produits finis. Ces produits sont référencés par numéro de références et par taille. La fabrication des produits est effectuée par diverses manufactures. Lorsqu'elle passe une commande à une manufacture, l'entreprise conserve alors les quantités dans un stock central. Les marchés sont les filiales de l'entreprise à l'international, et ce sont elles qui sont en contact direct avec le client final. Afin de répondre à la demande client, une filiale passe une commande à l'entreprise, qui si elle a les quantités nécéssaires en stock centrale, répond directement à la demande. Sinon, l'entreprise passe alors une commande à la manufacture. Il y a donc 3 niveaux à identifier clairement pour comprendre le déroulement de ce POK :

Présentation de l'outil :

Cet outil à pour but de répondre à plusieurs objectifs utilisateur :

Point d'avancement au premier Sprint

Remise à niveau Python

Pour cette partie de remise à niveau, je me suis appuyé sur le site Automate Boring Stuff With Python

Travailler avec des feuilles Excel

Il est possible de travailler sur Python à partir de classeurs Excel. Pour cela, il faut importer la librairie openpyxl.

Ouvrir un classeur Excel

La fonction openpyxl.load_workbook() permet de charger un classeur Excel. Une fois que celui-ci est chargé, nous pouvons accéder à son contenu et le manipuler ou le modifier. Il est important de noter que si le fichier contient des formules, il est parfois préférable de lire les valeurs calculées au lieu de lire les formules entrées dans les cellules. Dans ce cas, on précise à l'aide de l'option data_only=True. Ainsi, pour ouvrir le fichier Exemple en utilisant les valeurs calculée, on rédige le code suivant :

import openpyxl

wb = openpyxl.load_workbook('Exemple.xsl',data_only=True)

De même, si l'on ne souhaite pas faire de modification sur le classeur Excel, il existe l'option read_only=True permettant de l'ouvrir en lecture seule, ce qui est plus rapide :

import openpyxl

wb = openpyxl.load_workbook('Exemple.xsl',read_only=True)
Manipuler les feuilles

Une fois le classeur chargé, il est possible d'accéder aux feuilles qui le composent :

import openpyxl

wb = openpyxl.load_workbook('Exemple.xsl',data_only=True)

# Obtenir les noms de feuilles composant le classeur
wb.sheetnames

# Accéder à une feuille par son nom 
ws = wb['NameSheet']

# Obtenir le nom d'une feuille comme un type *String*
Nom_feuille = ws.title
Manipuler les cellules

On peut séléctionner et obtenir la valuer d'une cellule grâce à ses coordonnées :

import openpyxl

wb = openpyxl.load_workbook('Exemple.xsl',data_only=True)

ws = wb['NameSheet']

# Accéder à la cellule A1
Cell = ws['A1']

# Obtenir la valeur, la colonne et la ligne de la cellule A1
Cell.value, Cell.column, Cell.row
Dimensionner un tableau

Il est possible d'obtenir les dimensions d'un tableau en utilisant les attribut max.rowet max.column qui renvoient respectivmement le numéro de la dernière ligne et de la dernière colonne.

Manipuler lignes et colonnes

Il est possible de séletionner des lignes ou des colonnes d'un coup :

import openpyxl

wb = openpyxl.load_workbook('Exemple.xsl',data_only=True)

ws = wb['NameSheet']

# Sélectionner toutes les cellules de A7 à B9
tuple(ws['A7':'B9'])
Créer un classeur Excel

Nous pouvons créer un nouveau classeur Excel avec la fonction openpyxl.Workbook(). Par défaut, le classeur Excel créé est vide et contient une seule feuille nommé Sheet.

import openpyxl

# Créer un nouveau classeur
wb = openpyxl.Workbook()

Pour renommer le classeur ou la feuille, il suffit d'utiliser les attributs vus précédemment.

Créer ou supprimer une feuille

On peut manipuler le nombre de feuilles dans un claseur en en créant ou en en suppimant. Ainsi :

import openpyxl

wb = open.pyxl.Workbook()

# Ajouter une nouvelle feuille intitulé "Base de données" en première position
wb.create_sheet(index=0,title='Base de données')

# Supprimer la feuille "Sheet"
del wb['Sheet']

Tous ces éléments nous seront utiles pour automatiser la création des bons de commande.

Création d'une base de données fictive

Order Review

L'objectif de ce POK est d'automatiser la création de bons de commande à partir de l'état actuel des stocks. Ainsi, pour simuler cet exercice il est nécessaire de créer une base de données fictive refletant le niveau de stock. Cette bas de donnée doit contenir les éléments suivants :

alt text

Lead Time

Les bons de commande contiennent également une date de livraison demandée. Cette date de livraison dépend du fournisseur, une seconde feuille dans le classeur recense donc Lead Time en fonction du fournisseur (en mois).

Calcul des quantités recommandées

Les quantités de commande recommandées sont calculés comme suit :

1ère version des feuilles de propositions de commande

Le code suivant permet de créer, à partir de la base de données évoquée précédemment, une feuille par manufacture listant l'ensemble des références nécéssitant une commande auprès de la manufacture, et donnant la quantité de commande recommandée pour chacune des références.

Création et mise en page des feuilles de proposition de commande

import openpyxl

wb = openpyxl.load_workbook('/Users/charlescook/Desktop/DO IT/BD POK 1.xlsx',data_only=True)

indice_max = wb['Order Review'].max_row

# Création des feuilles proposition pour chaque manufacture
wb.create_sheet(title='Proposition CHT')
wb.create_sheet(title='Proposition ELA')
wb.create_sheet(title='Proposition ESN')
wb.create_sheet(title='Proposition FLE')
wb.create_sheet(title='Proposition GOD')
wb.create_sheet(title='Proposition MAT')

# Introduction des variables
indice_CHT=2
indice_ELA=2
indice_ESN=2
indice_FLE=2
indice_GOD=2
indice_MAT=2

# Mise en place des en-têtes pour les feuilles propositions
for k in range(1,11):
    wb['Proposition CHT'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition ELA'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition ESN'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition FLE'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition GOD'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition MAT'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    
    wb['Proposition CHT'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition CHT'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition ELA'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition ELA'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition ESN'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition ESN'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition FLE'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition FLE'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition GOD'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition GOD'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition MAT'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition MAT'].cell(row=1,column=12).value='Quantité Corrigée'

Ce code permet de créer une feuille par manufacture et la création des en-têtes.

Calcul de la quantité de commande recommandée par référence

for i in range(2,indice_max+1):
    Physique_Stock = wb['Order Review'].cell(row=i,column=8).value
    Commande_Manuf = wb['Order Review'].cell(row=i,column=9).value
    Commande_Marche = wb['Order Review'].cell(row=i,column=10).value
    MS = wb['Order Review'].cell(row=i,column=7).value
    MOQ = wb['Order Review'].cell(row=i,column=6).value
    Manufacture = wb['Order Review'].cell(row=i,column=4).value


    Physique_Dispo = Physique_Stock + Commande_Manuf - Commande_Marche

    if MS>Physique_Dispo :
        R_Qty = MS - Physique_Dispo
        if R_Qty<MOQ :
            R_Qty = MOQ
    else :
        R_Qty = 0

R_Qty est la quantité de commande recommandée, qui est nulle si il n'y a pas besoin de commander des pièces, et qui est supérieure ou égale au MOQ (Minimum Order Quantity) si il y a un besoin.

Extraction des refs ayant un besoin et trie par manufacture

# Copie des refs à commander dans des feuilles séparées par manufacture    

    if R_Qty>0 and Manufacture == 'CHT':
        for j in range(1,11):
            wb['Proposition CHT'].cell(row=indice_CHT,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition CHT'].cell(row=indice_CHT,column=11).value=R_Qty
        indice_CHT=indice_CHT+1
    if R_Qty>0 and Manufacture == 'ELA':
        for j in range(1,11):
            wb['Proposition ELA'].cell(row=indice_ELA,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition ELA'].cell(row=indice_ELA,column=11).value=R_Qty
        indice_ELA=indice_ELA+1
    if R_Qty>0 and Manufacture == 'ESN':
        for j in range(1,11):
            wb['Proposition ESN'].cell(row=indice_ESN,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition ESN'].cell(row=indice_ESN,column=11).value=R_Qty
        indice_ESN=indice_ESN+1  
    if R_Qty>0 and Manufacture == 'FLE':
        for j in range(1,11):
            wb['Proposition FLE'].cell(row=indice_FLE,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition FLE'].cell(row=indice_FLE,column=11).value=R_Qty
        indice_FLE=indice_FLE+1
    if R_Qty>0 and Manufacture == 'GOD':
        for j in range(1,11):
            wb['Proposition GOD'].cell(row=indice_GOD,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition GOD'].cell(row=indice_GOD,column=11).value=R_Qty
        indice_GOD=indice_GOD+1
    if R_Qty>0 and Manufacture == 'MAT':
        for j in range(1,11):
            wb['Proposition MAT'].cell(row=indice_MAT,column=j).value=wb['Order Review'].cell(row=i,column=j).value 
            wb['Proposition MAT'].cell(row=indice_MAT,column=11).value=R_Qty
        indice_MAT=indice_MAT+1

wb.save('/Users/charlescook/Desktop/DO IT/fichier_modifié.xlsx') 

Ce code permet d'obtenir une feuille par manufacture qui permet de une order review par l'utilisateur chargé du processus de la commande mensuelle.

alt text

Pistes d'amélioration du code

Il est possible d'améliorer le code en mettant en place un code couleur en fonction de l'importance de la commande, si cette commande résulte d'un besoin marché (un besoin client), la ligne pourrait être en rouge afin de souligner son importance. C'est une piste à étudier en second sprint.

Point d'avancement au second Sprint

Ajout d'un code couleur

L'objectif de l'ajout du code couleur est de faciliter la relecture de la proposition automatique de commande par l'utilisateur. Ainsi, le code suivant permet de surligner en jaune les lignes qui concernent des commandes jugées "importantes" car elles répondent à un besoin marché (un besoin client). Les autres commandes sont passées afin que le stock central soit au niveau des MS, il n'y a pas d'attente client derrière celles-ci.

Code de l'ajout du code couleur

if Physique_Dispo<0 :
            for l in range(1,13):
                wb['Proposition CHT'].cell(row=indice_CHT,column=l).fill=PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")

Ce code vérifie pour chaque commande passée la valeur du Physique_Dispo. Si celui-ci est négatif, cela signifie que la somme du Stock physique dans le stok central et des commandes déjà passées à la manufacture est inférieure aux quantités passées par les marchés. Ainsi il est urgent de repasser une commande manufacture afin de répondre au besoin client. Dans ce cas, le code couleur "FFFF00" est attribué à la ligne, soit la couleur jaune. Le résultat obtenu est le suivant : alt text

Création du fichier des bon de commande finaux

Objectif

L'objectif de cette partie est d'avoir un fihcier Excel comprenant tous les bons de commande par manufacture. Ceux-ci doivent contenir les informations suivantes :

Code pour le bons de commande

# Création des bons de commande
for i in range(2,indice_CHT):
    # Prise en compte des valeurs corrigées par l'utilisateur
    if wb2['Proposition CHT'].cell(row=i,column=12).value is None :
        wb2['Proposition CHT'].cell(row=i,column=12).value=wb2['Proposition CHT'].cell(row=i,column=11).value

Cette partie copie les quantités de commande finales : la valeur proposée automatiquement s'il n'y a pas eu de modification faite par l'utilisateur, la valeur entrée manuellement par l'utilisateur si un changement a été effectué.

    # Calcul des prix de lot
    wb2['Proposition CHT'].cell(row=i,column=13).value = wb2['Proposition CHT'].cell(row=i,column=5).value * wb2['Proposition CHT'].cell(row=i,column=12).value

Cette partie calcule le prix de chaque lot en mulitpliant simplement la quantité finale de commande par le prix unitaire.

    # Caclul des dates demandées en fonction du lead time de la manufacture
    wb2['Proposition CHT'].cell(row=i,column=14).value = (datetime.now() + relativedelta(months=+3)).strftime('%d/%m/%Y')

Cette partie va chercher le lead time par manufacture dans la feuille Lead Time et calcul donc la date demandée en fonction de la date à laquelle les bons de commande sont édités.

Les colonnes restantes qui ne sont pas nécessaires dans le bon de commande sont ensuite supprimées, et un total par manufacture est calculé (un total en nombre de pièces et un total en terme financier).

Résultat des bons de commande

Le résultat obtenu en faisant tourner le code est donc le suivant.

alt text

Historisation des commandes mensuelles

Il peut être intéressant, au niveau de l'analyse et de la vérification avant d'envoyer le bon de commande, d'avoir une vision globale sur les quantités qui ont été commandées lors des précédentes commeandes mensuelles. Pour cela, nous allons tenter de mettre en place un suivi de l'hitorique sur l'année. Nous considererons que les commandes mensuelles de janvier à octobre ont été passées. La commande en cours est la commande mensuelle de novembre.

Cette partie historisation permet notamment de pouvoir comparer les quantités afin de détecter d'éventuelles erreur dans la saisie des commandes des marchés par exemple.

Création d'une feuille Suivi Historique Fournisseur

Afin de pouvoir suivre de manière précise l'historique des commandes mensuelles, nous avons créé une feuille Historique : alt text

La cellule P2 doit être modifiée de manière manuelle par l'utilisateur, afin de renseigner le commande mensuelle de quelle mois va être passée. Ainsi, cela permet au moment de la génération des bons de commande finaux d'avoir une simulation des quantités par rapport aux précédentes.

Synthèse des historiques dans la création des bons de commande

Au moment de la génération des bons de commande, le code suibvant permet d'entrer les quantités afin de compléter le tableau de suivi :

# Ajout Historique CHT
wb2['Suivi Historique Four'].cell(row=3,column=hst+1).value = total

On obtient ainsi le tableau complété suivant : alt text

Afin d'avoir une présentation plus visuelle de l'évolution des quantités de commande, le code suivant permet de présenter les données sous forme de graphique en barres, par manufacture :

# Graphe Historique CHT
refObj=openpyxl.chart.Reference(wb2['Suivi Historique Four'], min_col=2, min_row=3, max_col=13,max_row=3)
seriesObj = openpyxl.chart.Series(refObj, title='Qty commande')
chartObj = openpyxl.chart.BarChart()
chartObj.title = 'Historique CHT'
chartObj.append(seriesObj)
wb2['Suivi Historique Four'].add_chart(chartObj, 'B12')

Le résultat obtenu est le suivant : alt text

Pistes d'amélioration

Le code, dont la version complète se trouve ci-après, fonctionne. Cependant, de nombreuses lignes se répètent. Dans un objectif d'efficacité et de clareté dans la lecture de celui-ci, il serait intéressant de le simplifier en utilisant des boucles. En effet, chacune des étapes est répétée 6 fois pour chacune des manufactures. Or, si un changement doit être apporté dans le code, il peut être chronophage de répéter ce changement 6 fois à chaque fois. De plus, cela peut également permettre d'améliorer la rapidité d'exécution du code. Ainsi, la solution est de créer une liste avec les identifiants de chaque manufacture, et d'utiliser une boucle for pour répéter l'opération pour chacun des éléments de la liste. Cette technique permet également d'ajouter facilement de nouvelle manufacture.

J'ai tenté d'implémenter cette solution mais je n'y suis pas parvenu, des erreurs empêchaient l'execution du code

Code complet

Code pour la partie génération de la proposition des quantités de commande.

import openpyxl
from datetime import datetime
from dateutil.relativedelta import relativedelta
from openpyxl.styles import Font
from openpyxl.styles import PatternFill

wb = openpyxl.load_workbook('/Users/charlescook/Desktop/DO IT/BD POK 1.xlsx',data_only=True)

indice_max = wb['Order Review'].max_row

# Création des feuilles proposition pour chaque manufacture
wb.create_sheet(title='Proposition CHT')
wb.create_sheet(title='Proposition ELA')
wb.create_sheet(title='Proposition ESN')
wb.create_sheet(title='Proposition FLE')
wb.create_sheet(title='Proposition GOD')
wb.create_sheet(title='Proposition MAT')

# Introduction des variables
indice_CHT=2
indice_ELA=2
indice_ESN=2
indice_FLE=2
indice_GOD=2
indice_MAT=2


# Mise en place des en-têtes pour les feuilles propositions
for k in range(1,11):
    wb['Proposition CHT'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition ELA'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition ESN'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition FLE'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition GOD'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    wb['Proposition MAT'].cell(row=1,column=k).value=wb['Order Review'].cell(row=1,column=k).value
    
    wb['Proposition CHT'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition CHT'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition ELA'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition ELA'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition ESN'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition ESN'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition FLE'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition FLE'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition GOD'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition GOD'].cell(row=1,column=12).value='Quantité Corrigée'
    wb['Proposition MAT'].cell(row=1,column=11).value='Quantité Recommandée'
    wb['Proposition MAT'].cell(row=1,column=12).value='Quantité Corrigée'

for i in range(2,indice_max+1):
    Physique_Stock = wb['Order Review'].cell(row=i,column=8).value
    Commande_Manuf = wb['Order Review'].cell(row=i,column=9).value
    Commande_Marche = wb['Order Review'].cell(row=i,column=10).value
    MS = wb['Order Review'].cell(row=i,column=7).value
    MOQ = wb['Order Review'].cell(row=i,column=6).value
    Manufacture = wb['Order Review'].cell(row=i,column=4).value

    Physique_Dispo = Physique_Stock + Commande_Manuf - Commande_Marche

    if MS>Physique_Dispo :
        R_Qty = MS - Physique_Dispo
        if R_Qty<MOQ :
            R_Qty = MOQ
    else :
        R_Qty = 0
    
# Copie des refs à commander dans des feuilles séparées par manufacture    

    if R_Qty>0 and Manufacture == 'CHT':
        for j in range(1,11):
            wb['Proposition CHT'].cell(row=indice_CHT,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition CHT'].cell(row=indice_CHT,column=11).value=R_Qty
        if Physique_Dispo<0 :
            for l in range(1,13):
                wb['Proposition CHT'].cell(row=indice_CHT,column=l).fill=PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
        indice_CHT=indice_CHT+1
    
    if R_Qty>0 and Manufacture == 'ELA':
        for j in range(1,11):
            wb['Proposition ELA'].cell(row=indice_ELA,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition ELA'].cell(row=indice_ELA,column=11).value=R_Qty
        if Physique_Dispo<0 :
            for l in range(1,13):
                wb['Proposition ELA'].cell(row=indice_ELA,column=l).fill=PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
        indice_ELA=indice_ELA+1
    if R_Qty>0 and Manufacture == 'ESN':
        for j in range(1,11):
            wb['Proposition ESN'].cell(row=indice_ESN,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition ESN'].cell(row=indice_ESN,column=11).value=R_Qty
        if Physique_Dispo<0 :
            for l in range(1,13):
                wb['Proposition ESN'].cell(row=indice_ESN,column=l).fill=PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
        indice_ESN=indice_ESN+1  
    if R_Qty>0 and Manufacture == 'FLE':
        for j in range(1,11):
            wb['Proposition FLE'].cell(row=indice_FLE,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition FLE'].cell(row=indice_FLE,column=11).value=R_Qty
        if Physique_Dispo<0 :
            for l in range(1,13):
                wb['Proposition FLE'].cell(row=indice_FLE,column=l).fill=PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
        indice_FLE=indice_FLE+1
    if R_Qty>0 and Manufacture == 'GOD':
        for j in range(1,11):
            wb['Proposition GOD'].cell(row=indice_GOD,column=j).value=wb['Order Review'].cell(row=i,column=j).value
            wb['Proposition GOD'].cell(row=indice_GOD,column=11).value=R_Qty
        if Physique_Dispo<0 :
            for l in range(1,13):
                wb['Proposition GOD'].cell(row=indice_GOD,column=l).fill=PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
        indice_GOD=indice_GOD+1
    if R_Qty>0 and Manufacture == 'MAT':
        for j in range(1,11):
            wb['Proposition MAT'].cell(row=indice_MAT,column=j).value=wb['Order Review'].cell(row=i,column=j).value 
            wb['Proposition MAT'].cell(row=indice_MAT,column=11).value=R_Qty
        if Physique_Dispo<0 :
            for l in range(1,13):
                wb['Proposition MAT'].cell(row=indice_MAT,column=l).fill=PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
        indice_MAT=indice_MAT+1

wb.save('/Users/charlescook/Desktop/DO IT/proposition_commande.xlsx')       

Code pour la partie génération des bons de comman de finaux et mise à jour de l'historique.

wb2 = openpyxl.load_workbook('/Users/charlescook/Desktop/DO IT/proposition_commande.xlsx',data_only=True)

# Suivi de l'historique
hst=wb2['Suivi Historique Four'].cell(row=2,column=16).value

# Création des bons de commande
for i in range(2,indice_CHT):
    # Prise en compte des valeurs corrigées par l'utilisateur
    if wb2['Proposition CHT'].cell(row=i,column=12).value is None :
        wb2['Proposition CHT'].cell(row=i,column=12).value=wb2['Proposition CHT'].cell(row=i,column=11).value
    # Calcul des prix de lot
    wb2['Proposition CHT'].cell(row=i,column=13).value = wb2['Proposition CHT'].cell(row=i,column=5).value * wb2['Proposition CHT'].cell(row=i,column=12).value
    # Caclul des dates demandées en fonction du lead time de la manufacture
    wb2['Proposition CHT'].cell(row=i,column=14).value = (datetime.now() + relativedelta(months=+3)).strftime('%d/%m/%Y')
# Supression des colonnes 
wb2['Proposition CHT'].delete_cols(4,8)
wb2['Proposition CHT'].cell(row=1,column=4).value='Quantité'
wb2['Proposition CHT'].cell(row=1,column=5).value='Prix lot'
wb2['Proposition CHT'].cell(row=1,column=6).value='Date demandée'
wb2['Proposition CHT'].cell(row=indice_CHT+1,column=3).value='TOTAL'
total=0
total2=0
for j in range(2,indice_CHT):
    total+=wb2['Proposition CHT'].cell(row=j,column=4).value
    total2+=wb2['Proposition CHT'].cell(row=j,column=5).value
wb2['Proposition CHT'].cell(row=indice_CHT+1,column=4).value=total
wb2['Proposition CHT'].cell(row=indice_CHT+1,column=5).value=total2

# Ajout Historique CHT
wb2['Suivi Historique Four'].cell(row=3,column=hst+1).value = total

# Mise en page 
for j in range(1,7) :
    wb2['Proposition CHT'].cell(row=1,column=j).font = Font(bold=True)
    wb2['Proposition CHT'].cell(row=1,column=j).fill=PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
wb2['Proposition CHT'].title = 'BDC CHT'

# Répétition pour ELA
for i in range(2,indice_ELA):
    if wb2['Proposition ELA'].cell(row=i,column=12).value is None :
        wb2['Proposition ELA'].cell(row=i,column=12).value=wb2['Proposition ELA'].cell(row=i,column=11).value
    wb2['Proposition ELA'].cell(row=i,column=13).value = wb2['Proposition ELA'].cell(row=i,column=5).value * wb2['Proposition ELA'].cell(row=i,column=12).value
    wb2['Proposition ELA'].cell(row=i,column=14).value = (datetime.now() + relativedelta(months=+2)).strftime('%d/%m/%Y')
wb2['Proposition ELA'].delete_cols(4,8)
wb2['Proposition ELA'].cell(row=1,column=4).value='Quantité'
wb2['Proposition ELA'].cell(row=1,column=5).value='Prix lot'
wb2['Proposition ELA'].cell(row=1,column=6).value='Date demandée'
wb2['Proposition ELA'].cell(row=indice_ELA+1,column=3).value='TOTAL'
total=0
total2=0
for j in range(2,indice_ELA):
    total+=wb2['Proposition ELA'].cell(row=j,column=4).value
    total2+=wb2['Proposition ELA'].cell(row=j,column=5).value
wb2['Proposition ELA'].cell(row=indice_ELA+1,column=4).value=total
wb2['Proposition ELA'].cell(row=indice_ELA+1,column=5).value=total2

# Ajout Historique ELA
wb2['Suivi Historique Four'].cell(row=4,column=hst+1).value = total

for j in range(1,7) :
    wb2['Proposition ELA'].cell(row=1,column=j).font = Font(bold=True)
    wb2['Proposition ELA'].cell(row=1,column=j).fill=PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
wb2['Proposition ELA'].title = 'BDC ELA'

# Répétition pour ESN
for i in range(2,indice_ESN):
    if wb2['Proposition ESN'].cell(row=i,column=12).value is None :
        wb2['Proposition ESN'].cell(row=i,column=12).value=wb2['Proposition ESN'].cell(row=i,column=11).value
    wb2['Proposition ESN'].cell(row=i,column=13).value = wb2['Proposition ESN'].cell(row=i,column=5).value * wb2['Proposition ESN'].cell(row=i,column=12).value
    wb2['Proposition ESN'].cell(row=i,column=14).value = (datetime.now() + relativedelta(months=+2)).strftime('%d/%m/%Y')
wb2['Proposition ESN'].delete_cols(4,8)
wb2['Proposition ESN'].cell(row=1,column=4).value='Quantité'
wb2['Proposition ESN'].cell(row=1,column=5).value='Prix lot'
wb2['Proposition ESN'].cell(row=1,column=6).value='Date demandée'
wb2['Proposition ESN'].cell(row=indice_ESN+1,column=3).value='TOTAL'
total=0
total2=0
for j in range(2,indice_ESN):
    total+=wb2['Proposition ESN'].cell(row=j,column=4).value
    total2+=wb2['Proposition ESN'].cell(row=j,column=5).value
wb2['Proposition ESN'].cell(row=indice_ESN+1,column=4).value=total
wb2['Proposition ESN'].cell(row=indice_ESN+1,column=5).value=total2

# Ajout Historique ESN
wb2['Suivi Historique Four'].cell(row=5,column=hst+1).value = total

for j in range(1,7) :
    wb2['Proposition ESN'].cell(row=1,column=j).font = Font(bold=True)
    wb2['Proposition ESN'].cell(row=1,column=j).fill=PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
wb2['Proposition ESN'].title = 'BDC ESN'

# Répétition pour FLE
for i in range(2,indice_FLE):
    if wb2['Proposition FLE'].cell(row=i,column=12).value is None :
        wb2['Proposition FLE'].cell(row=i,column=12).value=wb2['Proposition FLE'].cell(row=i,column=11).value
    wb2['Proposition FLE'].cell(row=i,column=13).value = wb2['Proposition FLE'].cell(row=i,column=5).value * wb2['Proposition FLE'].cell(row=i,column=12).value
    wb2['Proposition FLE'].cell(row=i,column=14).value = (datetime.now() + relativedelta(months=+2)).strftime('%d/%m/%Y')
wb2['Proposition FLE'].delete_cols(4,8)
wb2['Proposition FLE'].cell(row=1,column=4).value='Quantité'
wb2['Proposition FLE'].cell(row=1,column=5).value='Prix lot'
wb2['Proposition FLE'].cell(row=1,column=6).value='Date demandée'
wb2['Proposition FLE'].cell(row=indice_FLE+1,column=3).value='TOTAL'
total=0
total2=0
for j in range(2,indice_FLE):
    total+=wb2['Proposition FLE'].cell(row=j,column=4).value
    total2+=wb2['Proposition FLE'].cell(row=j,column=5).value
wb2['Proposition FLE'].cell(row=indice_FLE+1,column=4).value=total
wb2['Proposition FLE'].cell(row=indice_FLE+1,column=5).value=total2

# Ajout Historique FLE
wb2['Suivi Historique Four'].cell(row=6,column=hst+1).value = total

for j in range(1,7) :
    wb2['Proposition FLE'].cell(row=1,column=j).font = Font(bold=True)
    wb2['Proposition FLE'].cell(row=1,column=j).fill=PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
wb2['Proposition FLE'].title = 'BDC FLE'

# Répétition pour GOD
for i in range(2,indice_GOD):
    if wb2['Proposition GOD'].cell(row=i,column=12).value is None :
        wb2['Proposition GOD'].cell(row=i,column=12).value=wb2['Proposition GOD'].cell(row=i,column=11).value
    wb2['Proposition GOD'].cell(row=i,column=13).value = wb2['Proposition GOD'].cell(row=i,column=5).value * wb2['Proposition GOD'].cell(row=i,column=12).value
    wb2['Proposition GOD'].cell(row=i,column=14).value = (datetime.now() + relativedelta(months=+1)).strftime('%d/%m/%Y')
wb2['Proposition GOD'].delete_cols(4,8)
wb2['Proposition GOD'].cell(row=1,column=4).value='Quantité'
wb2['Proposition GOD'].cell(row=1,column=5).value='Prix lot'
wb2['Proposition GOD'].cell(row=1,column=6).value='Date demandée'
wb2['Proposition GOD'].cell(row=indice_GOD+1,column=3).value='TOTAL'
total=0
total2=0
for j in range(2,indice_GOD):
    total+=wb2['Proposition GOD'].cell(row=j,column=4).value
    total2+=wb2['Proposition GOD'].cell(row=j,column=5).value
wb2['Proposition GOD'].cell(row=indice_GOD+1,column=4).value=total
wb2['Proposition GOD'].cell(row=indice_GOD+1,column=5).value=total2

# Ajout Historique GOD
wb2['Suivi Historique Four'].cell(row=7,column=hst+1).value = total

for j in range(1,7) :
    wb2['Proposition GOD'].cell(row=1,column=j).font = Font(bold=True)
    wb2['Proposition GOD'].cell(row=1,column=j).fill=PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
wb2['Proposition GOD'].title = 'BDC GOD'

# Répétition pour MAT
for i in range(2,indice_MAT):
    if wb2['Proposition MAT'].cell(row=i,column=12).value is None :
        wb2['Proposition MAT'].cell(row=i,column=12).value=wb2['Proposition MAT'].cell(row=i,column=11).value
    wb2['Proposition MAT'].cell(row=i,column=13).value = wb2['Proposition MAT'].cell(row=i,column=5).value * wb2['Proposition MAT'].cell(row=i,column=12).value
    wb2['Proposition MAT'].cell(row=i,column=14).value = (datetime.now() + relativedelta(months=+2)).strftime('%d/%m/%Y')
wb2['Proposition MAT'].delete_cols(4,8)
wb2['Proposition MAT'].cell(row=1,column=4).value='Quantité'
wb2['Proposition MAT'].cell(row=1,column=5).value='Prix lot'
wb2['Proposition MAT'].cell(row=1,column=6).value='Date demandée'
wb2['Proposition MAT'].cell(row=indice_MAT+1,column=3).value='TOTAL'
total=0
total2=0
for j in range(2,indice_MAT):
    total+=wb2['Proposition MAT'].cell(row=j,column=4).value
    total2+=wb2['Proposition MAT'].cell(row=j,column=5).value
wb2['Proposition MAT'].cell(row=indice_MAT+1,column=4).value=total
wb2['Proposition MAT'].cell(row=indice_MAT+1,column=5).value=total2

# Ajout Historique MAT
wb2['Suivi Historique Four'].cell(row=8,column=hst+1).value = total

for j in range(1,7) :
    wb2['Proposition MAT'].cell(row=1,column=j).font = Font(bold=True)
    wb2['Proposition MAT'].cell(row=1,column=j).fill=PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
wb2['Proposition MAT'].title = 'BDC MAT'

#Supression des feuilles n'étant pas des bons de commande
del wb2['Order Review']
del wb2['Lead Time']

# Suivi de l'historique

# Graphe Historique CHT
refObj=openpyxl.chart.Reference(wb2['Suivi Historique Four'], min_col=2, min_row=3, max_col=13,max_row=3)
seriesObj = openpyxl.chart.Series(refObj, title='Qty commande')
chartObj = openpyxl.chart.BarChart()
chartObj.title = 'Historique CHT'
chartObj.append(seriesObj)
wb2['Suivi Historique Four'].add_chart(chartObj, 'B12')

# Graphe Historique ELA
refObj=openpyxl.chart.Reference(wb2['Suivi Historique Four'], min_col=2, min_row=4, max_col=13,max_row=4)
seriesObj = openpyxl.chart.Series(refObj, title='Qty commande')
chartObj = openpyxl.chart.BarChart()
chartObj.title = 'Historique ELA'
chartObj.append(seriesObj)
wb2['Suivi Historique Four'].add_chart(chartObj, 'J12')

# Graphe Historique ESN
refObj=openpyxl.chart.Reference(wb2['Suivi Historique Four'], min_col=2, min_row=5, max_col=13,max_row=5)
seriesObj = openpyxl.chart.Series(refObj, title='Qty commande')
chartObj = openpyxl.chart.BarChart()
chartObj.title = 'Historique ESN'
chartObj.append(seriesObj)
wb2['Suivi Historique Four'].add_chart(chartObj, 'B27')

# Graphe Historique FLE
refObj=openpyxl.chart.Reference(wb2['Suivi Historique Four'], min_col=2, min_row=6, max_col=13,max_row=6)
seriesObj = openpyxl.chart.Series(refObj, title='Qty commande')
chartObj = openpyxl.chart.BarChart()
chartObj.title = 'Historique FLE'
chartObj.append(seriesObj)
wb2['Suivi Historique Four'].add_chart(chartObj, 'J27')

# Graphe Historique GOD
refObj=openpyxl.chart.Reference(wb2['Suivi Historique Four'], min_col=2, min_row=7, max_col=13,max_row=7)
seriesObj = openpyxl.chart.Series(refObj, title='Qty commande')
chartObj = openpyxl.chart.BarChart()
chartObj.title = 'Historique GOD'
chartObj.append(seriesObj)
wb2['Suivi Historique Four'].add_chart(chartObj, 'B42')

# Graphe Historique MAT
refObj=openpyxl.chart.Reference(wb2['Suivi Historique Four'], min_col=2, min_row=7, max_col=13,max_row=7)
seriesObj = openpyxl.chart.Series(refObj, title='Qty commande')
chartObj = openpyxl.chart.BarChart()
chartObj.title = 'Historique MAT'
chartObj.append(seriesObj)
wb2['Suivi Historique Four'].add_chart(chartObj, 'J42')

wb2.save('/Users/charlescook/Desktop/DO IT/bon_de_commande.xlsx')