I. Contexte▲
Il s'agit d'une association caritative qui recueille des fonds sous la forme de cotisations, de parrainages et des dons ponctuels, pour les redistribuer à des personnes âgées en grande détresse, sous forme de dons alimentaires et vestimentaires, d'accès aux soins médicaux et à l'hygiène, d'hébergement ponctuel pour des seniors sans famille et sans abri.
II. Le plan comptable▲
On se réfère au plan comptable des associations (loi 1901) dont la liste complète peut être consultée sur ce site : Loi1901.com.
Pour ce qui concerne les besoins de l'exemple, nous avons retenu seulement ceux-ci :
- Une case cochée dans la colonne « À l'actif » signifie que ce compte de bilan (N° 1000 à 5999) figure à l'actif, les comptes de bilan non cochés s'inscriront au passif.
- Les colonnes « OKii » servent à indiquer si le compte peut être mouvementé pour enregistrer des opérations respectivement dans les livres banque, caisse, achats et opérations diverses.
- Les quelques comptes fournisseurs individuels sont intégrés dans le plan comptable pour des raisons pratiques (leur solde individuel est donc directement accessible). Pour la confection du bilan, ils sont globalisés dans le compte 4010 Fournisseurs.
III. Les différents formulaires pour saisir les événements comptables▲
III-A. Les opérations au compte Banque▲
III-B. Les opérations au compte Caisse▲
Remarque
Prélever des fonds dans la caisse pour les verser en banque se comptabilise :
- d'abord dans le journal de caisse, le jour du versement :
Virements de fonds
à Caisse
- ensuite, dans le journal de banque, quand on reçoit l'extrait de la banque :
Banque
à Virements de fonds
III-C. Le facturier des achats ▲
Les factures des fournisseurs sont numérotées et conservées dans un classeur.
Remarque : l'association qui sert d'exemple n'est pas assujettie à la TVA.
III-D. Les opérations diverses▲
IV. Quelques exemples de comptabilisations pour enregistrer des événements particuliers▲
-
Un artisan offre à l'association un lot d'objets qu'elle pourra mettre en vente lors de son marché de Noël, valeur estimée : 2000 € :
-
Un donateur offre un projecteur (750 €) avec une cartouche d'encre (45 €) à l'association :
-
Amortissement du projecteur reçu (par exemple en cinq ans) :
-
L'association achète un ordinateur (499 €) :
-
Amortissement de l'ordinateur acheté (par exemple en trois ans) :
- L'association perçoit les intérêts sur son livret bleu :
Note pour les lecteurs qui recourent aux services de
Mossack Fonseca : dans votre cas, il vaut mieux ne rien comptabiliser !
V. Les écritures relatives à la fin d'exercice▲
À la fin de l'exercice comptable (souvent le 31/12), on va « clôturer » les comptes et établir le bilan et le compte de résultat.
On commence par établir un inventaire. En fait on va comparer
- le contenu des comptes des classes 1 à 5 qui traduisent le patrimoine de l'association
avec
- la réalité du terrain.
En pratique, on tire une balance (« provisoire ») des comptes et on « justifie les soldes » des comptes de bilan.
Dans notre exemple :
Il s'agit de la contrepartie du projecteur reçu de Christophe.
On a décidé de l'amortir en cinq ans, on comptabilise l'amortissement :
On vérifie que c'est bien le report comptabilisé au bilan précédent (31/12/14).
Ce solde représente
- le projecteur (750 que nous avons déjà réduit plus haut)
et
- l'ordinateur (499) que nous avons décidé d'amortir en trois ans.
On comptabilise l'amortissement :
On dresse l'inventaire des objets encore en stock et on valorise chaque pièce à sa valeur d'achat (par exemple sur une feuille Excel).
Supposons que la valeur totale s'élève à 1234,00 €, on comptabilise l'ajustement :
et
On compare ce solde à celui (symétrique !(1)) qui figure à l'extrait bancaire.
On compte l'argent qui est effectivement dans la caisse.
Un ultime coup d'œil sur la balance (en principe « définitive ») pour détecter et corriger les erreurs d'imputation éventuelles.
Quand on est convaincu que tout est OK, on clôture les comptes de résultat de l'exercice.
Concrètement, on met leur solde à zéro par contrepartie de « 1100 Report à nouveau ».
Cette opération est automatisée :
Et viennent bilan et compte de résultat en deux clics :
De même pour les journaux auxiliaires :
et le grand-livre :
VI. Commentaires sur la confection de l'état sePassif▲
La source (plutôt acrobatique !) :
SELECT IIf([PlanCpte] Like "4*",4010,[Plancpte]) AS CptePassif, Sum(solde([PlanCpte],getgvLaDate())) AS Mt, First(IIf([PlanCpte]=[CptePassif],[PlanIntitule],"Fournisseurs")) AS Intitule
FROM tPlanCompta
WHERE (((tPlanCompta.[Actif])=No))
GROUP BY IIf([PlanCpte] Like "4*",4010,[Plancpte])
HAVING (((Sum(solde([PlanCpte],getgvLaDate())))>0.001));VII. Commentaires sur la confection de l'état eGrandLivre▲
Nous voulons, compte par compte, afficher :
le solde en début d'exercice (en-tête de groupe PlanCpte) ;
le détail des mouvements (détail) ;
le solde en fin d'exercice (pied de groupe PlanCpte).
La source n'est pas piquée des vers :
SELECT tPlanCompta.tPlanPK, tPlanCompta.PlanCpte, tPlanCompta.PlanIntitule,
"31/12/" & getgvAnnee()-1 AS DateDepart,
"31/12/" & getgvAnnee() AS DateFin,
solde([PlanCpte],[DateDepart]) AS SoldeOuverture,
IIf([soldeOuverture]<0,-[SoldeOuverture],0) AS SODebiteur,
IIf([soldeOuverture]>0,[SoldeOuverture],0) AS SOCrediteur,
solde([PlanCpte],[DateFin]) AS SoldeCloture,
IIf([SoldeCloture]<0,-[SoldeCloture],0) AS SCDebiteur,
IIf([SoldeCloture]>0,[SoldeCloture],0) AS SCCrediteur,
tMvts.Origine, tMvts.MvtDate,
IIf([MvtMt]<0,-[MvtMt],0) AS Debit,
IIf([MvtMt]>0,[MvtMt],0) AS Credit, tMvts.MvtLibelle
FROM tMvts INNER JOIN tPlanCompta ON tMvts.tPlanFK = tPlanCompta.tPlanPK
WHERE (((tMvts.MvtDate)>"31/12/" & getgvAnnee()-1 And (tMvts.MvtDate)<="31/12/" & getgvAnnee()))
ORDER BY tPlanCompta.PlanCpte;On comprendra mieux avec un dessin :
On construit la date de l'exercice précédent et celle de l'exercice courant.
On utilise chacune de ces dates comme paramètre de la fonction Solde().
On ventile la colonne du solde en solde débiteur et solde créditeur.
Seuls les mouvements de l'exercice sont sélectionnés.
Les MvtMt sont ventilés en débit et crédit.
VIII. Commentaires du code VBA (module mCompta)▲
VIII-A. Deux variables globales et les fonctions qui restituent leur valeur▲
Global gvLaDate As Date
Global gvAnnee As Long
Public Static Function GetgvLadate()
GetgvLadate = gvLaDate
End Function
Public Static Function GetgvAnnee()
GetgvAnnee = gvAnnee
End FunctionVIII-A-1. À quoi servent ces globales ?▲
Par exemple, si l'utilisateur demande l'édition du bilan, il devra renseigner la date.
On vérifie qu'il s'agit d'une date pertinente, avant de la stocker dans la variable globale gvLaDate.
La fonction GetgvDate() permet d'en récupérer la valeur pour l'utiliser dans la requête source de l'actif :
Dans une requête, on n'a pas accès aux variables globales. Pour pallier cette lacune, on utilise une fonction qui a la même valeur.
VIII-B. Deux fonctions pour demander une date ou une année▲
VIII-B-1. Pour demander une date▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
Public Function DemanderDate() As Boolean
On Error GoTo GestionErreurs
Dim LaDate As Date
'Invite
LaDate = InputBox("À quelle date jj/mm/aaaa ?")
'Vérifier la pertinence
If IsNull(DLookup("tMvtsPK", "tMvts", "year(MvtDate)=" & Year(LaDate))) Then
Err.Raise 13
End If
'MàJ gvLaDate
gvLaDate = LaDate
DemanderDate = True
GestionErreurs:
Select Case Err.Number
Case 0 'Pas d'erreur
Exit Function
Case 13, 94 'Valeur incorrecte
MsgBox "La valeur entrée n'est pas pertinente !"
Exit Function
Case Else
MsgBox "Erreur dans DemanderDate N° " & Err.Number & " " & Err.Description
End Select
End Function
Commentaires du code
7 : on vérifie que la table tMvts contient au moins un mouvement pour cette période. Si ce n'est pas le cas, on force une erreur N° 13 que l'on traite à l'instruction 17 pour signaler l'anomalie à l'utilisateur.
12 : si la date est pertinente, la fonction renvoie True. Sinon, elle renvoie sa valeur initiale par défaut : False.
Exemple d'utilisation
Si on clique sur le bouton « Balance » du menu, le code suivant se déclenche :
2.
3.
4.
5.
6.
7.
Private Sub btBalance_Click()
'Demander la date
If DemanderDate = True Then
Call CreaMvts
DoCmd.OpenReport "eBalance", acViewPreview
End If
End Sub
L'utilisateur reçoit cette invite :

