[VBA] amélioration tableau mémoire
- Messages
- 3'581
- Excel
- 2013, 2019, 365
- Inscrit
- 11/04/2020
- Emploi
- Formateur bureautique, dvpt fichiers
Bonjour à tous,
À mon petit niveau, j'arrive maintenant à peu près à me débrouiller avec les tableaux en mémoire, grâce aux aides et astuces de Pijaku, h2so4, LouReed, Eriiic, etc.
Je vous remercie encore une fois infiniment, c'est génial.
Je suis du coup arrivé à pondre le petit code imparfait ci-dessous. Il permet de créer le planning de certaines personnes, à partir d'une base de données (feuil8), dans laquelle on a des 1 ou du vide, dans les colonnes LUNDI, MARDI ... DIMANCHE. Il ne génère rien si le jour est férié.
C'est pas grand chose, mais j'en suis déjà content.
Mon souci, c'est que si une personne est hospitalisée, il ne faudrait pas lui générer de croix. On sait qu'une personne est hospitalisée si une date est rentrée dans la colonne hospitalisation (on notera la date de début d'indisponibilité de la personne).
Je n'arrive pas, dans la logique, à inclure ceci. Pareil, il faudrait que si la date de début de service de la personne se trouve un mois ultérieur à celui qu'on veut créer, alors on zappe la personne. Ca me paraît intégrable au 'si OK ou hospitalisation', mais je pense que pour résoudre le problème, il devrait sauter.
Le bouton "nouveau mois" est actif, c'est celui qui lance la macro.
Sub créerMois()
Dim ladate As String, ShNew As String
Dim jour As String
ladate = Format(Year(Date) & "/" & Month(Date) + 1 & "/" & 1, "dd/mm/yyyy") 'on propose le 1er jour du mois suivant
ladate = InputBox("Date du 1er jour du mois à créer ?", "Création", ladate)
If IsDate(ladate) = False Then Exit Sub 'si mauvaise date rentrée, on quitte
[D5] = CDbl(CDate(ladate)) 'on écrit le date en D5, le reste du mois s'adapte
ShNew = Format(ladate, "mmmm") 'on récupère le mois en toutes lettres pour nommer la feuille qu'on va créer
ActiveSheet.Copy after:=ActiveSheet: ActiveSheet.Name = ShNew 'on copie la feuille et on la nomme avec notre variable
ActiveSheet.Shapes.Range(Array("Rectangle : coins arrondis 2")).Delete 'on supprime la forme de création de mois
Dim tablo()
Dim j As Integer
j = 0
With Feuil8.ListObjects("T_BDD").ListColumns("DÉBUT DU SERVICE").Range.Cells(1, 1) 'la référence est la cellule "Début de service, BD feuil8
For i = 1 To Feuil8.Range("T_BDD").Rows.Count 'on boucle de 1 au nombre total de lignes de la BDD
'si la cellule "SITUATION" de la ligne concernée est égale à "OK" ou "Hospitaliation"
If UCase(.Offset(i, 11).Value) = "OK" Or UCase(.Offset(i, 11).Value) = "HOSPITALISATION" Then
j = j + 1 'on incrémente J
ReDim Preserve tablo(1 To 157, 1 To j)
tablo(1, j) = .Offset(i, -10) '1ère valeur = numéro
tablo(2, j) = .Offset(i, -8) '2ème valeur = nom
tablo(3, j) = .Offset(i, -2) '3ème valeur = régime
For k = 4 To 157 Step 5 'on n'aura pas forcément de 4ème valeur, ou 5ème, etc. Donc variable qui va adapter.
'si D5 est une date, et n'est pas un jour férié, alors
If IsDate(Sheets(ShNew).Range("A5").Offset(0, k - 1)) And _
Not (IsNumeric(Application.Match(Sheets(ShNew).Range("A5").Offset(0, k - 1), Feuil8.Range("AB1:AB200"), 0))) Then
jour = UCase(Format(Sheets(ShNew).Range("A5").Offset(0, k - 1), "dddd")) 'on récupère le jour de la date en toutes lettres
If Feuil8.ListObjects("T_BDD").ListColumns(jour).Range.Cells(i + 1, 1) = 1 Then 'si la cellule ligne actuelle, dans la colonne nommée comme le jour est égale à 1
tablo(k, j) = "X" 'la ligne k du tableau est égale à "X"
If .Offset(i, 8) = 1 Then tablo(k + 1, j) = "X" 'si la valeur "pain" est égale à 1, k+1 = "X"
If .Offset(i, 9) = 1 Then tablo(k + 2, j) = "X" 'si la valeur "potage" est égale à 1, k+2 = "X"
If .Offset(i, 10) = 1 Then tablo(k + 3, j) = "X" 'si la valeur "soir" est égale à 1, k+3 = "X"
Else: End If
Else: End If
Next k 'une fois tous les tests effectués pour une date, on passe 5 colonnes à droite, donc au lendemain
Else: End If
Next i 'une fois tous les jours du mois passés, on passe à la personne suivante et on recommence
End With
Sheets(ShNew).Cells(7, 1).Resize(UBound(tablo, 2), UBound(tablo, 1)) = Application.Transpose(tablo) 'on colle tout ça au bon endroit.
End Sub
Merci beaucoup à vous, si certains veulent passer du temps pour m'aider à améliorer ceci !
Salut JoyeuxNoël,
premières choses avant de continuer le décorticage...
- vérifier que la feuille ne soit pas déjà créée ;
- le nom de la forme "Rectangle" copiée sera toujours '1', me semble-t-il, et sera à chrcher dans la feuille du nom "ShNew".
ShNew = Format(ladate, "mmmm") 'on récupère le mois en toutes lettres pour nommer la feuille qu'on va créer
For x = 1 To Sheets.Count
If Sheets(x).Name = ShNew Then _
MsgBox "Ce mois a déjà été créé !": _
Exit Sub
Next
ActiveSheet.Copy after:=ActiveSheet: ActiveSheet.Name = ShNew 'on copie la feuille et on la nomme avec notre variable
Worksheets(ShNew).Shapes.Range(Array("Rectangle : coins arrondis 1")).Delete 'on supprime la forme de création de mois
Avant de me casser la tête à comprendre : Déso, parlé trop vite...
- comment définis-tu le menu précis de chaque personne ?
- valeurs "Livrée", "Livré",... ?? Différences ? Utilité ? Entrée manuelle ?
A+
Bonjour,
Chez moi (2016) ces shapes ne sont pas copiées et provoquent une erreur si on essaie de les supprimer.
Ça me parait pas net cette histoire de OK et d'hospitalisation... Il me semble que si OK on livre mais pas si "hospitalisation"...
A quoi doit-on se fier : A la colonne V ou à la colonne X... pourquoi faire 2 colonnes ? Ça augmente le risque que les 2 colonnes ne concordent pas...
A+
- Messages
- 3'581
- Excel
- 2013, 2019, 365
- Inscrit
- 11/04/2020
- Emploi
- Formateur bureautique, dvpt fichiers
Bonjour,
merci de vous être intéressés à mon sujet, qui n'est qu'une petite partie d'un projet plus vaste. Je vous ai recrée une petite partie ici. Les jours fériés ne sont normalement pas ici, de même que la source de la liste déroulante.
@Curulis :
Une fois le mois passé, la feuille sera archivée. On ne devrait donc pas avoir ce souci. Je pensais rajouter une vérification à ce niveau. C'est vrai que j'aurais pu le faire avant. Merci pour le bout de code !
Le nom de la forme est actuellement 2, il y en a une autre, que j'ai supprimée sur cette version. Au moment de la copie, elle garde son nom, donc normalement là-dessus c'est OK.
Pour ce qui est des valeurs livré à ce jour, livrée à ce jour, ce seront à priori seulement des données à titre indicatif, reste d'un ancien mode de fonctionnement (qui sautera certainement à terme et n'intervient en rien dans nos calculs).@Galopin :
Mince, bizarre, je l'ai fait sous 2013. Je ne vois pas de raison que ça foire sur 2016. Je vais essayer sur 2019 aussi.
On ne livre effectivement que si OK, ou si une date d'hospitalisation est renseignée, mais ultérieure à la date du jour (si elle a été programmée. Je ne sais pas si ce cas de figure peut arriver, mais j'aime autant anticiper).
Mais là c'est au moment de la création du mois. Il faut qu'une personne hospitalisée soit créée quand même. Son hospitalisation peut se terminer à tout moment et il faudra à nouveau la livrer à son retour. C'est donc aussi pratique si elle a déjà été générée.
Par contre, on ne lui mettra pas de croix si son statut est hospitalisé ET que la date concernée est postérieure à la date de début d'hospitalisation.
J'espère être clair. J'ai peut-être écrit mon poste initial trop précipitamment.
Bonsoir,
Moi je te le donne comme je le comprend et sans ton charabia d'Offset pour cause d'incompréhension.
De plus je n'ai gardé que la condition sur la date de départ et "OK" parce que tes explications sur les hospitalisation, pour moi c'est du jus de boudin.
Eventuellement YAPUKA rajouter des condition à Y...Tu verras c'est intuitif ! Il suffit que chaque ligne de Y soit Vrai :
Quand les idées sont claires les mots pour le dire se trouvent aisément...
Je n'ai rempli que les 3 premières colonnes pour cause d'allergies aux complications :
Tous le reste des adresses doit être écrit "en dur" comme ces 3 premières colonnes.
On ne fait référence qu'au tableau source et au tableau cible : Ça se lit comme la feuille...
C'est quand même plus simple que de calculer des décalages...
A+
Je viens de réfrléchir que pour évaluer la date, étant donné que ArrC n'intègrent pas laDate, tu auras besoin de te faire une petite Fonction perso qui évaluera en fonction de "k" et de "laDate" si "leJour" est un dimanche ou un Férié...
Je n'ai pas envie de travailler la dessus maintenant : Galopin est un couche tôt... Mais demain j'essaierai de te bricoler une "tite" fonction simple à partir de ces param... pour que tu puisses ajuster ton remplissage...
A+
Salut JoyeuxNoël,
Salut Galopin (bonne nuit !
- à l'ouverture du fichier, une macro décide de l'affichage "Hospitalisation" si une date en [X:X] arrive à échéance ;
Private Sub Workbook_Open()
'
With Worksheets("Noms & Coordonnées")
For x = 2 To .Range("X" & Rows.Count).End(xlUp).Row
If .Range("X" & x).Value <> "" Then _
If IsDate(.Range("X" & x).Value) Then _
If CDate(.Range("X" & x).Value) <= Date Then .Range("V" & x).Value = "Hospitalisation"
Next
End With
'
End Sub
- petite gestion des données de l'InputBox (ne t'étonne pas, je me sers souvent des mêmes variables pour plusieurs choses tant qu'elles ne s'entrechoquent pas, bien sûr)
Do
iOK = 1
ShNew = Application.InputBox("Date du 1er jour du mois à créer ?", "Création", Default:=Format(dDate, "[$-040C]dd/mm/yyyy"), Type:=2)
If IsDate(ShNew) Then
If Day(CDate(ShNew)) = 1 Then
iOK = 0
For x = 1 To Sheets.Count
If Sheets(x).Name = WorksheetFunction.Proper(Format(CDate(ShNew), "mmmm")) Then _
MsgBox "Ce mois a déjà été créé !": _
iOK = 1
Next
End If
End If
Loop Until iOK = 0 Or ShNew = "Faux"
If ShNew = "Faux" Then Exit Sub
- calcul principal
tBDD = Range("T_BDD").Value
iOK = WorksheetFunction.CountIf(Range("T_BDD[STATUT]"), "OK") + WorksheetFunction.CountIf(Range("T_BDD[STATUT]"), "Hospitalisation")
With Worksheets(ShNew)
tExtract = .Range("A7:FB" & 6 + iOK).Value
For x = 1 To UBound(tBDD, 1)
iOK = 0
If tBDD(x, 22) = "OK" Or tBDD(x, 22) = "Hospitalisation" Then
iIdx = iIdx + 1
tExtract(iIdx, 1) = tBDD(x, 1)
tExtract(iIdx, 2) = tBDD(x, 3)
tExtract(iIdx, 3) = tBDD(x, 9)
For y = 1 To Day(DateAdd("m", 1, dDate) - 1)
dToday = DateAdd("d", iOK, dDate)
If CDate(tBDD(x, 11)) <= dToday And Not IsNumeric(Application.Match(dToday, sWkNC.Range("AB1:AB" & sWkNC.Range("AB" & Rows.Count).End(xlUp).Row), 0)) Then
If (tBDD(x, 22) = "Hospitalisation" And dToday < CDate(tBDD(x, 24))) Or tBDD(x, 22) = "OK" Then
If CInt(tBDD(x, 11 + Weekday(dToday, vbMonday))) = 1 Then
tExtract(iIdx, 3 + (iOK * 5) + 1) = "X"
For Z = 19 To 21
If CInt(tBDD(x, Z)) = 1 Then tExtract(iIdx, 3 + (iOK * 5) + (Z - 17)) = "X"
Next
End If
End If
End If
iOK = iOK + 1
Next
End If
Next
.Range("A7").Resize(iIdx, UBound(tExtract, 2)).Value = tExtract
End With
À adapter en fonction de la disposition de tes données secondaires (statuts, fériés) dans ton fichier final.
A+
- Messages
- 3'581
- Excel
- 2013, 2019, 365
- Inscrit
- 11/04/2020
- Emploi
- Formateur bureautique, dvpt fichiers
Bonsoir,
Petit coucou depuis le téléphone. Du coup je n'ai pas encore téléchargé ton fichier, Curulis.
Je n'avais aucun doute, hein, quant à la longueur du chemin qu'il me reste à parcourir ! Mais vous me la montrez très rapidement ☺️
Un immense merci à vous 2, déjà, pour vos retours.
@galopin :
Je comprends que tous ces offset te gavent... Au départ, pour trouver les 1 dans les colonnes lundi, mardi, etc... Je faisais un offset de l'équivalent du jour de la semaine de la date concernée, dans un array des jours de la semaine. Mais au final ça foirait. Comme le reste tournait bien, j'ai gardé ces offset ailleurs.
J'ai essayé de rajouter des conditions à y mais, comme tu l'as signalé ensuite, ce n'est pas ici qu'on prend en compte la date d'hospitalisation potentielle. Et une fois mes conditions rajoutées, je retombe sur la solution que j'avais à l'origine. En plus élégant tu me diras. Je te l'accorde 😉
Et on peut générer les lignes pour les personnes OK ou hospitalisées et ensuite supprimer les croix pour les personnes hospitalisées dans un second temps. Je saurais le faire sans souci. Mais je trouve dommage de faire des choses pour les supprimer ensuite, et j'ai grand espoir d'arriver à progresser avec votre aide sur ce problème.
@Curulis, je n'ai donc pas encore regardé mais, de ce que j'ai pu comprendre du code, j'ai bon espoir que ce soit tout bon ! Je te redis ça demain. Encore merci !
Finalement ça m'a empêché de dormir, je t'ai finalisé TOUT sauf ton "Hospitalisation" qui me parait un peu confus.
Mais je t'ai laissé un ligne pour te montrer ou ajouter une condition...
Hum... J'ai testé un minimum mais ça devrait être bon !
A+
- Messages
- 3'581
- Excel
- 2013, 2019, 365
- Inscrit
- 11/04/2020
- Emploi
- Formateur bureautique, dvpt fichiers
Bonjour à tous,
Wow, JoyeuxNoel, créateur officiel d'insomnies depuis 2021 ! C'est un titre dont je me serais bien passé, m'enfin c'en est toujours un !
Désolé
@Galopin : Après test, ça a l'air effectivement bon ! Pour les hospitalisations, ce n'est normalement pas si compliqué que ça. Je te MP pour te poser un peu mieux le décor.
@Curulis, ça fonctionne bien effectivement. Par contre, pour la personne qui a une date d'hospitalisation, on ne devrait plus avoir de croix pour toutes les dates ultérieures à cette date là. C'est justement sur ce point que je coinçais.
Merciiii encore !
La modif pour une date d'hospitalisation colonne 24 (le jour d'hospitalisation est livré, à partir du lendemain non) :
'Ensuite il faudra rajouter une conditions pour les autres exceptions... (Hospitalisation etc...)
Y1 = Y1 And Not (ArrS(i, 24) <> "" And ArrS(i, 24) < CDate(DateSerial(Year(laDate), Month(laDate), (k + 1) / 5)))
If Y1 Then 'Gère les exceptions -La suite sans changement.
A+
Salut JoyeuxNoël,
Salut Galopin,
Petit défaut de signalisation : j"aurais dû laisser "Hospitalisation" pour cette personne, cela fonctionnera.
C'est un aspect, pas trop difficile mais, comme le soulignait Galopin, à mettre au point.
Qui dit "Hospitalisation" ou "Arrêt" dit "Reprise".
Je te propose ceci en colonne [X:X]:
- pour une future Hospitalisation : "H/12-2-2021" ;
- pour un futur Arrêt (vacances, retour en famille... ??) : "A/15-3-2021" ;
- non, je ne te dirai pas pour un futur décès !
- pour une date de reprise après "Hospitalisation" ou "Arrêt" : "R/25-4-2021
Il faut également savoir comment tu vas archiver tes feuilles car il faut trouver ces infos de mois en mois pour connaître les dates "Hospitalisation" et "Arrêt" à maintenir.
Si ça te convient, je planche là-dessus...
A+
- Messages
- 3'581
- Excel
- 2013, 2019, 365
- Inscrit
- 11/04/2020
- Emploi
- Formateur bureautique, dvpt fichiers
Salut JoyeuxNoël,
Salut Galopin,Petit défaut de signalisation : j"aurais dû laisser "Hospitalisation" pour cette personne, cela fonctionnera.
C'est un aspect, pas trop difficile mais, comme le soulignait Galopin, à mettre au point.
Qui dit "Hospitalisation" ou "Arrêt" dit "Reprise".Je te propose ceci en colonne [X:X]:
- pour une future Hospitalisation : "H/12-2-2021" ;
- pour un futur Arrêt (vacances, retour en famille... ??) : "A/15-3-2021" ;
- non, je ne te dirai pas pour un futur décès !
- pour une date de reprise après "Hospitalisation" ou "Arrêt" : "R/25-4-2021Il faut également savoir comment tu vas archiver tes feuilles car il faut trouver ces infos de mois en mois pour connaître les dates "Hospitalisation" et "Arrêt" à maintenir.
Si ça te convient, je planche là-dessus...
A+
Re,
Pour l'archivage des feuilles, une macro va déplacer la feuille voulue dans un fichier d'archives.
Une autre macro récupère toutes les croix pour chaque personne en fin de mois, pour lancer une facturation. Au passage, elle crée une vraie base de données, pour avoir des stats potentielles en fin d'année.
Si une personne arrête, on met donc le statut sur arrêt et sa ligne ne sera pas générée. Un double clic envoie la ligne de la personne dans une BDD d'archive.
Je ne pense pas qu'il y ait besoin de garder trace des hospitalisations.
- Messages
- 3'581
- Excel
- 2013, 2019, 365
- Inscrit
- 11/04/2020
- Emploi
- Formateur bureautique, dvpt fichiers
Re,
J'essaie de bidouiller et de triturer ça dans tous les sens depuis tout à l'heure, ça ne fonctionne pas chez moi.
La modif pour une date d'hospitalisation colonne 24 (le jour d'hospitalisation est livré, à partir du lendemain non) :
'Ensuite il faudra rajouter une conditions pour les autres exceptions... (Hospitalisation etc...) Y1 = Y1 And Not (ArrS(i, 24) <> "" And ArrS(i, 24) < CDate(DateSerial(Year(laDate), Month(laDate), (k + 1) / 5))) If Y1 Then 'Gère les exceptions -La suite sans changement. A+
J'ai modifié ça en amont, pour que ça génère la ligne quand il y a "hospitalisation" aussi.
For i = 1 To UBound(ArrS)
Y = ArrS(i, 11) <= DateSerial(Year(laDate), Month(laDate) + 1, 1)
Y = Y And (ArrS(i, 22) = "OK" Or ArrS(i, 22) = "Hospitalisation")
'ici tu peux rajouter autant de conditions que tu veux mais on ne traite que la présence de la ligne ou pas
Edit :
Fait intéressant :
Si je crée le mois de février, et que je mets la personne 1 en hospitalisation à partir du 12 février, rien n'est généré au niveau de ses croix, à part le jeudi 25 février
- Messages
- 3'581
- Excel
- 2013, 2019, 365
- Inscrit
- 11/04/2020
- Emploi
- Formateur bureautique, dvpt fichiers
Youpiiii, cette version fonctionne !!!
J'espère que ça servira au moins une fois, cette histoire d'hospitalisation !
Un immense merci à Galopin01 !
@Curulis, je vais essayer de faire marcher avec ta version également.
Il y a tout un tas de bonnes choses à prendre dans tout ce que vous m'avez proposé !
Option Explicit
DefBool Y 'définit toutes les variables qui commencent par Y as Boolean
Sub NewMonth() 'Pas d'accent !
Dim laDate$, ShNew$, Jour$, ArrS, ArrC, i%, ii%, k%, Y, Y1
Dim iLC%, iLR%, WS As Worksheet
Dim TData As ListObject
Set TData = Range("T_BDD").ListObject
ArrS = TData.DataBodyRange.Value2 'Le tableau Source
iLR = UBound(ArrS)
laDate = Format(Year(Date) & "/" & Month(Date) + 1 & "/" & 1, "dd/mm/yyyy") 'on propose le 1er jour du mois suivant
laDate = InputBox("Date du 1er jour du mois à créer ?", "Création", laDate)
If IsDate(laDate) = False Then Exit Sub 'si mauvaise date rentrée, on quitte
[D5] = CDbl(CDate(laDate)) 'on écrit la date en D5, le reste du mois s'adapte
ShNew = Format(laDate, "mmmm") 'on récupère le mois en toutes lettres pour nommer la feuille qu'on va créer
ActiveSheet.Copy after:=ActiveSheet 'on copie la feuille et on la nomme avec notre variable
Set WS = ActiveSheet
With WS
.Name = ShNew
For iLC = 4 To 154 Step 5
If .Cells(5, iLC) = "" Then Exit For
Next
'Le dernier jour du mois est iLC -5
ArrC = .Range(.Cells(7, 1), .Cells(6 + iLR, iLC - 1)).Value 'Le tableau cible est vide
For i = 1 To UBound(ArrS)
Y = ArrS(i, 11) <= DateSerial(Year(laDate), Month(laDate) + 1, 1)
Y = Y And (ArrS(i, 22) = "OK" Or ArrS(i, 22) = "Hospitalisation")
'ici tu peux rajouter autant de conditions que tu veux mais on ne traite que la présence de la ligne ou pas
If Y Then
ArrC(i, 1) = ArrS(i, 1)
ArrC(i, 2) = ArrS(i, 3)
ArrC(i, 3) = ArrS(i, 9)
For k = 4 To iLC - 5 Step 5
Jour = DateCol(k, CDbl(CDate(laDate)))
'C'est dans cette boucle For qu'on gère les interruptions en cours de mois
Y1 = CDate(DateSerial(Year(laDate), Month(laDate), (k + 1) / 5)) >= ArrS(i, 11)
Y1 = Y1 And Not EstJourFerie(CDate(DateSerial(Year(laDate), Month(laDate), k - 3)))
Y1 = Y1 And Not (ArrS(i, 22) = "Hospitalisation" And ArrS(i, 24) <= CDate(DateSerial(Year(laDate), Month(laDate), (k + 1) / 5)))
If Y1 Then 'Gère les exceptions
If ArrS(i, 11 + Jour) = 1 Then
ArrC(i, k) = "X"
For ii = 1 To 3
ArrC(i, k + ii) = IIf(ArrS(i, 18 + ii) = 1, "X", "")
Next
ArrC(i, k + ii) = ArrS(i, 2)
End If
End If
Next
End If
Next
'Et on bascule tout dans la feuille
.Range(.Cells(7, 1), .Cells(6 + iLR, iLC - 1)) = ArrC
For i = 6 + iLR To 7 Step -1
'ici on supprime les lignes vides
'If .Cells(i, 1) = "" Then Rows(i).Delete
Next
End With
End Sub
Function DateCol%(k%, D&)
Dim i% 'Cette fonction renvoie le jour de la semaine en fonction de k et D (laDate)
DateCol = Application.WorksheetFunction.Weekday(DateSerial(Year(D), Month(D), (k + 1) / 5), 2)
End Function
Function EstJourFerie(ByVal laDate As Date, Optional ByVal EstPentecoteFerie As Boolean = True) As Boolean
'Détermine si la date passée en argument est un jour férié (en France) ou non :
' 101 = 1er Janvier '501 = 1er Mai '508 = 8 Mai '714 = 14 Juillet
' 815 = 15 Août - 1101 = 1er Novembre '1111 = 11 Novembre '1225 = 25 Décembre
' dPa = Lundi de Pâques 'dAs = Jeudi de l'Ascension 'dPe = Lundi de Pentecôte
'Remarque : Le lundi de Pentecôte est un jour férié mais parfois non chômé (EstPentecoteFerie = False dans ce cas)
Static Annee As Integer, dPa As Date, dAs As Date, dPe As Date, bPe As Boolean
Dim a As Integer, m As Integer, j As Integer
a = Year(laDate): m = Month(laDate): j = Day(laDate)
Select Case m * 100 + j
Case 101, 501, 508, 714, 815, 1101, 1111, 1225
EstJourFerie = True
Case 323 To 614 '323: Date mini Lundi de Pâques - 614 : Date maxi Lundi de Pentecôte
If a <> Annee Or EstPentecoteFerie <> bPe Then
Annee = a: dPa = Paques(a) + 1: dAs = dPa + 38
bPe = EstPentecoteFerie: If bPe Then dPe = dPa + 49 Else dPe = #1/1/100#
End If
Select Case DateSerial(a, m, j): Case dPa, dAs, dPe: EstJourFerie = True: End Select
End Select
End Function
Public Function Paques(ByVal an As Integer) As Date
Paques = CDate(Evaluate("=DATE(" & an & ",3,29.56+0.979*MOD(204-11*MOD(" & an & ",19),30)- WEEKDAY(DATE(" & an & ",3,28.56+0.979*MOD(204-11*MOD(" & an & ",19),30))))"))
End Function
Ce qui est intéressant : Hormis la phase de création de feuille, c'est l'absence totale de référence à ces feuilles. On pompe toutes les données dans un Array Source (ArrS) et on prépare un Array Cible (ArrC) vide qui a déjà sa dimension définitive. Ce qui évite les nombreux Redim (chronophages et surtout incompréhensibles car transposés.
Ainsi on travaille toujours en mémoire avec des indices faciles à lire (Ça se lit comme les Cells des Tableaux d'origine) Pas besoin de décodeur pour savoir de quoi on parle !
Toujours dans le même esprit on sous-traite l'identification des routines (Jours Fériés…) à une fonction perso…
Les conditions sont facilement lisible grâce à la structure :
Y = Cond1
Y = Y And Cond2 'ou Or….
J'aurai encore pu ajouter un ou 2 petits allègements mébon… Je trouve cette soluce assez élégante et surtout très lisible. Pas besoin de kilos de commentaires pour comprendre chaque ligne !
A+
Salut JoyeuxNoël,
Salut Galopin,
quelques évolutions :
- j'ai supprimé les listes de validation en 'Noms & Coordonnées' [V:V] pour laisser le soin du statut à une macro en fonction de l'encodage en [X:X] ;
- en [X:X] donc, tu peux encoder une future date d'hospitalisation ou d'arrêt de livraison temporaires (ou définitives, d'ailleurs) ou une date de décès ;
- le principe : une lettre, un "+" et une dat
Private Sub Worksheet_Change(ByVal Target As Range)
'
Application.EnableEvents = False
'
If Not Intersect(Target, Columns("X")) Is Nothing Then
iRow = Target.Row
If Target = "" Or InStr(Target, "+") = 0 Then
Range("V" & iRow).Value = "OK"
Target = ""
Else
sItem = Split(Target, "+")(0)
Range("V" & iRow).Value = Switch(sItem = "H" Or sItem = "", "Hospitalisation", sItem = "A", "Arrêt", sItem = "D", "Décès", sItem = "R", "Reprise")
End If
End If
'
Application.EnableEvents = True
'
End Sub
* H+12-02-2021 = Hospitalisation à partir du 12/02/2021 ;
* A+25/03/2021 = arrêt de livraison à partir du 25/03/2021 ;
* D+15-02-2021 = décès le 15/02/2021 ;
* R+27/02/2021 = reprise après hospitalisation ou arrêt temporaire ;
- en fonction du mode d'encodage de la date, tu comprends pourquoi j'ai choisis le '+' comme séparateur et non le slash "/"...
Comme ces événements (hospitalisation, arrêt, décès ou reprise) peuvent intervenir de façon inopinée, tu peux intervenir sur ton agenda même si celui-ci est déjà programmé.
Il te suffit de sélectionner, sur une feuille-agenda (Février, Mars...) la ligne de la personne concernée sur le nombre de jours souhaités.
La sélection doit envelopper des groupes complets de 5 cellules (Repas-Pain-Potage-Soir-Tournée).
- s'il y a une seule croix dans la sélection, la macro décide de tout annuler ;
- s'il n'y en a aucune, la macro supposera qu'il faut réinstaller les "X" adéquats.
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
'
Dim dDate As Date, iRow%, iRowT%, iCol%
'
Application.ScreenUpdating = False
'
If Sh.Name <> "Matrice" And Sh.Name <> "Noms & Coordonnées" Then
If Selection.Column Mod 5 = 4 And Selection.Columns.Count Mod 5 = 0 Then
If WorksheetFunction.CountIf(Selection, "X") > 0 Then
Selection.Value = ""
Else
iRow = Selection.Row
With Worksheets("Noms & Coordonnées")
iRowT = .Columns(3).Find(what:=Sh.Range("B" & iRow).Value, lookat:=xlWhole, LookIn:=xlValues).Row
For x = 0 To (Selection.Columns.Count / 5) - 1
dDate = DateAdd("d", x, CDate(Sh.Cells(5, Target.Column)))
If Not IsNumeric(Application.Match(dDate, .Range("AB1:AB" & .Range("AB" & Rows.Count).End(xlUp).Row), 0)) Then
iCol = Selection.Column + (x * 5)
If CInt(.Cells(iRowT, 11 + Weekday(dDate, vbMonday))) = 1 Then
For y = 18 To 21
If y = 18 Or (y > 18 And CInt(.Cells(iRowT, y)) = 1) Then Sh.Cells(iRow, iCol + (y - 18)) = "X"
Next
End If
End If
Next
End With
End If
End If
Sh.Range("C" & Selection.Row).Select
End If
'
Application.ScreenUpdating = True
'
End Sub
ATTENTION : tu restes responsable de l'actualisation de l'affichage en [X:X] pour les mois suivants ces manipulations...
Á tester en profondeur... ce que je ferai aussi de mon côté plus tard...
A+