Se connecter avec
S'enregistrer | Connectez-vous

Ne copier/coller que certaines colonnes selon une condition

Dernière réponse : dans Programmation

Bonjour à toute la communauté,

Je viens vers vous car je souhaite avoir des précisions pour améliorer ce code que je vous propose.

  1. Option Explicit
  2.  
  3. Sub linecopy(ByVal line As Range, ByRef Target As Range, Optional clear As Boolean) '// nouvelle variable
  4.  
  5. Dim ws_prix_m As Worksheet
  6.  
  7. Set ws_prix_m = Worksheets("Prix matière")
  8.  
  9. line.EntireRow.Copy Destination:=Target
  10. Set Target = Target.Offset(1)
  11.  
  12. End Sub
  13.  
  14.  
  15. Sub Devis()
  16.  
  17. Dim ws_prix_m As Worksheet
  18. Dim ws_devis As Worksheet
  19. Dim plage_destination As Range
  20. Dim i As Long
  21. Dim j As Long
  22.  
  23. Set ws_prix_m = Worksheets("Prix matière")
  24. Set plage_destination = Worksheets("Devis").Rows(1) 'Feuille vierge 1ère ligne
  25. Set ws_devis = Worksheets("Devis")
  26.  
  27. For i = 3 To 23
  28. For j = 7 To 8
  29. If ws_prix_m.Cells(i, j).Value <> 0 Then
  30. linecopy ws_prix_m.Rows(i), plage_destination, True
  31. End If
  32.  
  33. Next
  34. Next
  35.  
  36. End Sub


1- Ce code est clairement à optimiser mais avant de finaliser ma base de données, je souhaite déjà savoir ce qu'il est possible de réaliser. C'est pour cela que la macro est appliquée jusqu'à la ligne 23.

2- Je pense avoir déclaré dés variables inutilement mais je ne parviens pas à rédiger la fonction If (ligne29) autrement. Ce que je souhaiterais c'est sélectionner la ligne à partir du moment où la cellule G ou H est différente de 0.

3- Pour l'instant, les lignes sont copiées sur une nouvelle feuille dès la 1ère ligne. Par la suite, je souhaiterais que les lignes se copient dans un document existant.
Comment indiquer en macro la ligne à partir de laquelle il peut copier ? Dans mon cas, je souhaiterais qu'il commence à copier en dessous d'une ligne qui contient le mot "MATERIEL" en colonne A.
Comment intégrer le nombre de lignes correspondant au nombre de lignes sélectionnées ?

4- Le tableau dans lequel je souhaite copier ces lignes a une structure différente. Est-il possible de sélectionner uniquement certaines colonnes selon la condition et de les coller dans un ordre différent ?

Merci d'avance pour votre aide à tous.


GTmacrodeb.
Lassé par la pub ? Créez un compte
Expert Programmation

Salut,

Tiens le code de linecopy() me dit quelque chose. Mais on en a enlevé, et on en a ajouté.
Dis-moi à quoi servent les lignes 5 et 7 ! A rien ? Vire-les.
Et ce paramètre clear de la fonction linecopy() ? Vestige d'un autre topic... Vire-le aussi.
Quel intérêt de la ligne 25 ? Tu ne t'en resers jamais. Vire-la.

Tu ne copies plus des lignes, alors ne te sers plus de linecopy().

Ca me paraît bizarre ce For j. Si pour une ligne la colonne G et la colonne H contiennent des valeurs non nulles, la ligne est copiée deux fois.
J'aurais tendance à dire "Si pour une ligne la colonne G ou la colonne H contiennent une valeur non nulle, copier la ligne".
A toi de voir.

Ca donne pour l'instant :
  1. Option Explicit
  1. Sub Devis()
  2.  
  3. Dim ws_source As Worksheet
  4. Dim cel_cible As Range
  5. Dim i As Long
  6.  
  7. Set ws_source = Worksheets("Prix matière" )
  8. Set cel_cible = Worksheets("Devis" ).Rows(1) 'Feuille vierge 1ère ligne
  9.  
  10. For i = 3 To 23
  11. If ws_source.Cells(i, 7).Value <> 0 Or _
  12. ws_source.Cells(i, 8).Value <> 0 Then
  13. ws_source.Rows(i).Copy Destination:=cel_cible
  14. Set cel_cible = cel_cible.Offset(1)
  15. End If
  16. Next
  17.  
  18. End Sub


Pour clarifier encore plus les choses, je vais directement traiter la zone G3:H23 :
  1. Sub Devis()
  2.  
  3. Dim cel_source As Range
  4. Dim cel_cible As Range
  5.  
  6. Set cel_cible = Worksheets("Devis" ).Rows(1) 'Feuille vierge 1ère ligne
  7.  
  8. For Each cel_source In Worksheets("Prix matière" ).Range("G3:G23")
  9. If cel_source.Value <> 0 Or cel_source.Offset(0, 1).Value <> 0 Then
  10. cel_source.EntireRow.Copy Destination:=cel_cible
  11. Set cel_cible = cel_cible.Offset(1)
  12. End If
  13. Next
  14.  
  15. End Sub
Etudie bien la fonction Offset().

Bon, maintenant, ce n'est pas toute la ligne (EntireRow) qu'on veut copier. Ben il va falloir le faire cellule par cellule.
  1. For Each cel_source In Worksheets("Prix matière" ).Range("G3:G23")
  2. If cel_source.Value <> 0 Or cel_source.Offset(0, 1).Value <> 0 Then
  3. cel_cible.Offset(, x1).Value = cel_source.Offset(, y1).Value
  4. cel_cible.Offset(, x2).Value = cel_source.Offset(, y2).Value
  5. cel_cible.Offset(, x3).Value = cel_source.Offset(, y3).Value
  6. cel_cible.Offset(, x4).Value = cel_source.Offset(, y4).Value
  7. cel_cible.Offset(, x5).Value = cel_source.Offset(, y5).Value
  8.  
  9. Set cel_cible = cel_cible.Offset(1)
  10. End If
  11. Next


