Contrôler la fiabilité d'un inventaire permanent

Un prétexte pour apprendre à utiliser Access

Claude Leloup

En relation avec cette Q/R du forum DVP.

La démarche consiste à partir d'un besoin concret (y a-t-il une différence entre le stock effectif et celui renseigné par un logiciel ?) pour montrer ce qu'on peut faire « rapidement » avec Access.

Différentes techniques sont décrites en détail :

- un formulaire tout en un qui permet d'une part, d'ajouter, modifier, supprimer les enregistrements de sa table source et d'autre part d'opérer des recherches d'après plusieurs critères ;

- des exemples de mises en forme conditionnelles ;

- une astuce pour faire en sorte que certaines valeurs répétitives soient automatiquement reportées lors des encodages en série ;

- quelques lignes de code bien décortiquées pour que le débutant se familiarise avec le VBA.

Vous pouvez commenter et donner vos avis dans la discussion associée sur le forum :

Commentez

- Titre : Contrôler la fiabilité d'un inventaire permanent

- Auteur : Claude Leloup

- Parution : 9 janvier 2015

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2014 Claude Leloup. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Prérequis

- II. Le pied à l'étrier

- III. Contexte de l'outil à créer

- III-A. Calcul du SRA

- IV. Par quoi commencer ?

- IV-A. Quoi montrer ?

- IV-B. Comment le montrer ?

- V. Le modèle de données

- V-A. Les tables liées

- V-B. Les autres tables non liées

- VI. Le formulaire fMenu

- VII. Le formulaire fRapport

- VII-A. Un formulaire tout-en-un

- VII-B. Autres particularités

- VII-B-1. L'écart s'allume en vert ou en rouge selon qu'il est égal ou non à zéro

- VII-B-2. Report automatique

- VII-B-3. Choisir l'article selon deux critères

- VII-B-4. Les contrôles cboRef et txtUnite

- VII-B-5. Le contrôle txtSemaine

- VII-B-6. La fonction DebutSemaine()

- VIII. Le formulaire fCorrection

- VIII-A. Comment il fonctionne

- VIII-A-1. À l'ouverture et après chaque choix d'une semaine

- VIII-A-2. Imaginons que l'utilisateur ajoute une nouvelle correction

- VIII-A-3. Après fermeture (ou changement de semaine)

- VIII-B. Particularités de certains contrôles de fCommande

- VIII-C. Le code associé au formulaire

- IX. Le formulaire fArchives

- IX-A. Remarque sur le tri

- X. Téléchargement

- XI. Remerciements

I - Prérequis

Savoir consulter l'aide d'Access.

D'une manière générale, pour se documenter sur les propriétés d'un formulaire ou d'un état, ou de leurs contrôles :
- afficher l'objet en mode création ;
- cliquer sur la propriété, elle se met alors en surbrillance ;
- enfoncer la touche <F1>.
L'aide Access s'ouvre à la bonne page.
On peut aussi :
- ouvrir l'aide <F1>, choisir l'onglet « Aide intuitive » et suivre les instructions ;
- ouvrir la fenêtre d'exécution (<Ctrl> + G), saisir un mot-clé, y placer le curseur de la souris et presser <F1>.

II - Le pied à l'étrier

Le défi de cet article, c'est d'expliquer à un néophyte comment apprendre Access, en le guidant pour réaliser un outil qui l'aide dans sa gestion quotidienne.

Puisqu'il s'agissait d'un premier contact avec le logiciel, j'ai conseillé de lire d'abord ces deux tutoriels :

1. Access - Les Bases de Maxence HUBICHE ;

2. Créer des requêtes simples de Jean Ballat.

III - Contexte de l'outil à créer

Dans l'entreprise, un système informatique en place (une boîte noire) gère les inventaires dans les entrepôts.

Chaque jour, des équipes de contrôleurs vérifient que le stock physique sur place correspond effectivement à celui renseigné par le logiciel.