S'il introduit par erreur « 311/2/2015 », ce message sera affiché :

VIII-B-2. Pour demander une année▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
Public Function DemanderAnnee() As Boolean
On Error GoTo GestionErreurs
Dim lngAnnee As Long
'Invite
lngAnnee = InputBox("Quelle année aaaa ?")
'Vérif pertinence
If IsNull(DLookup("tMvtsPK", "tMvts", "year(MvtDate)=" & lngAnnee)) Then
Err.Raise 13
End If
'MàJ gvAnnee
gvAnnee = lngAnnee
DemanderAnnee = True
GestionErreurs:
Select Case Err.Number
Case 0 'Pas d'erreur
Exit Function
Case 13, 94 'Valeur incorrecte
MsgBox "La valeur entrée n'est pas pertinente !"
Case Else
MsgBox "Erreur dans DemanderAnnee N° " & Err.Number & " " & Err.Description
End Select
End Function
Même explication, mutatis mutandis.
VIII-C. Deux fonctions pour connaître le solde d'un compte à une date▲
VIII-C-1. Solde()▲
Le solde d'un compte, c'est la somme algébrique de ses mouvements (dans notre cas, les débits sont signés « - »).
2.
3.
4.
5.
6.
7.
Public Function solde(NumCpte As Long, ADate As Date) As Single
Dim lngCptePK As Long
'Rechercher le tPlanPK
lngCptePK = DLookup("tPlanPK", "tPlanCompta", "PlanCpte =" & NumCpte)
'Solde = somme des mouvements
solde = Nz(DSum("MvtMt", "tMvts", "MvtDate<=#" & Format(ADate, "mm/dd/yyyy") & "# AND tPlanFK =" & lngCptePK), 0)
End Function
Dans une fonction (et d'une manière générale dans le VBA), les dates doivent être formatées à la sauce anglaise : mois, jour, année, et encadrées de croisillons (#).
Ex. pour le 2 mars 2016 : #3/2/16#.
Explication du code
4 : on recherche l'id (tPlanPK) du N° de compte dans la table tPlan.
7 : on additionne les mouvements de ce compte. Si la somme est null (pas encore de mouvement à cette date, par exemple), la fonction renvoie « 0 ».
Exemple d'utilisation
VIII-C-2. SoldePP()▲
Pour confectionner le bilan, les comptes de résultat (on dit aussi les comptes de pertes et profits =>PP) sont soldés par transfert au compte « 1100 Report à nouveau ».
La fonction SoldePP() renvoie le solde du compte juste avant cette écriture de transfert :
2.
3.
4.
5.
6.
7.
8.
9.
10.
Public Function SoldePP(NumCpte As Long, Annee As Long) As Double
'Solde des comptes de pertes et profits avant clôture
Dim lngCle As Long
'Recherche le tPlanPK du compte
lngCle = DLookup("tPlanPK", "tPlanCompta", "PlanCpte=" & NumCpte)
'Calcul du solde
SoldePP = Nz(DSum("MvtMt", "tMvts", "tPlanFk=" & lngCle _
& " AND format(MvtDate,""yyyy"")=" & Annee _
& " AND MvtLibelle <> ""ClôturePP"""), 0)
End Function
Explication du code
4 : on recherche l'id (tPlanPK) du N° de compte dans la table tPlan.
7-9 : on additionne les mouvements de ce compte en excluant ceux qui concernent la clôture (libellé = ClôturePP. Si la somme est null (pas encore de mouvement à cette date), la fonction renvoie « 0 ».
Exemple d'utilisation
VIII-D. Une fonction qui renvoie zéro si la donnée n'existe pas▲
On connaît la fonction Nz(variant[, valeur-si-null]) qui renvoie une valeur (par ex. « 0 ») si la valeur donnée en premier paramètre est Null.
Ce dont on parle ici, c'est d'une fonction qui renvoie « 0 »… si la valeur n'existe pas.
Un exemple concret pour comprendre de quoi on parle.
Prenons l'état eRecettesDepenses :
Instinctivement, on coderait la formule en jaune pour afficher le résultat.
Mais qu'advient-il si, par exemple, il n'y avait pas de dépenses ? Voici :
Écrire la formule comme ceci :
=Nz([CTNRseRecettes].[Etat]![txtTotalRecettes];0)-Nz[CTNRseDepenses].[Etat]![txtTotalDepenses];0)
ne changerait rien à l'affaire, car [CTNRseDepenses].[Etat]![txtTotalDepenses] n'a pas la valeur Null, le contrôle n'existe pas !
De là, l'idée de cette fonction :
Function AbsentToZero(anyValue As Variant) As Variant
On Error GoTo GestionErreur
AbsentToZero = Nz(anyValue, 0)
GestionErreur:
AbsentToZero = 0
End FunctionEt nous exprimerons la différence comme ceci :
=AbsentToZero([CTNRseRecettes].[Etat]![txtTotalRecettes])-AbsentToZero[CTNRseDepenses].[Etat]![txtTotalDepenses])
qui donnera ceci :
VIII-E. La routine CreaMvts()▲
Cette fonction recrée tous les mouvements comptables qui dérivent des encodages des quatre formulaires : fBanque, fCaisse, fAchats et fOpeDiv.
N.B. Tous les mouvements sont recréés à chaque exécution de la routine.
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.
Public Sub CreaMvts()
DoCmd.SetWarnings False
'Purge tMvts
DoCmd.RunSQL " DELETE * FROM tMvts;"
'------
'Banque
'------
'Mvts dons
DoCmd.OpenQuery "rMvtsBqDons"
'Mvts Recettes
DoCmd.OpenQuery "rMvtsBqRecettes"
'Mvts Dépenses
DoCmd.OpenQuery "rMvtsBqFrais"
'------
'Caisse
'------
'Mvts dons
DoCmd.OpenQuery "rMvtsCaDons"
'Mvts Recettes
DoCmd.OpenQuery "rMvtsCaRecettes"
'Mvts Dépenses
DoCmd.OpenQuery "rMvtsCaFrais"
'------------------------------
'Contreparties Banque et Caisse
'------------------------------
DoCmd.OpenQuery "rMvtsContreparties"
'------
'Achats
'------
DoCmd.OpenQuery "rMvtsAchatsFournisseurs"
DoCmd.OpenQuery "rMvtsAchatsContreParties"
'-------------------
'Opérations diverses
'-------------------
DoCmd.OpenQuery "rMvtsOpDivDebits"
DoCmd.OpenQuery "rMvtsOpDivCredits"
DoCmd.SetWarnings True
End Sub
Explication du code
4 : on vidange la table tMvts pour repartir de zéro.
6-13 : on crée les mouvements contreparties du compte banque au départ des tables qui ont servi à l'encodage des événements de banque.
9 : pour les dons :
11 : pour les autres montants reçus :
13: pour les montants prélevés au compte banque :
16-23 : on crée les mouvements contreparties du compte caisse au départ des tables qui ont servi à l'encodage des événements contre espèces.
La technique est identique à celle décrite plus haut pour la banque.
À ce stade, on a créé les mouvements contreparties des comptes 5100 Banque et 5300 Caisse, on va maintenant générer les mouvements pour ces deux comptes, pour chacune des dates.
28 : création des mouvements aux comptes Banque et Caisse :
31-34 : création des mouvements relatifs aux factures d'achat.
33 : mouvements aux comptes individuels des fournisseurs :
34 : mouvements dans les comptes contreparties :
34-40 : création des mouvements des opérations diverses.
39 : les débits :
40 : les crédits :
VIII-F. Automatiser la clôture des comptes de résultat▲
En fait, automatiser le remplissage du formulaire fOpeDiv pour comptabiliser l'écriture qui solde les comptes de résultat par transfert à « 1100 Report à nouveau ».
Dans le déroulement des travaux de clôture, il n'est pas rare de devoir recommencer plusieurs fois cette opération de transfert.
On croit que c'est terminé, on clôture… et on se rend compte qu'on a oublié de comptabiliser une opération importante.
Le mécanisme mis en place dans la routine CloturePP() permet de recommencer autant de fois que nécessaire : l'écriture précédente éventuelle est d'abord détricotée.
VIII-F-1. Code associé au clic du bouton▲
Private Sub BtCloturePP_Click()
If DemanderAnnee = True Then
Call CloturePP(gvAnnee)
DoCmd.OpenForm "fOpeDiv"
End If
End SubExplication du code
2 : on invite l'utilisateur à entrer une année.
VIII-F-2. La routine Cloture() proprement dite▲
Public Sub CloturePP(Annee As Long)
Dim lngCle As Long
Dim strSQL As String
Dim dblContrePartie As Double
Dim qdf As DAO.QueryDef
'------------------------------------------------------
'Éliminer les comptabilisations précédentes éventuelles
'------------------------------------------------------
DoCmd.SetWarnings False
'1° rechercher la clé de l'enregistrement dans tOpDiv
lngCle = Nz(DLookup("tOpeDivPk", "tOpeDiv", "tOpeDivDate=#" & "12/31/" & Annee & "#" _
& "AND tOpeDivLibelle=""ClôturePP"""), 0)
'S'il existe on supprime ses débits crédits, sinon on crée un nouvel enregistrement
If lngCle <> 0 Then
'Supprimer les débits et crédits anciens
DoCmd.RunSQL "DELETE * FROM tOpeDivDebits WHERE tOpeDivFK=" & lngCle & ";"
DoCmd.RunSQL "DELETE * FROM tOpeDivCredits WHERE tOpeDivFK=" & lngCle & ";"
Else
Set qdf = CurrentDb.QueryDefs("rClotPPOpeDiv")
qdf.Parameters("LaDate") = "31/12/" & Annee
qdf.Execute
End If
'----------------
'Recréer les mvts
'----------------
Call CreaMvts
DoCmd.SetWarnings False
'--------------------------------------------------------------------
'Garnir tOpeDivDebits et tOpeDivCredits pour solder les comptes de PP
'--------------------------------------------------------------------
lngCle = DLookup("tOpeDivPk", "tOpeDiv", "tOpeDivDate=#" & "12/31/" & Annee & "#" _
& "AND tOpeDivLibelle=""ClôturePP""")
'Débits pour solder les comptes créditeurs
Set qdf = CurrentDb.QueryDefs("rClotPPRaZCrediteurs")
qdf.Parameters("[LaDate]") = "31/12/" & Annee
qdf.Parameters("[CleOpeDiv]") = lngCle
qdf.Execute
'Crédits pour solder les comptes débiteurs
Set qdf = CurrentDb.QueryDefs("rClotPPRaZdebiteurs")
qdf.Parameters("[LaDate]") = "31/12/" & Annee
qdf.Parameters("[CleOpeDiv]") = lngCle
qdf.Execute
'-----------------------------------
'Contrepartie Résultat de l'exercice
'-----------------------------------
dblContrePartie = Nz(DSum("OpeDivDebitMt", "tOpeDivDebits", "tOpeDivFK =" & lngCle), 0) _
- Nz(DSum("OpeDivCreditMt", "tOpeDivCredits", "tOpeDivFK =" & lngCle), 0)
If dblContrePartie > 0 Then
'Bénéfice => créditer Report à Nouveau
Set qdf = CurrentDb.QueryDefs("rClotPPReportBenef")
qdf.Parameters("[ReportANouveau]") = dblContrePartie
qdf.Parameters("[CleOpeDiv]") = lngCle
qdf.Execute
Else
'Perte : => débiter Report à Nouveau
dblContrePartie = dblContrePartie * -1
Set qdf = CurrentDb.QueryDefs("rClotPPReportPerte")
qdf.Parameters("[ReportANouveau]") = dblContrePartie
qdf.Parameters("[CleOpeDiv]") = lngCle
qdf.Execute
End If
'-----------
'Libérer qdf
'-----------
Set qdf = Nothing
End SubCommentaires du code
9-24 : on annule l'écriture éventuelle déjà comptabilisée pour cette clôture.
13-14 : on recherche dans tOpeDiv la clé de cette précédente écriture éventuelle.
17-19 : s'il y a déjà eu une écriture de clôture cette année, on en supprime les débits et crédits.
21-23 : sinon, on crée un nouvel enregistrement dans tOpeDiv. On exécute la requête paramétrée rClotPPOpeDiv :