Il reste maintenant à trouver MATERIEL dans Devis et donc à remplacer la ligne
  1. Set cel_cible = Worksheets("Devis" ).Rows(1)

Merci pour tes précieux conseils !

Alors j'ai commencé à adapter le code que tu m'as proposé après avoir bien pris connaissance de la fonction Offset.

Donc pour le report des colonnes cela fonctionne par contre le souci, c'est que je souhaiterai que les lignes soient insérées en dessous la ligne cible alors que pour le moment il copie par dessus les lignes existantes...

Pour l'instant, je n'ai pas encore trouvé le moyen de lui faire identifier la cellule contenant le mot "MATERIEL" comme celulle cible. Dois-je utiliser une fonction .Find ou bien une formule du type value = "MATERIEL" ? J'ai essayé cette dernière solution mais ça n'a pas fonctionné.

Merci encore.
Expert Programmation

M'enfin, qu'est-ce que tu racontes ?
Il n'y a pas d'insertion de lignes ! Il y a copie de lignes dans une feuille vierge.
La cible est initialisée comme cela :
  1. Set cel_cible = Worksheets("Devis" ).Rows(1) 'Feuille vierge 1ère ligne


Si ta feuille n'est pas vierge, il faut identifier la dernière ligne non-vide de ta feuille, et prendre la suivante. Je te laisse chercher un peu.

-----------------------------

Comment ça, tu as essayé mais ça n'a pas fonctionné. Donne le code essayé.
Find() devrait donner de bon résultat.

Sinon, tu peux aussi te servir d'Excel : nomme une cellule et utilise ce nom :
  1. worksheets(x).Range("nom_de_la_cellule")

Il y a eu un souci de compréhension, je me suis mal expliqué.

Dans mon premier message, j'avais bien précisé que pour l'instant je faisais le test sur un document vierge à partir de la 1ère ligne mais que dans un futur plus ou moins proche, je souhaitais faire la copie à partir d'une cellule donnée (celle qui contient le mot MATERIEL).

C'est pourquoi j'avais précisé que je souhaitais intégrer (j'aurais dû dire "insérer") le nombre de lignes correspondantes (à la condition).

Car en dessous des lignes insérées, j'ai de nouveau des cellules "fixes".

Pour repréciser mon besoin :
Je travaille à partir d'une feuille excel qui liste différents matériels pour effectuer des devis.
Lorsque je sélectionne des matériels à partir des colonnes G ou H, je souhaite que ces matériels sélectionnés soient copiés dans la feuille devis à partir de la ligne matériel.

Sur la feuille "Devis", j'ai donc : la ligne Matériel (qui sert de référence), en dessous une ligne vierge dans laquelle certaines cellules comprennement des formules et en dessous une ligne pour calculer des totaux.

En dessous ces lignes, j'ai encore d'autres éléments mais complétés manuellement.

Je souhaite donc que ma les lignes se copient à partir de la ligne vierge en dessous matériel et également que lorsqu'une ligne est insérée que les formules soient conservées.

J'espère avoir été clair au niveau de mes explications.

Je poste mon code avec .find par la suite.

Merci

Donc voici le code actuellement et qui fonctionne.

  1. Sub Devis()
  2.  
  3. Dim cel_source As Range
  4. Dim cel_cible As Range
  5.  
  6. Set cel_cible = Worksheets("Devis").Range("B:B").Find("MATERIEL", searchdirection:=xlPrevious)
  7.  
  8. For Each cel_source In Worksheets("Prix matière").Range("I3:I52")
  9. If cel_source.Value <> 0 Or cel_source.Offset(0, 1).Value <> 0 Then
  10. cel_cible.Offset(1, 0).Value = cel_source.Offset(, -6).Value
  11. cel_cible.Offset(1, 1).Value = cel_source.Offset(, -2).Value
  12. cel_cible.Offset(1, 2).Value = cel_source.Offset(, -1).Value
  13. cel_cible.Offset(1, 3).Value = cel_source.Offset(, 0).Value
  14. cel_cible.Offset(1, 4).Value = cel_source.Offset(, 1).Value
  15. cel_cible.Offset(1, 5).Value = cel_source.Offset(, 4).Value
  16. cel_cible.Offset(1, 9).Value = cel_source.Offset(, 6).Value
  17.  
  18. Set cel_cible = cel_cible.Offset(1)
  19.  
  20. End If
  21.  
  22. Next
  23.  
  24. End Sub


Les cellules se copient aux endroits désirés, le seul souci comme je l'ai précisé dans mon message précédent, c'est que ce tableau n'est pas vierge. Il faut donc que je parvienne à insérer les lignes et non les copier directement.
Expert Programmation

Ahhhhhhhh :) 

Juste un petit truc : juste après la ligne 6, vérifie que quelque chose a bien été trouvé. On ne sait jamais :
  1. If cel_cible Is Nothing Then
  2. MsgBox "Impossible de trouvé MATERIEL.", vbCritical Or vbOKOnly
  3. Exit Sub
  4. End If


Encore un petit truc, encore plus petit. La ligne 18 est mal indentée.

Tiens, c'est marrant, j'aurais mis la ligne 18 avant la ligne 10, pour ne pas avoir à faire trop de décalages.

-------------------------

Bon, maintenant, comment insérer une ligne. Alors je serais tenté de te proposer de demander à l'enregistreur de macro. Qui alors te dirait
  1. Rows("8:8").Select
  2. Selection.Insert Shift:=xlDown
Beurk. A traduire en :
  1. Rows(8).Insert Shift:=xlDown