On veut mettre en place un indice de confiance des données renseignées par le logiciel, suivre l'évolution hebdomadaire de cet indice SRA (Stock Record Accuracy) et enregistrer les actions correctives.

III-A - Calcul du SRA

Soit les données relevées :

on calcule un taux de référence qui est l'inverse du nombre d'occurrences :

on calcule alors un indice de la manière suivante :

- si la valeur absolue de l'écart est supérieure à 5 %, l'indice vaut 0 ;

- sinon, l'indice vaut le taux de référence :

et le calcul final :

Au plus le SRA approche les 100 %, au plus la fiabilité est grande.

IV - Par quoi commencer ?

Pour se faire une idée de la bonne démarche, voyez les interventions de François de Sainte Marie (fsmrel) dans ce bijou de discussion.

IV-A - Quoi montrer ?

En d'autres mots, quelles sont les entités de l'univers de l'application ?

- Les articles de l'inventaire.

- L'endroit où ils se trouvent.

- Quand et qui a comparé la quantité renseignée par le logiciel avec celle effectivement sur place.

- L'historique des SRA hebdomadaires avant et après actions correctives.

C'est l'objet du modèle de données présenté au chapitre suivant.

IV-B - Comment le montrer ?

Nous présenterons ensuite :

- les constations des contrôleurs sur place dans un formulaire fRapport ;

- les calculs des SRA hebdomadaires avant et après actions correctives dans un formulaire fCorrection ;

- l'historique dans un troisième formulaire fArchives.

V - Le modèle de données

V-A - Les tables liées

Toutes les relations sont du type un à plusieurs et appliquent l'intégrité référentielle :

Voir Comprendre les jointures dans Access de Maxence Hubiche.

V-B - Les autres tables non liées

tArchiveSRA : source du formulaire fArchives. Elle sert à stocker le SRA initial et le SRA corrigé de chaque semaine.

tCor : elle intervient dans le processus de correction des SRA hebdomadaires.

tStrucCor : c'est la source du formulaire fCorrection. Cette table est systématiquement vidangée et repeuplée à chaque affichage du formulaire.

VI - Le formulaire fMenu

C'est le formulaire qui s'affiche à l'ouverture de la base de données.

Le code associé aux boutons appelle peu de commentaires :

1. Option Compare Database

2. Option Explicit

4. Private Sub btRapport_Click()

5. DoCmd.OpenForm "fRapport"

6. DoCmd.GoToRecord acDataForm, "fRapport", acNewRec

7. End Sub

9. Private Sub btCor_Click()

10. DoCmd.OpenForm "fCorrection"

11. End Sub

Aux lignes 5 et 10, on ouvre les formulaires respectifs.

En 6, on rend le formulaire fRapport prêt pour encoder un nouvel enregistrement.

VII - Le formulaire fRapport

VII-A - Un formulaire tout-en-un

Pour plus de détails sur ce genre de formulaire, voir ce tutoriel Formulaire de recherche polyvalent sur la base d'une requête enregistrée.

- Il permet d'ajouter, de modifier, de supprimer des enregistrements de la table tRapports.

- Il est conçu pour opérer des recherches multicritères (partie gris foncé).

Sa source est la requête rFiltreRapports :