PARAMETERS [LaDate] DateTime;
INSERT INTO tOpeDiv ( tOpeDivDate, tOpeDivLibelle )
SELECT [LaDate] AS [Date], "ClôturePP" AS libelle;27-30 : on recrée les mouvements avant clôture.
33-67 : on construit une nouvelle écriture de clôture.
37-41 : on débite les comptes de résultat actuellement créditeurs pour les solder. Pour ce faire, on exécute la requête rClotPPRazCrediteurs :
PARAMETERS [LaDate] DateTime, [CleOpeDiv] Long;
INSERT INTO tOpeDivDebits ( tPlanFK, OpeDivDebitMT, tOpeDivFK )
SELECT tPlanCompta.tPlanPK, solde([PlanCpte],[LaDate]) AS Mvt, [CleOpeDiv] AS Cle
FROM tPlanCompta
WHERE (((solde([PlanCpte],[LaDate]))>0.001) And ((tPlanCompta.PlanCpte)>=6000 And (tPlanCompta.PlanCpte)<=7999))
ORDER BY tPlanCompta.PlanCpte;43-47 : on procède de manière semblable pour solder les comptes de résultat actuellement débiteurs.
50-67 : « 1100 Report à nouveau » est la contrepartie de ces débits et crédits de remise à zéro des comptes de résultat.
52-53 : on calcule le résultat. Si la somme des débits de mise à zéro est supérieure à celle des crédits, nous sommes en bénéfice, sinon en perte.
56-59 : dans le cas d'un bénéfice, on exécute la requête rCloPPReportBenef :

PARAMETERS [ReportANouveau] IEEEDouble, [CleOpeDiv] Long;
INSERT INTO tOpeDivCredits ( OpeDivCreditMT, tOpeDivFK, tPlanFK )
SELECT [ReportANouveau] AS benef, [CleOpeDiv] AS Cle, 2 AS NumCpte;62-67 : idem mutatis mutandis s'il s'agit d'une perte.
IX. Téléchargement▲
La base de données se trouve ici. Vous devez décompresser l'archive dans un répertoire quelconque qui inclura le sous-dossier Images.
X. Remerciements ▲
Ma gratitude à Joseph de l'association vidica qui a eu la patience de m'expliquer en détail le fonctionnement de son association.
Merci aux utilisateurs de cette application de me signaler les erreurs et lacunes éventuelles.
Merci aussi à Christophe LOUVET, Denis Hulo (User), Pierre Fauconnier et Winjerome pour leurs remarques.
Merci à Jacques Thery pour sa correction orthographique.













