La ligne 8, c'est un exemple. A la place, on voudrait bien la ligne après MATERIEL. Ca donne :
  1. Dim cel_source As Range
  2. Dim cel_cible As Range
  3.  
  4. Set cel_cible = Worksheets("Devis" ).Columns(2).Find("MATERIEL", searchdirection:=xlPrevious)
  5. If cel_cible Is Nothing Then
  6. MsgBox "Impossible de trouvé MATERIEL.", vbCritical Or vbOKOnly
  7. Exit Sub
  8. End If
  9.  
  10. For Each cel_source In Worksheets("Prix matière" ).Range("I3:I52" )
  11. If cel_source.Value <> 0 Or _
  12. cel_source.Offset(, 1).Value <> 0 _
  13. Then
  14. Set cel_cible = cel_cible.Offset(1)
  15. cel_cible.EntireRow.Insert Shift:=xlDown
  16.  
  17. cel_cible.Offset(, 0).Value = cel_source.Offset(, -6).Value
  18. cel_cible.Offset(, 1).Value = cel_source.Offset(, -2).Value
  19. cel_cible.Offset(, 2).Value = cel_source.Offset(, -1).Value
  20. cel_cible.Offset(, 3).Value = cel_source.Offset(, 0).Value
  21. cel_cible.Offset(, 4).Value = cel_source.Offset(, 1).Value
  22. cel_cible.Offset(, 5).Value = cel_source.Offset(, 4).Value
  23. cel_cible.Offset(, 9).Value = cel_source.Offset(, 6).Value
  24.  
  25. End If
  26. Next

Encore merci pour les conseils. Voici le code :

  1. Sub Devis()
  2.  
  3. Dim cel_source As Range
  4. Dim cel_cible As Range
  5.  
  6. Set cel_cible = Worksheets("Devis").Columns(2).Find("MATERIEL", searchdirection:=xlPrevious)
  7.  
  8. If cel_cible Is Nothing Then
  9. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  10. Exit Sub
  11. End If
  12.  
  13. For Each cel_source In Worksheets("Prix matière").Range("I3:I122")
  14. If cel_source.Value <> 0 Or cel_source.Offset(, 1).Value <> 0 Then
  15. Set cel_cible = cel_cible.Offset(1)
  16. cel_cible.Offset(1).EntireRow.Insert Shift:=xlDown
  17. cel_cible.Range("H:J", "L:N").Select
  18. Selection.AutoFill Destination:=cel_cible.Offset(1) 'je ne parviens pas à désigner la ligne qui a été ajoutée
  19.  
  20. cel_cible.Offset(, 0).Value = cel_source.Offset(, -6).Value
  21. cel_cible.Offset(, 1).Value = cel_source.Offset(, -2).Value
  22. cel_cible.Offset(, 2).Value = cel_source.Offset(, -1).Value
  23. cel_cible.Offset(, 3).Value = cel_source.Offset(, 0).Value
  24. cel_cible.Offset(, 4).Value = cel_source.Offset(, 1).Value
  25. cel_cible.Offset(, 5).Value = cel_source.Offset(, 4).Value
  26. cel_cible.Offset(, 9).Value = cel_source.Offset(, 6).Value
  27.  
  28. End If
  29.  
  30. Next
  31.  
  32. End Sub


Jai modifié la ligne 16, car je souhaitais que les lignes soient insérées en dessous la ligne vierge plutôt qu'en dessous de la ligne MATERIEL.

Par contre, j'ai un souci pour incrémenter la ligne insérée. Comme je l'ai noté dans le code, je n'arrive pas à désigner cette ligne insérée. Faut-il déclarer une variable ?

Le message d'erreur est le suivant pour la ligne 18 : "Erreur d'exécution '1004': La méthode AutoFill de la classe Range a échoué."

Merci d'avance.
Expert Programmation

Arggghhhhh x_X :fou:  :fou:  :fou: 
Je ne veux pas voir de Truc.Select/Selection.Machin() dans le code des gens que j'aide !!!!! Merci de les remplacer par des Truc.Machin(). Picétou (c)PL

  1. 'je ne parviens pas à désigner la ligne qui a été ajoutée
Euh, il faudrait que je teste mes codes avant que je te les propose :whistle: 
  1. MsgBox cel_cible.Address
  2. cel_cible.EntireRow.Insert
  3. MsgBox cel_cible.Address
La cellule cible ne bouge pas. Donc la nouvelle ligne est un cran en dessous, soit cel_cible.Offset(1). Evidemment, dans ton code, on se décale encore d'une ligne. Donc :
  1. cel_cible.Offset(1).EntireRow.Insert Shift:=xlDown
  2. Set newline = cel_cible.Offset(2).EntireRow


  1. Range("H:J", "L:N" )
Non, non et non. Il faudrait écrire
  1. Range("H:J,L:N" )
Mais ça ne peut pas marcher.

Pour créer ta zone, plusieurs solutions :
  1. ' // Soluce 1 - Beurk
  2. Set MaZone = Range("H" & cel_cible.Row & ":J" & cel_cible.Row & ",L" & cel_cible.Row & ":N" & cel_cible.Row )
  3.  
  4. ' // Soluce 2 - Pour 2 ou 3, ça va encore... Pour 6, à la rigueur...
  5. Set MaZone = Union(cel_cible.Offset(,6), cel_cible.Offset(,7), cel_cible.Offset(,8), cel_cible.Offset(,10), cel_cible.Offset(,11), cel_cible.Offset(,12))
  6.  
  7. ' // Soluce 3 - Ma préférée
  8. Set Les6Colonnes = Worksheets("ma_feuille" ).Range("H:J,L:N")
  9. Set MaZone = Intersect(cel_cible.EntireRow, Les6Colonnes)
  10.  
  11. ' // On peut aussi écrire directement comme ça :
  12. Set MaZone = Intersect(cel_cible.EntireRow, cel_cible.Worksheet.Range("H:J,L:N"))


Pour ta mise au point, n'hésite pas à ajouter des MsgBox ma_cellule.Address partout pour savoir où tu en es ;) 
Si les $ te perturbent, utilise ma_cellule.Address(False, False).