SELECT tRapports.tRapportsPK, tRapports.RapDate, tRapports.tEquipesFK, tRapports.tAdressesFK, tRapports.tArticlesFK, tRapports.QPhysique, tRapports.QLog, tRapports.Commentaire, [QPhysique]-[QLog]<>0 AS Expr1
FROM tRapports
WHERE (((tRapports.RapDate)>=IIf(IsNull([Formulaires]![fRapport]![filtreDu]),#1/1/1900#,[Formulaires]![fRapport]![filtreDu]) And (tRapports.RapDate)<=IIf(IsNull([Formulaires]![fRapport]![filtreAu]),#1/1/2100#,[Formulaires]![fRapport]![filtreAu])) AND ((tRapports.tEquipesFK) Like IIf([Formulaires]![fRapport]![filtreEquipe].[ListIndex]=-1,"*" & [Formulaires]![fRapport]![filtreEquipe] & "*",[Formulaires]![fRapport]![filtreEquipe])) AND ((tRapports.tAdressesFK) Like IIf([Formulaires]![fRapport]![filtreAdresse].[ListIndex]=-1,"*" & [Formulaires]![fRapport]![filtreAdresse] & "*",[Formulaires]![fRapport]![filtreAdresse])) AND ((tRapports.tArticlesFK) Like IIf([Formulaires]![fRapport]![filtreReference].[ListIndex]=-1,"*" & [Formulaires]![fRapport]![filtreReference] & "*",[Formulaires]![fRapport]![filtreReference])) AND (([QPhysique]-[QLog]<>0)=Yes Or ([QPhysique]-[QLog]<>0)=No));

Un mot toutefois pour commenter la dernière colonne de la requête :

À première vue, cette instruction est farfelue : on teste si l'écart est différent de zéro… et on prend, quelle que soit la réponse !

C'est une ruse !

La fin du SQL de la requête se présentera comme ceci :

Et pour tenir compte de l'état (cochée ou non) de la case « N'afficher que s'il y a une différence : » (ccDifference), il suffirait de supprimer la partie surlignée en orange :

voici comment on y arrive par le code :

1. Public Sub MaJ()

2. 'Sub appelée à chaque mise à jour d'un filtre

3. Dim q As QueryDef

4. 'si la case "avec différence" n'est pas cochée, on choisit rFiltreRapports comme source

5. If Me.ccDifference = 0 Then 'donc pas cochée

6. Me.RecordSource = "rFiltreRapports"

7. 'si elle est cochée, on corrige le SQL de cette requête pour ne reprendre que les différences <> 0

8. Else

9. Set q = CurrentDb.QueryDefs("rFiltreRapports")

10. Me.RecordSource = Replace(q.SQL, "=Yes Or ([QPhysique]-[QLog]<>0)=No", "")

11. Set q = Nothing

12. End If

13. Me.Requery

14. End Sub

Explication du code (les nombres en gras indiquent les N° de lignes)

3 : définition d'une variable de type « QueryDef » : la bibliothèque « Microsoft DAO x.x Object Library » doit être ajoutée au projet. Pour ce faire, enfoncez <ALT+F11> pour accéder au code et dans la barre des menus, choisissez Outils>Références… et cherchez la référence dans la liste pour la cocher :

5-6 : si la case n'est pas cochée, on prend rFiltreRapports comme source => les enregistrements sont ramenés quel que soit l'état de la case,

9-11 : si la case est cochée,

9 : on va chercher le SQL de rFiltreRapports,
10 : on y supprime =Yes Or ([QPhysique]-[QLog]<>0=No et on l'affecte comme source,
11 : on libère q.

Ce qui correspond à ceci :

SELECT tRapports.tRapportsPK, tRapports.RapDate, tRapports.tEquipesFK, tRapports.tAdressesFK, tRapports.tArticlesFK, tRapports.QPhysique, tRapports.QLog, tRapports.Commentaire, [QPhysique]-[QLog] AS Expr1
FROM tRapports
WHERE (((tRapports.RapDate)>=IIf(IsNull([Formulaires]![fRapport]![filtreDu]),#1/1/1900#,[Formulaires]![fRapport]![filtreDu]) And (tRapports.RapDate)<=IIf(IsNull([Formulaires]![fRapport]![filtreAu]),#1/1/2100#,[Formulaires]![fRapport]![filtreAu])) AND ((tRapports.tEquipesFK) Like IIf([Formulaires]![fRapport]![filtreEquipe].[ListIndex]=-1,"*" & [Formulaires]![fRapport]![filtreEquipe] & "*",[Formulaires]![fRapport]![filtreEquipe])) AND ((tRapports.tAdressesFK) Like IIf([Formulaires]![fRapport]![filtreAdresse].[ListIndex]=-1,"*" & [Formulaires]![fRapport]![filtreAdresse] & "*",[Formulaires]![fRapport]![filtreAdresse])) AND ((tRapports.tArticlesFK) Like IIf([Formulaires]![fRapport]![filtreReference].[ListIndex]=-1,"*" & [Formulaires]![fRapport]![filtreReference] & "*",[Formulaires]![fRapport]![filtreReference])) AND (([QPhysique]-[QLog])<>0));

qui ramènera uniquement les écarts différents de zéro.

VII-B - Autres particularités

VII-B-1 - L'écart s'allume en vert ou en rouge selon qu'il est égal ou non à zéro

Pour ce faire, on recourt à la mise en forme conditionnelle :

dans le formulaire en construction, on sélectionne le contrôle ;

dans la barre des menus, on clique sur Format/Mise en forme conditionnelle ;

on complète le tableau qui s'affiche.

VII-B-2 - Report automatique

Lorsqu'on encode un enregistrement, les valeurs de l'équipe, la date et l'adresse deviennent automatiquement les valeurs par défaut de l'enregistrement suivant à encoder (éventuel).

C'est dans l'événement « Après mise à jour » de ces contrôles que cela se passe : on modifie leur propriété Valeur par défaut à la volée :

1. Private Sub cboEquipe_AfterUpdate()

2. Me.cboEquipe.DefaultValue = Me.cboEquipe

3. End Sub

5. Private Sub txtRapDate_AfterUpdate()

6. Me.txtRapDate.DefaultValue = "#" & Format(Me.txtRapDate, "mm/dd/yy") & "#"

7. End Sub

9. Private Sub CboAdresse_AfterUpdate()

10. Me.CboAdresse.DefaultValue = Me.CboAdresse

11. End Sub

Remarquez la syntaxe :

- si la valeur du champ est de type numérique (ligne 2), on écrit par exemple Me.cboEquipe.DefaultValue = Me.cboEquipe ;

- si c'est une date (ligne 6), il faut la présenter sous le format anglo-saxon (mois, jour, année) et l'encadrer de croisillons « # » ;

- si c'était une valeur de type texte, il faudrait l'encadrer d'apostrophes droites, comme ceci :

Me.DuTexte.DefaultValue = "'" & Me.DuTexte & "'".

VII-B-3 - Choisir l'article selon deux critères

L'article peut être choisi indifféremment par sa référence ou sa dénomination.

Cette technique est décrite dans le tutoriel de Pierre Fauconnier : Access : Sélection d'une donnée par l'une ou l'autre de ses propriétés.

VII-B-4 - Les contrôles cboRef et txtUnite

Examinons comment le contrôle (txtUnite) est alimenté.

D'abord, considérons la liste modifiable cboRef. Elle a quatre colonnes, c'est la deuxième qui apparaitra à l'écran (la 1re qui n'a pas une largeur de 0 cm) et c'est la 1re qui est liée (tArticleFK).

Considérons maintenant la propriété « Source contrôle » de txtUnite : =[cboRef].column(3). On récupère la quatrième colonne de la liste de cboRef.

Access commence sa numérotation à zéro : la 1re colonne [cboRef].column(0), la 2e colonne [cboRef].column(1)…

VII-B-5 - Le contrôle txtSemaine

1. Private Sub txtSemaine_AfterUpdate()

2. If Me.txtSemaine Like "*/*" Then

3. Me.FiltreDu = DebutSemaine(Me.txtSemaine)

4. Me.FiltreAu = Me.FiltreDu + 6

5. Call MaJ

6. Else

7. If Me.txtSemaine >= 0 And Me.txtSemaine <= 53 Then

8. Me.txtSemaine = Me.txtSemaine & "/" & Format(Date, "yy")

9. Call txtSemaine_AfterUpdate

10. End If

11. End If

12. End Sub

Explication du code (les nombres en gras indiquent les N° de lignes)

2-5 : si l'utilisateur a saisi un N° semaine/Année, on garnit le contrôle FiltreDu avec la date du lundi qui correspond à cette semaine et FiltreAu avec la date du dimanche suivant. Et on actualise le formulaire.
N.B. La fonction DebutSemaine() est décrite au § suivant.

7-9 : si l'utilisateur a seulement introduit un N° de semaine sans préciser l'année, on ajoute celle en cours par défaut. Et on refait un tour (c'est donc une fonction récursive : elle fait appel à elle-même !)

VII-B-6 - La fonction DebutSemaine()

1. Public Function DebutSemaine(Semaine As String) As Date

2. '        ? DebutSemaine("49/14") pour 49e semaine de 2014 => lundi 1er décembre

3. Dim i As Integer

4. Dim iSem As Integer 'N° de la semaine du 1er lundi

5. Dim t() As String

6. t = Split(Semaine, "/")

7. For i = 1 To 7

8. If Weekday(DateSerial(t(1), 1, i), vbMonday) = 1 Then Exit For

9. Next i

10. 'i/1/t(1) = date du 1er lundi de l'année

11. 'dans quelle semaine se trouve ce 1er lundi ?

12. iSem = DatePart("ww", DateSerial(t(1), 1, i), vbMonday, vbFirstFourDays)

13. 'rechercher la date de la semaine demandée

14. DebutSemaine = DateSerial(t(1), 1, i) + 7 * (t(0) - iSem)

15. End Function

Explication du code (les nombres en gras indiquent les N° de lignes)

3-5 : on définit des variables. t() est un tableau non dimensionné qui servira pour la fonction Split().

6 : nous aurons donc t(0) (la 1re colonne du tableau) qui contiendra le N° de semaine et t(1) (la 2e colonne du tableau) qui contiendra l'année.

7-9 : on lance une boucle qui construit une date qui part du 1er janvier de l'année et qui s'incrémente d'un jour à chaque itération. On sort de la boucle lorsque la date correspond à un lundi.

12 : on détermine quel est le N° de la semaine de ce 1er lundi de l'année.

Le 1er lundi est peut-être dans la 2e semaine ! Le calendrier français est régi par la norme ISO 8601:2000 qui déclare que la première semaine de l'année est la première semaine contenant le jeudi. Donc si le 1er janvier tombe un mardi, un mercredi ou un jeudi, le 1er lundi se situera dans la 2e semaine.

14 : on ajoute à la date du 1er lundi autant de fois 7 jours pour atteindre le lundi de la semaine désirée.

Pour tout savoir, ou presque, sur le traitement des dates en Access, voir le tutoriel deMaxence Hubiche  Les Fonctions Date/Heure.

VIII - Le formulaire fCorrection

VIII-A - Comment il fonctionne

La table tStrucCor est la source de ce formulaire.

VIII-A-1 - À l'ouverture et après chaque choix d'une semaine

tStrucCor est vidangée

puis, dans un 1er temps, repeuplée (exécution de la requête « Ajout » rCreatStrucCor :

INSERT INTO tStrucCor ( tRapportsPK, CorDate, Denomination, Adresse, QPhysique, QLog, Ecart, TauxRef, IndiceRef, QPhysiqueCor, QLogCor, EcartCor, TauxRefcor, IndiceRefCor )
SELECT tRapports.tRapportsPK, tRapports.RapDate, tArticles.Denomination, tAdresses.Adresse, tRapports.QPhysique, tRapports.QLog, CDbl(IIf([QPhysique]<>0,Abs(([QLog]-[QPhysique])/[QPhysique]),0)) AS pcEcart, 1/DLookUp("Nbre","rOccurrencesCor","tArticlesFK=" & [tArticlesFK]) AS TauxRef, IIf([pcEcart]>0.05,0,[TauxRef]) AS Indice, tRapports.QPhysique AS PhyCor, tRapports.QLog AS LogCor, CDbl(IIf([QPhysique]<>0,Abs(([QLog]-[QPhysique])/[QPhysique]),0)) AS pcEcartCor, 1/DLookUp("Nbre","rOccurrencesCor","tArticlesFK=" & [tArticlesFK]) AS TauxRefCor, IIf([pcEcart]>0.05,0,[TauxRef]) AS IndiceCor
FROM tArticles INNER JOIN (tAdresses INNER JOIN tRapports ON tAdresses.tAdressesPK = tRapports.tAdressesFK) ON tArticles.tArticlesPK = tRapports.tArticlesFK
WHERE (((tRapports.RapDate)>=IIf(IsNull([Formulaires]![fCorrection]![txtDu]),#1/1/1900#,[Formulaires]![fCorrection]![txtDu]) And (tRapports.RapDate)<=IIf(IsNull([Formulaires]![fCorrection]![txtAu]),#1/1/2100#,[Formulaires]![fCorrection]![txtAu])));

et, dans la foulée, modifiée par la requête « Mise à jour » rMajCorStrucCor :

UPDATE tCor INNER JOIN tStrucCor ON tCor.tRapportsPK = tStrucCor.tRapportsPK SET tStrucCor.[Action] = [tCor].[Action], tStrucCor.QPhysiqueCor = [tCor].[QPhysiqueCor], tStrucCor.QLogCor = [tCor].[QLogCor], tStrucCor.EcartCor = [tCor].[EcartCor], tStrucCor.IndiceRefCor = [tCor].[IndiceRefCor];

et le formulaire affiche :

VIII-A-2 - Imaginons que l'utilisateur ajoute une nouvelle correction

VIII-A-3 - Après fermeture (ou changement de semaine)

Les requêtes rCorAjouts et rCorModif sont exécutées :

INSERT INTO tCor ( tRapportsPK, [Action], QPhysiqueCor, QLogCor, EcartCor, IndiceRefCor )
SELECT tStrucCor.tRapportsPK, tStrucCor.Action, tStrucCor.QPhysiqueCor, tStrucCor.QLogCor, tStrucCor.EcartCor, tStrucCor.IndiceRefCor
FROM tStrucCor
WHERE (((tStrucCor.QPhysiqueCor)<>[QPhysique])) OR (((tStrucCor.QLogCor)<>[QLog]));

UPDATE tStrucCor INNER JOIN tCor ON tStrucCor.tRapportsPK = tCor.tRapportsPK SET tCor.[Action] = [tStrucCor].[Action], tCor.QPhysiqueCor = [tStrucCor].[QPhysiqueCor], tCor.QLogCor = [tStrucCor].[QLogCor], tCor.EcartCor = [tStrucCor].[EcartCor], tCor.IndiceRefCor = [tStrucCor].[IndiceRefCor];

La 1re ajoutera les corrections dans la table tCor. (Pour autant qu'il n'y ait pas encore de corrections pour ce tRapportPK. En l'occurrence, la mise à jour de cet enregistrement serait rejetée.)

Et la seconde actualisera les valeurs des corrections antérieures éventuelles.

la table tCor contient maintenant :

… et un nouveau cycle peut commencer.

VIII-B - Particularités de certains contrôles de fCommande

Il s'agit de txtDu et txtAu, ils ont leur propriété « Visible » à Non pour ne pas encombrer le formulaire affiché. Ils sont alimentés à la volée, après la mise à jour de txtSemaine. Bien que ces contrôles soient cachés, leur valeur est disponible : on s'en sert dans la requête rCreatStrucCor.

La propriété « Source contrôle » est =Somme([IndiceRefCor]). Cette syntaxe signifie que l'on demande la somme des valeurs de toute la colonne IndiceRefCor de la table/requête source du formulaire.

Ici, la « Source contrôle » est ="(" & Compte([tRapportsPK]) & " rapports)". Cette fois, on compte le nombre de lignes (non Null) dans la colonne tRapportsPK de la source et on concatène le résultat avec du texte.

Ici, on affiche le contenu d'un autre contrôle =[TlIndiceRefCor]. La syntaxe : le signe « = » suivi du nom du contrôle encadré de crochets.

Comme au point précédent, mais cette fois dans un calcul : =[NumCor]/[DenCor].

Un clic sur ce bouton déclenchera l'affichage du formulaire fArchives.

Les requêtes rArchiAjout et rArchiRemplace s'exécutent pour compléter la source du formulaire fArchives. Celui-ci s'affichera juste en dessous du bouton.

INSERT INTO tArchiveSRA ( RsaSemaine, RsaInitial, RsaModif )
SELECT Formulaires!fCorrection!txtSemaine AS Expr1, Formulaires!fCorrection!SRA AS Expr2, Formulaires!fCorrection!SRAcor AS Expr3;

UPDATE tArchiveSRA SET tArchiveSRA.RsaInitial = Formulaires!fCorrection!SRA, tArchiveSRA.RsaModif = Formulaires!fCorrection!SRAcor
WHERE (((tArchiveSRA.RsaSemaine)=[Formulaires]![fCorrection]![txtSemaine]));

(Le code est expliqué ci-dessous en 80-95.)

VIII-C - Le code associé au formulaire

1. Option Compare Database

2. Option Explicit

4. Private Sub Form_Open(Cancel As Integer)

5. 'positionner en haut de l'écran

6. DoCmd.MoveSize 200, 200

7. 'Vidanger tStrucCor

8. DoCmd.SetWarnings False

9. DoCmd.RunSQL "DELETE * FROM tStrucCor;"

10. 'Alimenter tStrucCor

11. DoCmd.OpenQuery "rCreatStrucCor"

12. DoCmd.OpenQuery "rMajCorStrucCor"

13. DoCmd.SetWarnings True

14. 'Afficher

15. Me.Requery

16. End Sub

18. Private Sub Form_Close()

19. 'Mettre la tCor à jour

20. DoCmd.SetWarnings False

21. DoCmd.OpenQuery "rCorAjouts"

22. DoCmd.OpenQuery "rCorModif"

23. DoCmd.SetWarnings True

24. 'Fermer fArchives

25. If CurrentProject.AllForms("fArchives").IsLoaded Then DoCmd.Close acForm, "fArchives"

26. End Sub

28. Private Sub txtSemaine_BeforeUpdate(Cancel As Integer)

29. 'Sauver les modifs éventuelles de l'affichage précédent

30. Call Form_Close

31. End Sub

33. Private Sub txtSemaine_AfterUpdate()

34. 'Fermer l'historique

35. If CurrentProject.AllForms("fArchives").IsLoaded Then DoCmd.Close acForm, "fArchives"

36. 'Habiller txtSemaine

37. If Me.txtSemaine Like "*/*" Then

38. Me.TxtDu = DebutSemaine(Me.txtSemaine)

39. Me.txtAu = Me.TxtDu + 6

40. Else

41. If IsNull(Me.txtSemaine) Then

42. Me.TxtDu = Null

43. Me.txtAu = Null

44. End If

45. If Me.txtSemaine >= 0 And Me.txtSemaine <= 53 Then

46. Me.txtSemaine = Me.txtSemaine & "/" & Format(Date, "yy")

47. Me.TxtDu = DebutSemaine(Me.txtSemaine)

48. Me.txtAu = Me.TxtDu + 6

49. End If

50. End If

51. Call Form_Open(0)

52. End Sub

54. Private Sub btTout_Click()

55. Me.txtSemaine = Null

56. Call txtSemaine_AfterUpdate

57. End Sub

59. Private Sub txtQPhysiqueCor_AfterUpdate()

60. 'Recalculer l'écart

61. If Me.txtQPhysiqueCor = 0 Then

62. Me.txtEcartCor = 0

63. Else

64. Me.txtEcartCor = Abs((Me.txtQLogCor - Me.txtQPhysiqueCor) / Me.txtQPhysiqueCor)

65. End If

66. 'Recalculer l'indiceRefCor

67. If Me.txtEcartCor > 0.05 Then

68. Me.txtIndiceRefCor = 0

69. Else

70. Me.txtIndiceRefCor = Me.txtTauxRefCor

71. End If

72. 'Actualiser

73. Me.Refresh

74. End Sub

76. Private Sub txtQLogCor_AfterUpdate()

77. Call txtQPhysiqueCor_AfterUpdate

78. End Sub

80. Private Sub btHistoriser_Click()

81. 'Vérifier qu'une semaine a été choisie

82. If IsNull(Me.txtSemaine) Then

83. MsgBox "Vous devez choisir une semaine"

84. Exit Sub

85. End If

86. 'Archiver les 2 SRA de cette semaine

87. DoCmd.SetWarnings False

88. DoCmd.OpenQuery "rArchiAjout"

89. DoCmd.OpenQuery "rArchiRemplace"

90. DoCmd.SetWarnings True

91. 'Afficher l'historique (Méthode Arkham46)

92. If CurrentProject.AllForms("fArchives").IsLoaded Then DoCmd.Close acForm, "fArchives"

93. DoCmd.OpenForm "fArchives", , , , , acHidden

94. PositionForm Forms("fArchives"), Me.btHistoriser

95. End Sub

Explication du code (les nombres en gras indiquent les N° de lignes)

6 : à l'ouverture, on positionne le formulaire en haut de l'écran. Pour vous documenter sur la méthode Docmd.moveSize, voir Comment positionner un formulaire à un endroit déterminé.

8-13 : on crée la structure comme expliqué plus haut.

19-23 : à la fermeture, le processus décrit plus haut.

25 : on referme le formulaire fArchive si celui-ci est encore ouvert.

30 : avant d'afficher les enregistrements d'une nouvelle sélection de « Semaine », on sauve les éventuelles corrections apportées durant la précédente sélection. En fait, on exécute les mêmes instructions que lors de la fermeture du formulaire (voir 18-26).

35 : on referme le formulaire fArchive si celui-ci est encore ouvert.

37-51 : on aménage txtDu et txtAu en fonction de la semaine choisie et on exécute les mêmes instructions que lors de l'ouverture du formulaire (voir 4-16).

55-56 : au clic sur le bouton « Autre semaine » (btTout), on met à Null txtSemaine et on exécute les mêmes instructions que si ce changement avait été opéré manuellement (35-52).

59-78 : on recalcule l'écart et l'indice en cas de modification d'une quantité.

80-95 : au clic sur le bouton « Archiver les SRA de cette semaine », on vérifie que l'utilisateur a choisi une semaine, sinon on affiche un message(82-85). On exécute les requêtes rArchiAjout et rAchiRemplace, la première ajoute la semaine à l'historique (dans l'hypothèse où elle n'y est pas déjà) et la seconde réactualise les données (dans l'hypothèse contraire). On affiche le formulaire fArchives, juste en dessous du bouton, en appliquant la méthode décrite par Arkham46 dans cette contribution.

IX - Le formulaire fArchives

Il a comme source la table tArchiveSRA triée dans l'ordre décroissant.

IX-A - Remarque sur le tri

donnerait par exemple ceci :

En effet le texte « 52/13 » est plus grand que « 5/14 » lui-même plus grand que « 49/14 », alors que l'on veut l'ordre chronologique décroissant !

Qu'à cela ne tienne, voici une fonction qui va nous dépanner :

1. Public Function ArgTriSem(Semaine As String) As Integer

2. Dim t() As String

3. t = Split(Semaine, "/")

4. ArgTriSem = t(1) & Format(t(0), "00")

5. End Function

L'instruction de la ligne 4 transforme « 52/13 » en « 13/52 » et « 5/14 » en « 14/05 ».

Si nous construisons la source comme ceci :

on obtient l'ordre souhaité :

X - Téléchargement

La base de données au format Access 2000 se trouve ici.

XI - Remerciements

Particulièrement à Saumon80 qui m'a donné l'idée de cet article.

Merci aussi à Zoom61 et jpcheck pour la relecture technique et à Milkoseck pour l'orthographe.