I. Le contexte▲
Il s'agit de mettre en place quelques formulaires pour que l'équipe du CRC (Centre de Relation Clients) puisse :
- enregistrer une demande de réservation ;
- enregistrer l'annulation éventuelle d'une réservation ;
- notifier ces réservations et annulations à la compagnie de taxis ;
- vérifier la concordance entre les réservations comptabilisées et la facturation adressée par les compagnies de taxis.
II. Modéliser l'offre de transports▲
II-A. Première étape : le réseau▲
Il se compose de lignes et chaque ligne comporte plusieurs arrêts.
Par exemple,
- la ligne : 24 FONTAINE S/ SOMME - LONGPRE LES CORPS SAINTS,
- comporte les arrêts : 1) FONTAINE S/SOMME, 2) LONG LE CATELET et 3) LONGPRE LES CORPS SAINTS.
II-A-1. Deux tables pour enregistrer ces données ▲
II-A-2. Un formulaire père-fils pour l'encodage▲
Si la technique des formulaires pères/fils ne vous est pas familière, consultez ce tutoriel : Comment classer les données dans des tables liées et construire un formulaire père/fils. |
II-B. Deuxième étape : préciser les horaires▲
Pour chaque ligne, il faut annoncer :
- les jours de circulation ;
- le sens (aller ou retour) ;
- l'heure de chaque arrêt.
Nous intégrons ici la notion de compagnie de taxis qui assure le service.
II-B-1. Le modèle de données s'étoffe un peu▲
III. Encoder l'horaire d'une ligne : formulaire fEncoHorLigne▲
III-A. À l'ouverture▲
fEncoHorLigne se présente comme ceci :
C'est un formulaire père avec trois fils : sfEncoHorArrets, sfEncoHorValid et sfEncoHorValidSauf.
Chacun a comme source la table indiquée sur la figure ci-dessus.
À l'ouverture, ces tables sont réinitialisées.
Private
Sub
Form_Open
(
Cancel As
Integer
)
'Purger tEncoHorArrets,tEncoHorValid, tEncoHorValidSauf
DoCmd.SetWarnings
False
DoCmd.RunSQL
"Delete * From tEncoHorArrets"
DoCmd.RunSQL
"Delete * From tEncoHorValid"
DoCmd.RunSQL
"Delete * From tEncoHorValidSauf"
DoCmd.SetWarnings
True
Me.Refresh
End
Sub
Private
Sub
Form_Current
(
)
Dim
ctl As
Control
'Réinitialiser
Me.cboLigne
=
""
Me.CaSens
=
-
1
Me.txtNumCirculation
=
Null
Me.cboTaxis
=
Null
For
Each
ctl In
Me.Controls
If
ctl.Name
Like "chk*"
Then
ctl =
-
1
Next
ctl
Me.Refresh
End
Sub
Le formulaire va permettre à l'utilisateur de compléter ces quatre tables de transit : un clic sur provoquera ensuite le transfert vers les tables cibles (tHorLignes, tHorArrets, tHorValid et tHorValidSauf).
III-B. L'utilisateur choisit une ligne dans la liste déroulante▲
Private
Sub
cboLigne_AfterUpdate
(
)
Dim
strSQL As
String
Me.Refresh
'Purger tEncoHorArrets,tEncoHorValid, tEncoHorValidSauf
DoCmd.SetWarnings
False
DoCmd.RunSQL
"Delete * From tEncoHorArrets"
DoCmd.RunSQL
"Delete * From tEncoHorValid"
DoCmd.RunSQL
"Delete * From tEncoHorValidSauf"
'Créer la structure des arrêts
DoCmd.OpenQuery
"rStructureArrets"
DoCmd.SetWarnings
True
Me.CTNRsfEncoHorArrets.Form.Requery
End
Sub
Si l'utilisateur veut encoder un horaire pour le sens Retour, il coche le bouton radio et l'ordre des arrêts s'inverse :
Private
Sub
CaSens_AfterUpdate
(
)
'ordonner le sous-formulaire
If
Me.CaSens
=
-
1
Then
Me.CTNRsfEncoHorArrets.Form.RecordSource
=
_
"SELECT * FROM tEncoHorArrets ORDER BY EncoSequence;"
Else
Me.CTNRsfEncoHorArrets.Form.RecordSource
=
_
"SELECT * FROM tEncoHorArrets ORDER BY EncoSequence DESC;"
End
If
End
Sub
Private
Sub
CaSens_AfterUpdate
(
)
'ordonner le sous-formulaire
If
Me.CaSens
=
-
1
Then
Me.CTNRsfEncoHorArrets.Form.RecordSource
=
_
"SELECT * FROM tEncoHorArrets ORDER BY EncoSequence;"
Else
Me.CTNRsfEncoHorArrets.Form.RecordSource
=
_
"SELECT * FROM tEncoHorArrets ORDER BY EncoSequence DESC;"
End
If
End
Sub
III-C. L'utilisateur complète les données▲
III-D. Quand toutes les données sont encodées▲
Le clic sur le bouton déclenche le code suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
Private
Sub
Btjouter_Click
(
)
Dim
lngCle As
Long
Dim
oRst As
DAO.Recordset
Dim
oQry As
DAO.QueryDef
Dim
dHeure As
Date
Me.Refresh
'Champs obligatoires présents
If
IsNull
(
Me.txtNumCirculation
) +
IsNull
(
Me.cboTaxis
) <>
0
Then
MsgBox
"Un champ obligatoire n'est pas rempli !"
, vbCritical
Exit
Sub
End
If
'Vérifer que cette circulation n'est pas déjà répertoriée
If
DCount
(
"*"
, "tHorLignes"
, "tLignesFK = "
&
Me.cboLigne
_
&
" AND NumCirculation ="
&
Me.txtNumCirculation
) <>
0
Then
MsgBox
"Cette circulation est déjà répertoriée"
, vbCritical
Exit
Sub
End
If
'Vérifier ordre chronologique des arrêts
Set
oRst =
Me.CTNRsfEncoHorArrets.Form.RecordsetClone
oRst.MoveFirst
dHeure =
oRst
(
"EncoArretHeure"
)
oRst.MoveNext
Do
Until
oRst.EOF
If
oRst
(
"EncoArretHeure"
) <=
dHeure Then
MsgBox
"Chronologie incohérente !"
, vbCritical
Exit
Sub
End
If
dHeure =
oRst
(
"EncoArretHeure"
)
oRst.MoveNext
Loop
'Libérer oRst
oRst.Close
Set
oRst =
Nothing
'Vérifier qu'au moins une période de validité est spécifiée
If
DCount
(
"*"
, "tEncoHorValid"
) =
0
Then
MsgBox
"La période de validité manque !"
, vbCritical
Exit
Sub
End
If
'Ajouter dans tHorLignes
DoCmd.SetWarnings
False
DoCmd.OpenQuery
"rAJOUTtHorLignes"
'Récupérer le N° attribué à tHorLignesPK
lngCle =
DMax
(
"tHorLignesPK"
, "tHorlignes"
)
'Ajouter dans tHorArrets
Set
oQry =
CurrentDb.QueryDefs
(
"rAJOUTtHorArrets"
)
oQry.Parameters
(
"Ligne"
) =
lngCle
oQry.Execute
'Ajouter dans tHorValid
Set
oQry =
CurrentDb.QueryDefs
(
"rAJOUTtHorValid"
)
oQry.Parameters
(
"Ligne"
) =
lngCle
oQry.Execute
'Ajouter dans tHorValidSauf
Set
oQry =
CurrentDb.QueryDefs
(
"rAJOUTtHorValidSauf"
)
oQry.Parameters
(
"Ligne"
) =
lngCle
oQry.Execute
'Libérer oQry
Set
oQry =
Nothing
DoCmd.SetWarnings
True
'Réinitialiser pour un suivant éventuel
Call
Form_Open
(
0
)
Call
Form_Current
End
Sub
Commentaires du code
7 - 38 : on vérifie que les données sont cohérentes.
41 - 55 : on reporte le contenu des tables tEncoHor* dans les tables tHor*.
IV. Encoder une réservation▲
IV-A. À l'ouverture du formulaire : choix du client ▲
Private
Sub
Form_Open
(
Cancel As
Integer
)
Me.cboClient.SetFocus
Me.cboClient.Dropdown
End
Sub
La liste des clients se déploie.
Le choix du client provoque le déploiement de la liste des lignes :
Private
Sub
cboClient_AfterUpdate
(
)
Me.cboLigne.SetFocus
Me.cboLigne.Dropdown
End
Sub
IV-B. Choix du trajet▲
Quand l'opérateur choisit une ligne, de deux choses l'une :
- soit cette ligne ne comporte que deux arrêts : le point de départ et le point de destination sont donc déterminés d'office ;
- soit la ligne comporte des arrêts intermédiaires, la liste de ceux-ci est proposée au choix, à remarquer que le terminus ne fait pas partie des départs possibles.
Quand le départ a été choisi, à nouveau de deux choses l'une :
- soit l'arrêt suivant est le terminus, c'est donc la destination ;
- soit ce n'est pas le cas et une liste contenant les arrêts suivants est proposée à l'utilisateur.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
Private
Sub
cboLigne_AfterUpdate
(
)
Dim
oQry As
DAO.QueryDef
'Libérer Me.cboDepart.RowSource et cboDestination.RowSource
Me.cboDepart.RowSource
=
""
Me.cboDepart
=
""
Me.cboDestination.RowSource
=
""
Me.cboDestination
=
""
'Garnir la source de CboDepart
DoCmd.SetWarnings
False
DoCmd.RunSQL
"Delete * From tCboDepart"
Set
oQry =
CurrentDb.QueryDefs
(
"rReservationGarnirCboDepart"
)
oQry.Parameters
(
"Ligne"
) =
Me.cboLigne
oQry.Parameters
(
"Sens"
) =
Me.cboLigne.Column
(
2
)
oQry.Execute
'Éliminer le terminus en tant que point de départ
DoCmd.OpenQuery
"rReservationEliminerTerminusDepart"
'Affecter tCboDepart à cboDepart
Me.cboDepart.RowSource
=
"SELECT * FROM tCboDepart ORDER BY ArretHeure;"
'S'il n'y a qu'un seul élément, on l'affecte.Sinon on déploie.
If
Me.cboDepart.ListCount
>
1
Then
Me.cboDepart.SetFocus
Me.cboDepart.Dropdown
GoTo
Sortie
Else
Me.cboDepart
=
Me.cboDepart.ItemData
(
0
)
Call
cboDepart_AfterUpdate
End
If
Sortie
:
DoCmd.SetWarnings
True
Set
oQry =
Nothing
End
Sub
Private
Sub
cboDepart_AfterUpdate
(
)
'Libérer cboDestination.RowSource
Me.cboDestination.RowSource
=
""
Me.cboDestination
=
""
DoCmd.SetWarnings
False
'Créer la table tCboDestination
DoCmd.OpenQuery
"rReservationGarnirCboDestination"
'Affecter tCboDestination à cboDestination
Me.cboDestination.RowSource
=
"SELECT tArretsFK, ArretNom, ArretHeure "
_
&
"FROM tCboDestination ORDER BY ArretHeure;"
'S'il n'y a qu'un seul élément, on l'affecte. Sinon on déploie.
If
Me.cboDestination.ListCount
>
1
Then
Me.cboDestination.SetFocus
Me.cboDestination.Dropdown
GoTo
Sortie
Else
Me.cboDestination
=
Me.cboDestination.ItemData
(
0
)
Call
cboDestination_AfterUpdate
End
If
Sortie
:
DoCmd.SetWarnings
True
End
Sub
Private
Sub
cboDestination_AfterUpdate
(
)
'Si les champs utiles sont remplis, construire la structure d'une réservation
If
IsNull
(
Me.cboClient
) +
IsNull
(
Me.cboLigne
) _
+
IsNull
(
Me.cboDepart
) +
IsNull
(
Me.cboDestination
) =
0
_
Then
Call
ConstruireReservation
Me.EtiCochez.Visible
=
True
Me.EtiNbre.Visible
=
True
Me.EtiObservations.Visible
=
True
End
Sub
Commentaires du code
4 - 7 : on réinitialise les zones de liste Départ et Destination.
11 - 15 : on confectionne la liste des arrêts de la ligne en exécutant la requête paramétrée :
17 : et on élimine ensuite le terminus :
34 - 55 : cas où plusieurs points de départ étaient possibles => confection de la liste des points de destination offerts.
57 - 64 : quand la destination est aussi déterminée,
- on vérifie que les champs obligatoires sont complétés (59 - 60),
- on déclenche la Sub ConstruireReservation (61), elle est décrite au § suivant,
- on rend visibles les étiquettes pour la liste qui va s'afficher (62 - 64).
IV-C. Comptabilisation de la réservation▲
Quand l'opérateur a coché les dates demandées par le client, un clic sur le bouton « Comptabiliser les réservations » :
- un e-mail décrivant le détail est envoyé automatiquement à la compagnie de taxis qui opère sur la ligne ;
- la table tReservations est complétée.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
Private
Sub
BtCompta_Click
(
)
Dim
oCtl As
Control
Dim
oQry As
DAO.QueryDef
Me.Refresh
'Vérif présence du code opérateur
If
IsNull
(
Me.cboOperateur
) Then
MsgBox
"Opérateur ?"
, vbCritical
Me.cboOperateur.SetFocus
Me.cboOperateur.Dropdown
Exit
Sub
End
If
'Envoi de l'e-mail de réservation
Call
EnvoiMailRes
'Enregistrement de la réservation
DoCmd.SetWarnings
False
Set
oQry =
CurrentDb.QueryDefs
(
"rReservationCompta"
)
oQry.Parameters
(
"Client"
) =
Me.cboClient
oQry.Parameters
(
"Ligne"
) =
Me.cboLigne
oQry.Parameters
(
"Operateur"
) =
Me.cboOperateur
oQry.Execute
DoCmd.SetWarnings
True
Set
oQry =
Nothing
Me.Section
(
"Détail"
).Visible
=
False
For
Each
oCtl In
Me.Controls
If
oCtl.Name
Like "Eti*"
Then
oCtl.Visible
=
False
If
oCtl.Name
Like "Cbo*"
Then
oCtl =
Null
Next
oCtl
Me.cboClient.SetFocus
Me.cboClient.Dropdown
End
Sub
Commentaires du code
14 : la routine EnvoiMailRes est décrite plus loin dans ce tutoriel.
17 - 21 : on exécute la requête rReservationCompta
24 - 30 : on remet le formulaire en forme pour la réservation suivante.
V. La routine ConstruireReservation▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
Public
Sub
ConstruireReservation
(
)
Dim
i As
Integer
Dim
oRst As
DAO.Recordset
Dim
oRst2 As
DAO.Recordset
Dim
DateValide As
Date
Dim
oQry As
DAO.QueryDef
DoCmd.SetWarnings
False
'----------------------------------------------------'
'Garnir tUneReservation avec toutes les dates valides'
'----------------------------------------------------'
'Vidanger tUneReservation
'------------------------
DoCmd.RunSQL
"Delete * from tUneReservation;"
'Garnir tDatesValides pour les périodes de validation
'----------------------------------------------------
Set
oRst =
CurrentDb.OpenRecordset
(
"SELECT ValidDu, ValidAu FROM tHorValid "
_
&
"WHERE tHorLignesFK="
&
Me.cboLigne
&
";"
)
Set
oQry =
CurrentDb.QueryDefs
(
"rReservationDatesFutures"
)
Do
While
Not
oRst.EOF
If
oRst
(
"ValidAu"
) >
Date
Then
DateValide =
IIf
(
oRst
(
"ValidDu"
) >=
Date
, oRst
(
"ValidDu"
), Date
+
1
)
Do
While
DateValide <=
oRst
(
"ValidAu"
)
oQry.Parameters
(
"LaDate"
) =
DateValide
oQry.Parameters
(
"Depart"
) =
Me.cboDepart
oQry.Parameters
(
"Destination"
) =
Me.cboDestination
oQry.Execute
DateValide =
DateValide +
1
Loop
End
If
oRst.MoveNext
Loop
'Supprimer les jours de la semaine non servis
'--------------------------------------------
'Chercher la liste des jours non servis
Set
oRst =
CurrentDb.OpenRecordset
(
"tHorLignes"
, dbOpenDynaset)
oRst.FindFirst
"[tHorlignesPK] = "
&
Me.cboLigne
' faire défiler tous les jours d'une semaine pour accéder aux colonnes de tHorLignes
Set
oQry =
CurrentDb.QueryDefs
(
"rReservationJoursSemSans"
)
For
i =
1
To
7
If
oRst
(
Format
(
Date
+
i, "dddd"
)) =
False
Then
oQry.Parameters
(
"JoursSemSans"
) =
Format
(
Date
+
i, "dddd"
)
oQry.Execute
End
If
Next
i
'Supprimer les jours ordinaires fériés
'-------------------------------------
Set
oQry =
CurrentDb.QueryDefs
(
"rReservationSuppressionJF"
)
oQry.Parameters
(
"SaufFete"
) =
oRst
(
"SaufFete"
)
oQry.Execute
'Ajouter les dates fériées si roule tous les jours fériés
'--------------------------------------------------------
If
oRst
(
"TousLesFeries"
) =
True
Then
Set
oQry =
CurrentDb.QueryDefs
(
"rReservationTousJrsFeries"
)
'Rechercher les périodes de validité de cette ligne
Set
oRst2 =
CurrentDb.OpenRecordset
(
"SELECT ValidDu, ValidAu FROM tHorValid WHERE tHorLignesFK="
&
Me.cboLigne
&
";"
)
'Ajouter les dates fériées valides à venir
Do
While
Not
oRst2.EOF
If
Date
>=
oRst2
(
"ValidDu"
) And
Date
<=
oRst2
(
"ValidAu"
) Then
oQry.Parameters
(
"ValidAu"
) =
oRst2
(
"ValidAu"
)
oQry.Parameters
(
"Depart"
) =
Me.cboDepart
oQry.Parameters
(
"Destination"
) =
Me.cboDestination
oQry.Execute
End
If
oRst2.MoveNext
Loop
End
If
'Supprimer les dates exclues
'---------------------------
'Rechercher les dates exclues de cette ligne
Set
oRst2 =
CurrentDb.OpenRecordset
(
"SELECT SaufDate FROM tHorValidSauf WHERE tHorLignesFK="
&
Me.cboLigne
&
";"
)
Set
oQry =
CurrentDb.QueryDefs
(
"rReservationSuppressionDatesExclues"
)
Do
While
Not
oRst2.EOF
oQry.Parameters
(
"DateExclue"
) =
oRst2
(
"SaufDate"
)
oQry.Execute
oRst2.MoveNext
Loop
Sortie
:
Me.Section
(
"Détail"
).Visible
=
True
Me.Requery
DoCmd.SetWarnings
True
oRst.Close
Set
oRst =
Nothing
On
Error
Resume
Next
oRst2.Close
's'il n'a pas été ouvert, une erreur 91 sera générée
Set
oRst2 =
Nothing
End
Sub
Commentaires du code
L'idée est de procéder par étapes pour peupler la table tUneReservation.
13 : on la vidange.
18 - 31 : pour chaque période de validité, on ajoute toutes les dates encore à venir.
Chaque date sera unique, même si en cours de processus on tente d'ajouter plusieurs fois une même date (des périodes de validité qui se chevaucheraient par exemple)
33 - 45 : on supprime les dates qui correspondent aux jours non servis.
36 - 37 : on accède à la ligne de la table tHorlignes qui contient les données de la ligne en cours de traitement. Ceci pour trouver la valeur (Vrai ou Faux) affectée aux colonnes « Lundi », « Mardi », etc.
39 - 45 : on boucle sept fois sur le format d'une date quelconque (ici celle d'aujourd'hui) pour obtenir chaque jour de la semaine et sept fois exécuter la requête rReservationJoursSemSans :
46 - 50 : on élimine ici les dates des jours qui ne sont pas servis s'ils sont fériés
en exécutant la requête rReservationJF
53 - 68 : on ajoute les dates de tous les jours fériés si .
54 - 57 : si la case est cochée, on se crée un jeu d'enregistrements avec les périodes de validité de cette ligne.
59 - 68 : si aujourd'hui est situé à l'intérieur d'une période de validité, on ajoute les dates fériées encore à venir dans cette période :
69 - 78 : reste à supprimer les dates exclues éventuelles :
VI. La routine EnvoiMailRes()▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
Private
Sub
EnvoiMailRes
(
)
Dim
oOutlook As
Outlook.Application
Dim
oMonMessage As
Object
Dim
oRST As
DAO.Recordset
Dim
strobjet As
String
Dim
strDestinataire As
String
Dim
strMessage As
String
Dim
iRet As
Integer
Dim
bolOutLookOuvert As
Boolean
Dim
strDetail As
String
'Tentative pour récupérer une session Outlook éventuellement déjà ouverte
On
Error
Resume
Next
'S'il n'y a pas de session ouverte, l'instruction suivante va provoquer une erreur (N° 429)
Set
oOutlook =
GetObject
(
, "Outlook.Application"
)
'Traitement de l'erreur éventuelle : nous devons ouvrir une session Outlook
If
Err
.Number
=
429
Then
iRet =
Shell
(
"C:\Program Files (x86)\Microsoft Office\Office14\OUTLOOK.EXE"
, vbHide)
Set
oOutlook =
New
Outlook.Application
bolOutLookOuvert =
True
'ceci pour permettre de refermer in fine Outlook que nous avons ouvert
End
If
On
Error
GoTo
GestionErreur
'Construire les composantes du message
strDestinataire =
DLookup
(
"TaxiMail"
, "rMailResTaxi"
)
strobjet =
"Réservation "
'Construction du détail des réservations date par date
'Recréer tMailRes et son recordset
DoCmd.SetWarnings
False
DoCmd.OpenQuery
"rMailRes"
DoCmd.SetWarnings
True
Set
oRST =
CurrentDb.OpenRecordset
(
"tMailRes"
)
oRST.MoveFirst
Do
While
Not
oRST.EOF
strDetail =
strDetail &
oRST
(
"ResDate"
) &
" pour "
_
&
oRST
(
"NbrePers"
) &
" personne(s) "
&
oRST
(
"Observations"
) _
&
Chr
(
10
)
oRST.MoveNext
Loop
strMessage =
"Bonjour,"
&
Chr
(
13
) &
Chr
(
10
) _
&
Chr
(
13
) &
Chr
(
10
) _
&
"Taxi commandé : "
&
Chr
(
13
) &
Chr
(
10
) _
&
DLookup
(
"CoordTaxi"
, "rMailResTaxi"
) &
Chr
(
13
) &
Chr
(
10
) _
&
" "
&
Chr
(
13
) &
Chr
(
10
) _
&
"Parcours : "
&
Me.cboDepart.Column
(
1
) &
" "
&
Format
(
Me.cboDepart.Column
(
2
), "hh:mm"
) _
&
" => "
&
Me.cboDestination.Column
(
1
) &
" "
&
Format
(
Me.cboDestination.Column
(
2
), "hh:mm"
) _
&
Chr
(
13
) &
Chr
(
10
) _
&
"Circulation : "
&
Me.
[cboLigne].Column
(
3
) &
Chr
(
13
) &
Chr
(
10
) _
&
" "
&
Chr
(
13
) &
Chr
(
10
) _
&
"Pour le client : "
&
Chr
(
13
) &
Chr
(
10
) _
&
DLookup
(
"CoordClient"
, "rMailResClient"
) &
Chr
(
13
) &
Chr
(
10
) _
&
Chr
(
13
) &
Chr
(
10
) _
&
"Pour les dates suivantes : "
&
Chr
(
13
) &
Chr
(
10
) _
&
strDetail _
&
Chr
(
13
) &
Chr
(
10
) _
&
"Réservation enregistrée par "
&
Me.cboOperateur.Column
(
1
) &
Chr
(
13
) &
Chr
(
10
) _
&
Chr
(
13
) &
Chr
(
10
) _
&
"Bien à vous."
'Envoi de l'e-mail
Set
oMonMessage =
oOutlook.CreateItem
(
0
) 'ouvrir une structure de message
oMonMessage.To
=
strDestinataire
oMonMessage.Subject
=
strobjet
oMonMessage.Body
=
strMessage
oMonMessage.Send
Sleep 1000
'pause de 1 sec pour l'envoi
'Libérer les variables
Set
oOutlook =
Nothing
Set
oMonMessage =
Nothing
oRST.Close
Set
oRST =
Nothing
'si on a dû ouvrir une session Outlook, on la referme (sinon, on laisse la main à l'utilisateur)
If
bolOutLookOuvert =
True
Then
KillApp (
iRet)
GestionErreur
:
Select
Case
Err
.Number
Case
0
' pas d'erreur
Case
Else
MsgBox
"Erreur dans EnvoiMailRes N° "
&
Err
.Number
&
" "
&
Err
.Description
End
Select
End
Sub
Commentaires du code
11 -20 : ouverture d'une session Outlook (si nécessaire).
22 - 56 : construction du message.
26 - 37 : on construit la liste des réservations avec retour à la ligne après chaque item.
41 : pour les coordonnées du taxi : la requête rMailResTaxi
Remarquez l'insertion de Car
(
13
) &
Car
(
10
) dans la concaténation du texte qui provoquera un retour à la ligne.
49 : idem pour les coordonnées du client.
58 - 62 : on déclenche l'envoi du message.
63 : on temporise une seconde pour laisser le temps à Outlook.
Cette instruction nécessite que la fonction Sub Sleep soit déclarée dans un module (dans notre cas mFonctions) :
Public
Declare
Sub
Sleep Lib
"kernel32"
(
ByVal
dwMilliseconds As
Long
)
65 - 68 : on libère les variables.
70 : on referme Outlook, si nous avions dû ouvrir la session.
VII. Encoder une annulation▲
VII-A. À l'ouverture du formulaire : choix du client▲
Seuls les clients pour lesquels une annulation est encore possible s'affichent. Contenu de la zone de liste :
VII-B. Quand le client est désigné▲
Ses réservations encore à venir s'affichent :
Private
Sub
cboClient_AfterUpdate
(
)
Dim
sSQL As
String
'Purge de tAnnulation
DoCmd.SetWarnings
False
DoCmd.RunSQL
" Delete * From tAnnulation;"
'Créer la table des annulations possibles
DoCmd.OpenQuery
"rCreatAnnulation"
DoCmd.SetWarnings
True
'Afficher
Me.EtiCochez.Visible
=
True
Me.EtiDepuis.Visible
=
True
Me.Section
(
"Détail"
).Visible
=
True
Me.Requery
End
Sub
VII-C. Comptabilisation de l'annulation▲
Private
Sub
BtEnregistrer_Click
(
)
Me.Refresh
'Vérif présence opérateur
If
IsNull
(
Me.cboOperateur
) Then
MsgBox
"Quel opérateur ?"
Me.cboOperateur.SetFocus
Me.cboOperateur.Dropdown
Exit
Sub
End
If
'Vérif au moins une annulation
If
DCount
(
"*"
, "tAnnulation"
, "Annul = true"
) =
0
Then
MsgBox
"Vous n'avez coché aucun poste !"
Exit
Sub
End
If
'Envoyer le mail d'annulation
Call
EnvoiMailAnnul
'Enregistrer dans tReservations
DoCmd.SetWarnings
False
DoCmd.OpenQuery
"rAnnulation"
DoCmd.SetWarnings
True
'Fermer le formulaire
DoCmd.Close
acForm, Me.Name
End
Sub
VII-D. La routine EnvoiMailAnnul()▲
Private
Sub
EnvoiMailAnnul
(
)
Dim
objOutlook As
Outlook.Application
Dim
objMonMessage As
Object
Dim
objRST As
DAO.Recordset
Dim
strobjet As
String
Dim
strDestinataire As
String
Dim
strMessage As
String
Dim
iRet As
Integer
Dim
bolOutLookOuvert As
Boolean
Dim
strDetail As
String
'Tentative pour récupérer une session Outlook éventuellement déjà ouverte
On
Error
Resume
Next
'S'il n'y a pas de session ouverte, l'instruction suivante va provoquer une erreur (N° 429)
Set
objOutlook =
GetObject
(
, "Outlook.Application"
)
'Traitement de l'erreur éventuelle : nous devons ouvrir une session Outlook
If
Err
.Number
=
429
Then
iRet =
Shell
(
"C:\Program Files (x86)\Microsoft Office\Office14\OUTLOOK.EXE"
, vbHide)
Set
objOutlook =
New
Outlook.Application
bolOutLookOuvert =
True
'ceci pour permettre de refermer in fine Outlook que nous avons ouvert
End
If
On
Error
GoTo
GestionErreur
'Recréer tMailAnnul
DoCmd.SetWarnings
False
DoCmd.OpenQuery
"rMailAnnul"
DoCmd.SetWarnings
True
'Construire les composantes du message
Set
objRST =
CurrentDb.OpenRecordset
(
"tMailAnnul"
)
objRST.MoveFirst
Do
While
Not
objRST.EOF
strDestinataire =
objRST
(
"TaxiMail"
)
strobjet =
"Annulation de réservation(s) "
strDetail =
objRST
(
"ResDate"
) &
" pour "
&
objRST
(
"NbrePers"
) &
" personne(s) "
&
objRST
(
"Observations"
) &
Chr
(
10
) &
Chr
(
13
)
strMessage =
"Bonjour,"
&
Chr
(
13
) &
Chr
(
10
) _
&
" "
&
Chr
(
13
) &
Chr
(
10
) _
&
"Taxi commandé : "
&
Chr
(
13
) &
Chr
(
10
) _
&
objRST
(
"Taxi"
) &
Chr
(
13
) &
Chr
(
10
) _
&
" "
&
Chr
(
13
) &
Chr
(
10
) _
&
"Parcours : "
&
objRST
(
"Depart"
) &
" => "
&
objRST
(
"Destination"
) &
Chr
(
13
) &
Chr
(
10
) _
&
" "
&
Chr
(
13
) &
Chr
(
10
) _
&
"Circulation : "
&
objRST
(
"NumCircul"
) &
Chr
(
13
) &
Chr
(
10
) _
&
" "
&
Chr
(
13
) &
Chr
(
10
) _
&
"Pour le client : "
&
Chr
(
13
) &
Chr
(
10
) _
&
objRST
(
"Client"
) &
Chr
(
13
) &
Chr
(
10
) _
&
" "
&
Chr
(
13
) &
Chr
(
10
) _
&
strDetail _
&
"Annulation enregistrée par "
&
objRST
(
"Operateur"
) &
Chr
(
13
) &
Chr
(
10
) _
&
Chr
(
13
) &
Chr
(
10
) _
&
"Bien à vous."
'Envoi de l'e-mail
Set
objMonMessage =
objOutlook.CreateItem
(
0
) 'ouvrir une structure de message
objMonMessage.To
=
strDestinataire
objMonMessage.Subject
=
strobjet
objMonMessage.Body
=
strMessage
objMonMessage.Send
Sleep 1000
'pause de 1 sec pour l'envoi
objRST.MoveNext
Loop
'Libérer les variables
Set
objOutlook =
Nothing
Set
objMonMessage =
Nothing
'si on a dû ouvrir une session Outlook, on la referme (sinon, on laisse la main à l'utilisateur)
If
bolOutLookOuvert =
True
Then
KillApp (
iRet)
End
If
GestionErreur
:
Select
Case
Err
.Number
Case
0
' pas d'erreur
Case
Else
MsgBox
"Erreur dans EnvoiMailAnnul N° "
&
Err
.Number
&
" "
&
Err
.Description
End
Select
End
Sub
Ce code est calqué sur celui de la sous-routine EnvoiMailRes() décrite plus haut.
VIII. Vérifier la facturation▲
Ce qui est utile pour vérifier la facturation reçue des taxis se limite à une liste des réservations non annulées, en permettant le choix :
- de la compagnie de taxis ;
- du type de tarif (Jour : départ entre 7 et 19 heures ; Nuit sinon) ;
- d'une période.
Une procédure en deux temps :
- un formulaire avec recherche multicritère pour cibler les courses ;
- un état pour structurer la sélection.
VIII-A. Le formulaire fVerif▲
C'est un formulaire avec recherche multicritère.
VIII-A-1. À l'ouverture▲
VIII-A-2. Après ciblage▲
VIII-A-3. La source : rVerif▲
SELECT
tTaxis.taxiNom, IIf(
Format
(
[ArretHeure]
,"hh"
)<
"07"
,"Nuit"
,IIf(
Format
(
[ArretHeure]
,"hh"
)>
"19"
,"Nuit"
,IIf(
[ResDate]
=
[DateFeriee]
,"Nuit"
,IIf(
Weekday
(
[ResDate]
)=
1
,"Nuit"
,"Jour"
))))
AS
Mode
, [LigneNom]
&
"/"
&
[NumCirculation]
AS
Circulation, tReservations.ResDate, tClients.ClientNom, tReservations.NbrePers, tReservations.Observations, tJoursFeries.dateFeriee, tReservations.tHorLignesFK, tLignes.LigneNom, tHorLignes.NumCirculation, tHorArrets.ArretHeure
FROM
tTaxis INNER
JOIN
(
tLignes INNER
JOIN
((
tClients INNER
JOIN
((
tReservations INNER
JOIN
tHorLignes ON
tReservations.tHorLignesFK =
tHorLignes.tHorLignesPK)
LEFT
JOIN
tJoursFeries ON
tReservations.ResDate =
tJoursFeries.dateFeriee)
ON
tClients.tClientsPK =
tReservations.tClientsFK)
INNER
JOIN
tHorArrets ON
tHorLignes.tHorLignesPK =
tHorArrets.tHorLignesFK)
ON
tLignes.tLignesPK =
tHorLignes.tLignesFK)
ON
tTaxis.tTaxisPK =
tHorLignes.tTaxisFK
WHERE
(((
tTaxis.taxiNom)
Like
"*"
&
[Formulaires]
![fVerif]
![FiltreTaxi]
&
"*"
)
AND
((
IIf(
Format
(
[ArretHeure]
,"hh"
)<
"07"
,"Nuit"
,IIf(
Format
(
[ArretHeure]
,"hh"
)>
"19"
,"Nuit"
,IIf(
[ResDate]
=
[DateFeriee]
,"Nuit"
,IIf(
Weekday
(
[ResDate]
)=
1
,"Nuit"
,"Jour"
)))))
Like
"*"
&
[Formulaires]
![fVerif]
![FiltreMode]
&
"*"
)
AND
((
tReservations.ResDate)>=
IIf(
IsNull
(
[Formulaires]
![fVerif]
![FiltreDu]
)
,#1/1/1900#
,[Formulaires]
![fVerif]
![FiltreDu]
)
And
(
tReservations.ResDate)<=
IIf(
IsNull
(
[Formulaires]
![fVerif]
![FiltreAu]
)
,#1/1/2100#
,[Formulaires]
![fVerif]
![FiltreAu]
))
AND
((
tReservations.AnnulDate)
Is
Null
)
AND
((
tHorArrets.Sequence)=
1
))
ORDER
BY
tTaxis.taxiNom, IIf(
Format
(
[ArretHeure]
,"hh"
)<
"07"
,"Nuit"
,IIf(
Format
(
[ArretHeure]
,"hh"
)>
"19"
,"Nuit"
,IIf(
[ResDate]
=
[DateFeriee]
,"Nuit"
,IIf(
Weekday
(
[ResDate]
)=
1
,"Nuit"
,"Jour"
))))
, [LigneNom]
&
"/"
&
[NumCirculation]
, tReservations.ResDate, tClients.ClientNom;
|
VIII-B. L'état eVerif▲
IX. Téléchargement▲
L'application au format Access2000 peut être téléchargée à cette adresse.
X. Remerciements▲
Merci à jbachet qui m'a expliqué en détail les aspects métier dans cette discussion.
Merci à jlliagre pour ses remarques.
Merci à f-leb pour la correction orthographique.