A te lire.....
Je sens qu'on est proche du dénouement.

Je sens également qu'on est plus proche de la fin que du début malheureusement, il reste encore un bout de chemin à parcourir.

Voici le nouveau code :

  1. Option Explicit
  2.  
  3. Sub Devis()
  4.  
  5. Dim cel_source As Range
  6. Dim cel_cible As Range
  7. Dim newline As Range
  8. Dim Les6Colonnes As Range
  9. Dim MaZone As Range
  10.  
  11. Set cel_cible = Worksheets("Devis").Columns(2).Find("MATERIEL", searchdirection:=xlPrevious)
  12. If cel_cible Is Nothing Then
  13. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  14. Exit Sub
  15. End If
  16.  
  17. For Each cel_source In Worksheets("Prix matière").Range("I3:I122")
  18. If cel_source.Value <> 0 Or cel_source.Offset(, 1).Value <> 0 Then
  19. Set cel_cible = cel_cible.Offset(1)
  20. cel_cible.Offset(1).EntireRow.Insert Shift:=xlDown
  21. Set newline = cel_cible.Offset(2).EntireRow
  22. Set Les6Colonnes = Worksheets("Devis").Range("H:J,L:N")
  23. Set MaZone = Intersect(cel_cible.EntireRow, Les6Colonnes)
  24.  
  25. MaZone.AutoFill Destination:=newline
  26.  
  27. cel_cible.Offset(, 0).Value = cel_source.Offset(, -6).Value
  28. cel_cible.Offset(, 1).Value = cel_source.Offset(, -2).Value
  29. cel_cible.Offset(, 2).Value = cel_source.Offset(, -1).Value
  30. cel_cible.Offset(, 3).Value = cel_source.Offset(, 0).Value
  31. cel_cible.Offset(, 4).Value = cel_source.Offset(, 1).Value
  32. cel_cible.Offset(, 5).Value = cel_source.Offset(, 4).Value
  33. cel_cible.Offset(, 9).Value = cel_source.Offset(, 6).Value
  34. End If
  35.  
  36. Next
  37.  
  38. End Sub


J'ai de nouveau le même message d'erreur au niveau de la ligne 21 : "Erreur d'exécution '1004': La méthode AutoFill de la classe Range a échoué."

Si j'enlève cette ligne 21, les lignes sont insérées et correctement copiées par contre les formules ne sont pas incrémentées et certains éléments de la mise en forme ne sont pas reproduits (ex : bordure) par contre c'est bon pour la couleur de fond.

Je poursuis mes essais.

EDIT : j'ai bien lu le paragraphe sur l'indentation, j'espère que la présentation du code sera plus "agréable"...
Expert Programmation

Je ne t'aide plus si tu ne m'indentes pas correctement ton code. :fou: 

Ajoute aussi Option Explicit au début de ton code, et déclare tes variables.

EDIT: Eh, c'est la méthode AutoFill() qui n'est pas appropriée. Utilise Copy(Destination) ou explique plus exactement ce que tu veux faire.

Alors pour préciser mon besoin :
Sur ma feuille "Devis", j'ai donc la ligne avec MATERIEL en colonne B qui me sert de "référence".
En dessous cette ligne, j'ai une ligne dans laquelle je copie certaines cellules issues de la feuille "Prix matière" grâce à la macro. Sur cette même ligne, j'ai des formules qui se calculent automatiquement une fois les données copiées.
Pour cette 1ère ligne, cela fonctionne.

Par contre, les autres lignes qui sont insérées grâce à la macro ne présentent pas exacement le même format et les formules ne sont pas conservées.

Dernier point, je souhaite conserver une autre ligne en bas de ce bloc qui calcule le total des lignes insérées. Pour cette partie, je crois que l'incrémentation se fait automatiquement étant donné que la ligne est déjà présente avant l'exécution de la macro.

Merci encore.

EDIT : alors après quelques tests, je n'arrive toujours pas à intégrer les formules sur les lignes insérées.
Par contre, je vais modifier légèrement mon besoin.
Pour faciliter la chose : on va dire que MATERIEL se situe en A1.
A partir de la ligne 2 : je commence à insérer les lignes copiées.
En C3 : j'ai actuellement une formule qui se présente de la sorte =SOMME(C2:C2)*B3 ou =SOMME(C2)*B3 (j'ai essayé des deux manières) --> lorsque j'insère une ligne au-dessus (même manuellement), la formule reste telle quelle.
En C4 : j'ai la formule =SOMME(C2:C3) et lorsque j'insère une ligne au-dessus de C3 comme le fait la macro la formule s'incrémente.
J'ai donc un souci au niveau de l'incrémentation de la formule en C3.
Expert Programmation

Oki. C'est clair pour moi maintenant.
Pour l'intentation, c'est presque ça (lignes 12 & 34 [:zeb:4] ) :lol: 

  1. Dim lig_materiel As Range
  2. Dim col_intersec As Range
  3. Dim lig_cible As Range
  4. Dim ws_devis As Worksheet
  5.  
  6. Set ws_devis = Worksheets("Devis" )
  7.  
  8. ' // J'ai viré le SearchDirection, inutile et faux
  9. Set lig_materiel = ws_devis.Columns(2).Find("MATERIEL").EntireRow
  10. If lig_materiel Is Nothing Then
  11. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  12. Exit Sub
  13. End If
  14.  
  15. ' // Il faut déterminer combien de lignes sont déjà présentes
  16. ' // pied du tableau = "je souhaite conserver une autre ligne en bas de ce bloc qui calcule le total des lignes insérées."
  17.  
  18. ' // S'il y a au moins un espace vide entre la dernière ligne du tableau et le pied du tableau
  19. Set lig_cible = ws_devis.Columns(2).End(xlDown).EntireRow
  20.  
  21. ' // Si le pied du tableau contient un mot clef comme la tête du tableau contenait MATERIEL
  22. Set lig_cible = ws_devis.Columns(2).Find("PIED").EntireRow ' // <- /!\ je n'ai pas fait de gestion d'erreur
  23.  
  24. ' // Si le pied du tableau contient une cellule nommée
  25. Set lig_cible = ws_devis.Range("PIED").Offset(-1).EntireRow
  26.  
  27. ' // Sinon, par exemple on ajoute après la ligne de référence.
  28. Set lig_cible = lig_materiel.Offset(2)
  29.  
  30. ' // J'ai sorti cette ligne de la boucle. Inutile de faire 120 fois la même chose
  31. Set col_intersec = Worksheets("Devis" ).Range("H:J,L:N" )
  32.  
  33. For Each cel_source In Worksheets("Prix matière" ).Range("I3:I122" )
  34. If cel_source.Value <> 0 Or _
  35. cel_source.Offset(, 1).Value <> 0 _
  36. Then
  37. ' // On insert une ligne juste après la ligne lig_cible en cours
  38. ' // J'ai viré le Shift inutile.
  39. lig_cible.Insert
  40. Set lig_cible = lig_cible.Offset(-1)
  41.  
  42. ' // Maintenant, on copie la ligne de référence dans la nouvelle ligne
  43. ' // Les attributs sont conservés, les formules aussi.
  44. lig_materiel.Offset(1).Copy lig_cible
  45.  
  46. ' // S'il y a des valeurs dont on ne veut pas, on les supprime a posteriori
  47.  
  48. ' // Ou on les écrase :
  49. lig_cible.Cells(2).Value = cel_source.Offset(, -6).Value
  50. lig_cible.Cells(3).Value = cel_source.Offset(, -2).Value
  51. lig_cible.Cells(4).Value = cel_source.Offset(, -1).Value
  52. lig_cible.Cells(5).Value = cel_source.Offset(, 0).Value
  53. lig_cible.Cells(6).Value = cel_source.Offset(, 1).Value
  54. lig_cible.Cells(7).Value = cel_source.Offset(, 4).Value
  55. lig_cible.Cells(11).Value = cel_source.Offset(, 6).Value
  56. End If
  57. Next

Tout d'abord bonjour,

J'ai bien essayé le code hier, mais le résultat n'est pas celui attendu...

J'ai essayé en le modifiant mais sans plus de succès... Actuellement, il insère des lignes vierges en trop, avec des formats différents d'une ligne à l'autre et en efface également.

Voici le code que j'ai placé dans la macro :
  1. Sub Devis()
  2.  
  3. Dim ws_devis As Worksheet
  4. Dim lig_materiel As Range
  5. Dim col_intersec As Range
  6. Dim lig_cible As Range
  7. Dim cel_source As Range
  8.  
  9. Set ws_devis = Worksheets("Devis")
  10.  
  11. Set lig_materiel = ws_devis.Columns(2).Find("MATERIEL").EntireRow
  12. If lig_materiel Is Nothing Then
  13. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  14. Exit Sub
  15. End If
  16.  
  17. Set lig_cible = lig_materiel.Offset(1) ' // Sinon, par exemple on ajoute après la ligne de référence.
  18. Set col_intersec = ws_devis.Range("H:J,L:N")
  19.  
  20. For Each cel_source In Worksheets("Prix matière").Range("I3:I122")
  21. If cel_source.Value <> 0 Or cel_source.Offset(, 1).Value <> 0 Then
  22. lig_cible.Insert
  23. Set lig_cible = lig_cible.Offset(1)
  24. lig_materiel.Offset(1).Copy lig_cible ' // Maintenant, on copie la ligne de référence dans la nouvelle ligne
  25.  
  26. lig_cible.Cells(2).Value = cel_source.Offset(, -6).Value
  27. lig_cible.Cells(3).Value = cel_source.Offset(, -2).Value
  28. lig_cible.Cells(4).Value = cel_source.Offset(, -1).Value
  29. lig_cible.Cells(5).Value = cel_source.Offset(, 0).Value
  30. lig_cible.Cells(6).Value = cel_source.Offset(, 1).Value
  31. lig_cible.Cells(7).Value = cel_source.Offset(, 4).Value
  32. lig_cible.Cells(11).Value = cel_source.Offset(, 6).Value
  33. End If
  34. Next
  35.  
  36. End Sub


Je reprécise mon besoin et le format de ma feuille "Devis" pour le bloc MATERIEL :
Pour faciliter la chose, on va dire que MATERIEL se situe en A1.
A partir de la ligne 2 : je commence à insérer les lignes copiées.
En C3 : j'ai actuellement une formule qui se présente de la sorte =SOMME(C2:C2)*B3 ou =SOMME(C2)*B3 (j'ai essayé des deux manières) --> lorsque j'insère une ligne au-dessus (même manuellement), la formule reste telle quelle.
En C4 : j'ai la formule =SOMME(C2:C3) et lorsque j'insère une ligne au-dessus de C3 comme le fait la macro la formule s'incrémente.
J'ai donc un souci au niveau de l'incrémentation de la formule en C3.

Je vais poursuivre les tests.
Expert Programmation

Salut,

M'enfin, que devient ta ligne de référence qui est censée se trouver sous MATERIEL ?
Tu changes la configuration à chaque fois :/ 

Rhooolala, je viens de vérifier le code que je t'ai donné (Je n'ai pas toujours Excel sous la main, désolé).... J'ai corrigé le code. A la ligne 23 de ton code, il faut remonter d'une ligne, donc se décaler de -1 ligne.

Bon, sinon effectivement, Excel est assez espiègle quand il s'agit d'auto-incrémentation. Une solution consiste a réécrire la formule.

  1. ' // On cherche MATERIEL dans la colonne 2
  2. Set lig_matos = ws_devis.Columns(2).Find("MATERIEL").EntireRow
  3. ' // On trouve MATERIEL ...
  4. If lig_matos Is Nothing Then
  5. ' // ... Ou pas
  6. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  7. Exit Sub
  8. End If
  9.  
  10. ' // On dit que le pied de tableau est juste après...
  11. Set lig_somme = lig_matos.Offset(1)
  12. Do While True
  13. ' // On vérifie que ce pied de page contient une formule de type =Somme(..)..
  14. If lig_somme.Cells(3).Formula Like "=SUM(*" Then Exit Do
  15. ' // Sinon, c'est peut être la suivante
  16. Set lig_somme = lig_somme.Offset(1)
  17. ' // Mouhais. Qui a oublié de faire un pied de tableau plein de sommes ?
  18. If lig_somme.Row > 60000 Then
  19. MsgBox "Impossible de trouver le pied de tableau.", vbCritical Or vbOKOnly
  20. Exit Sub
  21. End If
  22. Loop
  23.  
  24. ' // On recopie les valeurs qui nous intéressent
  25. For Each cel_source In Worksheets(2).Range("I3:I122")
  26. lig_somme.Insert
  27. Set lig_cible = lig_somme.Offset(-1)
  28.  
  29. lig_cible.Cells(...).Value = cel_source.Offset(...).Value
  30. Next
  31.  
  32. ' // Et on recalcule la somme en colonne C
  33. lig_somme.Cells(3).Formula = "=SUM(" & ws_devis.Range(lig_matos.Offset(1), _
  34. lig_somme.Offset(-1)).Columns(3).Address & _
  35. ")*" & lig_somme.Cells(2).Address

Evidemment, il y a des considérations de mise en forme qui ne sont pas prises ici.
D'ailleurs, il faudra que tu t'expliques un peu mieux à ce sujet.
De la même manière que l'on a forcé la formule de somme, on peut forcer la mise en forme.

Encore merci, je vais tester ça de suite.

Juste pour repréciser mes changements de configuration.

Officiellement, je comptais utiliser la ligne MATERIEL comme ligne de référence. Mais je me suis aperçu que j'avais des formules à calculer dans les lignes que je comptais ajouter par la suite. J'ai donc créé ces 2 lignes vierges avec les formules dans les cellules concernées (dans les colonnes "H, J, L, N") en espérant qu'en insérant les lignes en dessous, les formules s'incrémentent...

Peut-être est-il plus simple d'indiquer directement à la ligne insérée, les formules à intégrer aux cases concernées ? Solution que tu sembles me proposer dans ta macro.

Désolé si une nouvelle fois je n'ai pas été assez précis.

Pour la mise en forme, je veux dire que certains paramètres comme la couleur de fond de la cellule étaient conservés par contre les lignes insérées n'avaient pas toutes des bordures mais il me semble que j'ai réussi à le rectifier.

Je vais adapter et tester ton code et je donne des nouvelles...

Merci.
Expert Programmation

Oki.

Effectivement, je te conseille de tout faire par VBA, surtout que nous sommes dans le forum VB :spamafote: 

Autant pour les formules des colonnes H, J, L et N, que pour la mise en forme.

Alors si je récapitule, dans ma feuille "Devis" pour le bloc MATERIEL, je ne conserve que la ligne qui sert de référence (celle qui contient le mot MATERIEL).

A partir de la condition, j'insère l'ensemble des lignes concernées et je copie les cellules qui m'intéressent. J'insère également les formules aux cellules souhaitées.

En bas de ces lignes, j'insère une ligne qui va me permettre de calculer un pourcentage à partir d'une somme calculée sur les lignes précedentes (concerne 2 celulles).

J'insère enfin une dernière ligne dans laquelle j'intègre des sommes calculées sur les lignes précédentes pour certaines cellules (concerne 7 cellules).

Dernier point, il faudra que je pense à intégrer le format des cellules lors de l'insertion des lignes.

J'ai comme l'impression que le chemin s'est rallongé.

Je vais commencer à voir comment je peux organiser tout ça. Merci de m'indiquer si cette démarche est la bonne.

2e solution :

Je reposte une macro précédemment réalisée qui nous avait laissée croire que la fin était proche.

  1. Option Explicit
  2.  
  3. Sub Devis()
  4.  
  5. Dim cel_source As Range
  6. Dim cel_cible As Range
  7.  
  8. Set cel_cible = Worksheets("Devis").Columns(2).Find("MATERIEL", searchdirection:=xlPrevious)
  9. If cel_cible Is Nothing Then
  10. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  11. Exit Sub
  12. End If
  13.  
  14. For Each cel_source In Worksheets("Prix matière").Range("I3:I122")
  15. If cel_source.Value <> 0 Or cel_source.Offset(, 1).Value <> 0 Then
  16. Set cel_cible = cel_cible.Offset(1)
  17. cel_cible.Offset(1).EntireRow.Insert Shift:=xlDown
  18.  
  19. cel_cible.Offset(, 0).Value = cel_source.Offset(, -6).Value 'B --> B
  20. cel_cible.Offset(, 1).Value = cel_source.Offset(, -2).Value 'C --> G
  21. cel_cible.Offset(, 2).Value = cel_source.Offset(, -1).Value 'D --> H
  22. cel_cible.Offset(, 3).Value = cel_source.Offset(, 0).Value 'E --> I
  23. cel_cible.Offset(, 4).Value = cel_source.Offset(, 1).Value 'F --> J
  24. cel_cible.Offset(, 5).Value = cel_source.Offset(, 4).Value 'G --> M
  25. cel_cible.Offset(, 9).Value = cel_source.Offset(, 6).Value 'K --> O
  26. End If
  27. Next
  28.  
  29. End Sub


Cette macro est satisfaisante si j'ai bien une ligne vierge intégrée en dessous MATERIEL. Les lignes insérées conservent le même format.

Les seuls soucis sont :
- la non incrémentation des formules tirées de la ligne "vierge" --> comment indiquer dans cette macro quelle formule appliquée pour les cellules concernées ? (--> avec la dernière macro proposée, je devrais m'en rapprocher).
- la ligne vierge qui est conservée tout au long de la macro et qui reste donc en pied de bloc à la fin de l'éxécution

Quelle solution choisir ?
Expert Programmation

Citation :
Quelle solution choisir ?
Un problème n'a pas toujours qu'une solution, c'est à toi de décider.

Citation :
- la ligne vierge qui est conservée tout au long de la macro et qui reste donc en pied de bloc à la fin de l'éxécution
Supprime-là - Rows(x).Delete, où cache-là - Rows(x).Hidden = True.

Effectivement je me doutais que les deux solutions étaient envisageables.

Mais je n'ai sûrement pas les connaissances suffisantes pour juger de la solution la plus simple ou plus efficace contrairement aux personnes compétentes qui participent activement à ce forum. Et je les en remercie une nouvelle fois.

Voici le code :
  1. Option Explicit
  2.  
  3. Sub Devis()
  4.  
  5. Dim cel_source As Range
  6. Dim cel_cible As Range
  7. Dim row_vide As Range
  8.  
  9. Set cel_cible = Worksheets("Devis").Columns(2).Find("MATERIEL", searchdirection:=xlPrevious)
  10. If cel_cible Is Nothing Then
  11. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  12. Exit Sub
  13. End If
  14.  
  15. For Each cel_source In Worksheets("Prix matière").Range("I3:I122")
  16. If cel_source.Value <> 0 Or cel_source.Offset(, 1).Value <> 0 Then
  17. Set cel_cible = cel_cible.Offset(1)
  18. cel_cible.Offset(1).EntireRow.Insert Shift:=xlDown
  19.  
  20. cel_cible.Offset(, 0).Value = cel_source.Offset(, -6).Value 'B --> B
  21. cel_cible.Offset(, 1).Value = cel_source.Offset(, -2).Value 'C --> G
  22. cel_cible.Offset(, 2).Value = cel_source.Offset(, -1).Value 'D --> H
  23. cel_cible.Offset(, 3).Value = cel_source.Offset(, 0).Value 'E --> I
  24. cel_cible.Offset(, 4).Value = cel_source.Offset(, 1).Value 'F --> J
  25. cel_cible.Offset(, 5).Value = cel_source.Offset(, 4).Value 'G --> M
  26. cel_cible.Offset(, 9).Value = cel_source.Offset(, 6).Value 'K --> O
  27. cel_cible.Offset(, 6).Formula = "=cel_cible.Offset(,3)*cel_cible.Offset(,5)"
  28. End If
  29. Next
  30.  
  31. Set row_vide = cel_cible.Offset(1)
  32. row_vide.EntireRow.Delete
  33.  
  34. End Sub


Ce code supprime correctement la ligne vierge. --> Merci

Reste ensuite à gérer l'incrémentation des formules... Existe-t-il un moyen de l'incrémenter lors de l'insertion et copie de la cellule précédente (au-dessus) ?
Méthode qui me semble la plus simple.
Ou alors indiquer pour les lignes insérées les formules à intégrer dans les cellules souhaitées.

EDIT : j'ai réédité le message en insérant une des formules que je souhaiterais intégrer (voir ligne 27) au cas où l'insertion avec l'incrémentation n'est pas possible. Actuellement, il me met une erreur dans la cellule correspondante et lorsque je clique sur la case, la formule n'est pas calculée et il est affiché "=cel_cible.Offset(,3)*cel_cible.Offset(,5)".
Expert Programmation

  1. ' // !!
  2. Set row_vide = cel_cible.Offset(1)
  3. row_vide.EntireRow.Delete
Attention à tes noms de variables. Tu définis row_vide comme une cellule, pas comme une ligne.

  1. ' // Version longue 1
  2. Set cel_reference_ligne_vide = cel_cible.Offset(1)
  3. cel_reference_ligne_vide.EntireRow.Delete
  4.  
  5. ' // Version longue 2
  6. Set row_vide = cel_cible.Offset(1).EntireRow
  7. row_vide.Delete
  8.  
  9. ' // master cut
  10. cel_cible.Offset(1).EntireRow.Delete


Pour copier tes formules, il faut te faire une liste de tes cellules et voilà :
  1. For Each cell In cellules_à_remplir_des_formules_du_dessus
  2. cell.FormulaR1C1 = Range("E6").Offset(-1).FormulaR1C1
  3. Next

Alors j'ai contourné le problème pour certaines formules avec le code ci-dessous :

  1. Sub Devis_materiel()
  2.  
  3. Dim cel_source As Range
  4. Dim cel_cible As Range
  5. Dim row_vide As Range
  6. Dim cell As Range
  7.  
  8. Set cel_cible = Worksheets("Devis").Columns(2).Find("MATERIEL", searchdirection:=xlPrevious)
  9. If cel_cible Is Nothing Then
  10. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  11. Exit Sub
  12. End If
  13.  
  14. For Each cel_source In Worksheets("Prix matière").Range("I4:I123")
  15. If cel_source.Value <> 0 Or cel_source.Offset(, 1).Value <> 0 Then
  16. Set cel_cible = cel_cible.Offset(1)
  17. cel_cible.Offset(1).EntireRow.Insert Shift:=xlDown
  18.  
  19. cel_cible.Offset(, 0).Value = cel_source.Offset(, -6).Value 'B --> B
  20. cel_cible.Offset(, 1).Value = cel_source.Offset(, -2).Value 'C --> G
  21. cel_cible.Offset(, 2).Value = cel_source.Offset(, -1).Value 'D --> H
  22. cel_cible.Offset(, 3).Value = cel_source.Offset(, 0).Value 'E --> I
  23. cel_cible.Offset(, 4).Value = cel_source.Offset(, 1).Value 'F --> J
  24. cel_cible.Offset(, 5).Value = cel_source.Offset(, 4).Value 'G --> M
  25. cel_cible.Offset(, 9).Value = cel_source.Offset(, 6).Value 'K --> O
  26. cel_cible.Offset(, 6).Copy Destination:=cel_cible.Offset(1, 6)
  27. cel_cible.Offset(, 7).Copy Destination:=cel_cible.Offset(1, 7)
  28. cel_cible.Offset(, 8).Copy Destination:=cel_cible.Offset(1, 8)
  29. cel_cible.Offset(, 10).Copy Destination:=cel_cible.Offset(1, 10)
  30. cel_cible.Offset(, 11).Copy Destination:=cel_cible.Offset(1, 11)
  31. cel_cible.Offset(, 12).Copy Destination:=cel_cible.Offset(1, 12)
  32. End If
  33. Next
  34.  
  35. cel_cible.Offset(1).EntireRow.Delete
  36.  
  37. End Sub


Dernier souci, entre la dernière ligne insérée et celle qui comprend les différents totaux, j'ai donc une ligne dans laquelle j'ai certaines cellules qui ont comme formule =SOMME(C2:C2)*B3 ou =SOMME(C2)*B3. Je ne parviens pas à rédiger la formule pour qu'elle se calcule sur l'ensemble des lignes insérées.
Expert Programmation

  1. cel_cible.Offset(, 6).Copy Destination:=cel_cible.Offset(1, 6)
M'ouais... C'est juste que c'est plus joli comme ça :
  1. cel_cible.Offset(1, 6).FormulaR1C1 = cel_cible.Offset(0, 6).FormulaR1C1
Parce que ça ne copie que la formule, et pas le reste, notamment la mise en forme. Pis 6 fois la même chose, c'est beaucoup :
  1. For Each j In Array(6, 7, 8, 10, 11, 12)
  2. cel_cible.Offset(1, j).FormulaR1C1 = cel_cible.Offset(0, j).FormulaR1C1
  3. Next
(Si tu trouves que je pinaille, c'est normal :o  ... :D  )

Pour tes formules, les dernières lignes de ce code ne te conviennent-elles pas ?

Tu ne pinailles pas, tu embellis ;)  Magnifique le code avec Array.

Je m'excuse pour la demande sur la formule avec SOMME, car j'étais effectivement en train d'adapter ce code dont je me souvenais mais j'avais anticipé un problème car avec moi les choses ne sont jamais évidentes.

Toujours est-il qu'à l'heure actuelle le code marche parfaitement.

Voici le code :
  1. Sub Devis_materiel()
  2.  
  3. Dim cel_source As Range
  4. Dim cel_cible As Range
  5. Dim row_vide As Range
  6. Dim cell As Range
  7. Dim lig_matos As Range
  8. Dim lig_somme As Range
  9. Dim j As Variant
  10.  
  11. Set cel_cible = Worksheets("Devis").Columns(2).Find("MATERIEL", searchdirection:=xlPrevious)
  12. If cel_cible Is Nothing Then
  13. MsgBox "Impossible de trouver MATERIEL.", vbCritical Or vbOKOnly
  14. Exit Sub
  15. End If
  16.  
  17. For Each cel_source In Worksheets("Prix matière").Range("I4:I123")
  18. If cel_source.Value <> 0 Or cel_source.Offset(, 1).Value <> 0 Then
  19. Set cel_cible = cel_cible.Offset(1)
  20. cel_cible.Offset(1).EntireRow.Insert Shift:=xlDown
  21.  
  22. cel_cible.Offset(, 0).Value = cel_source.Offset(, -6).Value 'B --> B
  23. cel_cible.Offset(, 1).Value = cel_source.Offset(, -2).Value 'C --> G
  24. cel_cible.Offset(, 2).Value = cel_source.Offset(, -1).Value 'D --> H
  25. cel_cible.Offset(, 3).Value = cel_source.Offset(, 0).Value 'E --> I
  26. cel_cible.Offset(, 4).Value = cel_source.Offset(, 1).Value 'F --> J
  27. cel_cible.Offset(, 5).Value = cel_source.Offset(, 4).Value 'G --> M
  28. cel_cible.Offset(, 9).Value = cel_source.Offset(, 6).Value 'K --> O
  29.  
  30. For Each j In Array(6, 7, 8, 10, 11, 12)
  31. cel_cible.Offset(1, j).FormulaR1C1 = cel_cible.Offset(0, j).FormulaR1C1
  32. Next
  33. End If
  34. Next
  35.  
  36. Set lig_matos = Worksheets("Devis").Columns(2).Find("MATERIEL").EntireRow
  37. Set lig_somme = cel_cible.Offset(2).EntireRow
  38. lig_somme.Cells(8).Formula = "=SUM(" & Worksheets("Devis").Range(lig_matos.Offset(1), lig_somme.Offset(-1)).Columns(8).Address & " )*" & lig_somme.Cells(7).Address
  39.  
  40. cel_cible.Offset(1).EntireRow.Delete
  41.  
  42. End Sub


Je ne devrais pas tarder à clore ce sujet si aucune remarque n'est faite pour optimiser ce code et si la finalisation se passe dans de "bonnes conditions".

Je vous remercie de votre aide et je n'oublie pas l'autre macro en cours de réalisation sur un autre sujet.

Des remerciements plus prononcés pour "zeb" qui m'a assisté tout au long de ce topic.
Expert Programmation

:jap: 

------------------

Tu aimes ça les Array ?

Tiens, remplace tes lignes 22 à 28 :
  1. Dim x As Variant
  2. ' // B --> B C --> G D --> H E --> I F --> J G --> M K --> O
  3. For each x in Array(Array(0, -6), Array(1, -2), Array(2, -1), Array(3, 0), Array(4, 1), Array(5, 4), Array(9, 6))
  4. cel_cible.Offset(, x(0)).Value = cel_source.Offset(, x(1)).Value
  5. Next


Pis indente moi la ligne 31 .... :fou:  .... :lol: 
Lassé par la pub ? Créez un compte