I. Au sujet des méthodes d'évaluation d'un stock▲
Pour ce qui concerne les entrées, elles se comptabilisent au prix d'achat plus éventuellement divers frais accessoires supportés jusqu'à l'entrée au lieu d'entreposage.
Pour la valorisation des sorties, on propose classiquement trois méthodes d'évaluation :
- celle qui suppose que l'utilisation des marchandises respecte le même ordre chronologique que les entrées dans l'entrepôt. En d'autres mots, on prélève d'abord les marchandises stockées depuis plus longtemps : les premières entrées sont les premières sorties, d'où l'acronyme FIFO (First In First Out) ;
- celle qui suppose, au contraire, que ce sont les dernières entrées qui sont d'abord consommées, d'où l'acronyme LIFO (Last In First Out) ;
- celle qui consiste à recalculer à chaque nouvelle entrée, un coût moyen unitaire pondéré : CMUP.

II. Exemple de calcul▲
Les mouvements positifs représentent les entrées, les mouvements négatifs, les sorties.
La colonne « PU » renseigne le prix d'achat unitaire.

Pour la suite de l'exposé, nous allons supposer que, en amont dans notre application, les mouvements de stock sont enregistrés dans cette table :
Dans la colonne Mvt, un nombre positif représente une entrée, un nombre négatif représente une sortie.
La colonne PUEntree renseigne le prix unitaire à l'achat.
Le système d'encodage situé en amont garantit que :
- les opérations sont enregistrées dans l'ordre chronologique ;
- l'enregistrement d'une sortie n'est accepté que si le solde en stock est suffisant.
III. Application de la méthode FIFO▲
III-A. Analyse de la démarche FIFO▲
Il faut prélever la sortie sur le stock restant en commençant pour les plus anciennes entrées encore approvisionnées. En d'autres mots, avant d'imputer, il faut d'abord ventiler le stock restant en différents postes selon leur ancienneté, et ensuite, imputer en suivant l'ordre chronologique.
Pour valoriser une sortie, la démarche va donc comporter plusieurs étapes :
- déterminer le nombre d'unités qui étaient en stock avant de constater la sortie ;
- considérer chaque entrée en commençant par la dernière et déterminer la part de cette entrée qui subsiste en stock. (Puisque les sorties ont été imputées dans l'ordre des entrées, ce qui reste se trouve nécessairement dans les dernières entrées !) ;
- on peut alors valoriser la sortie en l'imputant sur les entrées encore provisionnées, en commençant par la plus ancienne.
III-B. Algorithme FIFO▲

III-C. Le code FIFO▲
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.
Option Compare Database
Option Explicit
Public Function CalculerPUSortieFifo(Produit As Long, _
NumMVT As Long, Nombre As Double) As Double
Dim oRst As Recordset
Dim dStockAVentiler As Double
Dim sSQL As String
Dim dResteAImputer As Double
'Calcul du stock avant la sortie
dStockAVentiler = DSum("Mvt", "tMouvements", "tMouvementsPK<" & NumMVT _
& " AND tProduitsFK=" & Produit)
'Création du recordset des entrées
sSQL = "SELECT Mvt, PUEntree FROM tMouvements " _
& "WHERE tMouvementsPK<" & NumMVT _
& " AND tProduitsFK=" & Produit _
& " AND Mvt>0;"
Set oRst = CurrentDb.OpenRecordset(sSQL, dbOpenSnapshot)
'Ventilation du stock par poste d'entrée
oRst.MoveLast
Do While dStockAVentiler > oRst("Mvt")
dStockAVentiler = dStockAVentiler - oRst("Mvt")
If dStockAVentiler > 0 Then oRst.MovePrevious
Loop
'À ce stade, on se trouve sur l'entrée la plus ancienne encore provisionnée
'On va prélever la sortie sur ce poste et les suivants (si nécessaire)
'Valorisation de la sortie FIFO
dResteAImputer = Nombre
If dStockAVentiler >= dResteAImputer Then 'le solde restant de cette entrée suffit
CalculerPUSortieFifo = CalculerPUSortieFifo _
+ dResteAImputer * oRst("PUEntree")
GoTo PrixUnitaireSortieFIFO
Else
CalculerPUSortieFifo = CalculerPUSortieFifo _
+ dStockAVentiler * oRst("PUEntree")
dResteAImputer = dResteAImputer - dStockAVentiler
oRst.MoveNext
End If
Do
If dResteAImputer > oRst("Mvt") Then
CalculerPUSortieFifo = CalculerPUSortieFifo _
+ oRst("Mvt") * oRst("PUEntree")
dResteAImputer = dResteAImputer - oRst("Mvt")
Else
CalculerPUSortieFifo = CalculerPUSortieFifo _
+ (dResteAImputer * oRst("PUEntree"))
Exit Do
End If
oRst.MoveNext
Loop
PrixUnitaireSortieFIFO:
CalculerPUSortieFifo = CalculerPUSortieFifo / Nombre
'Libérer la mémoire
oRst.Close
Set oRst = Nothing
End Function
III-D. Une requête pour afficher le prix unitaire des sorties FIFO▲

SELECT tMouvements.tMouvementsPK, tMouvements.tProduitsFK, tMouvements.MvtDate, tMouvements.Mvt, tMouvements.PUEntree, IIf([Mvt]<0,CalculerPUSortieFiFo([tProduitsFK],[tMouvementsPK],-[mvt]),"") AS PUSortieFIFO
FROM tMouvements;
IV. Application de la méthode LIFO▲
IV-A. Analyse de la démarche LIFO▲
Pour valoriser une sortie, il faut lire à rebours tous les enregistrements qui précèdent, et prélever la sortie sur les entrées encore provisionnées.
IV-B. Algorithme LIFO▲

IV-C. Le code LIFO▲
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.
Function CalculerPUSortieLifo(Produit As Long, NumMVT As Long, Nombre As Double) As Double
Dim oRst As Recordset
Dim dResteAImputer As Double
Dim sSQL As String
Dim dCumulSorties As Double
'Créer le recordset pour lire les mouvements qui précèdent
sSQL = "SELECT Mvt, PUEntree FROM tMouvements" _
& " WHERE tMouvementsPK <" & NumMVT & " And tProduitsFK =" & Produit & ";"
Set oRst = CurrentDb.OpenRecordset(sSQL, dbOpenDynaset)
oRst.MoveLast
dResteAImputer = Nombre
Do While Not oRst.EOF
If oRst("Mvt") < 0 Then
'Sortie => il faut cumuler pour l'imputer à l'entrée qui précède
dCumulSorties = dCumulSorties + oRst("Mvt")
Else
'C'est donc une entrée.
'Reste-t-il du disponible ?
If oRst("Mvt") + dCumulSorties > 0 Then
'si oui De 2 choses l'une :
'- ou bien elle suffit pour alimenter la sortie
If dResteAImputer <= oRst("Mvt") + dCumulSorties Then
CalculerPUSortieLifo = CalculerPUSortieLifo + _
(dResteAImputer * oRst("PUEntree"))
Exit Do
'- ou bien,il n'y a pas encore assez et il faut continuer
Else
CalculerPUSortieLifo = CalculerPUSortieLifo + _
(oRst("Mvt") + dCumulSorties) * oRst("PUEntree")
dResteAImputer = dResteAImputer - (oRst("Mvt") + dCumulSorties)
dCumulSorties = 0
End If
Else
'Sinon, il faut retenir le solde négatif dans dSortiesImputées et passer au suivant
dCumulSorties = oRst("Mvt") + dCumulSorties
End If
End If
oRst.MovePrevious
Loop
'Prix unitaire sortie
CalculerPUSortieLifo = CalculerPUSortieLifo / Nombre
'Libérer la mémoire
oRst.Close
Set oRst = Nothing
End Function
IV-D. Une requête pour afficher le prix unitaire des sorties LIFO▲

SELECT tMouvements.tMouvementsPK, tMouvements.tProduitsFK, tMouvements.MvtDate, tMouvements.Mvt, tMouvements.PUEntree, IIf([Mvt]<0,CalculerPUSortieLiFo([tProduitsFK],[tMouvementsPK],-[mvt]),"") AS PUSortieLIFO
FROM tMouvements;
V. La méthode du CMUP▲
V-A. Analyse de la démarche CMUPLe CMUP d'une sortie est celui qui a été recalculé pour l'entrée la plus récente.▲
Le CMUP d'une entrée se calcule en trois temps :
1° on valorise le stock restant au CMUP qui avait été calculé lors de la dernière entrée ;
2° on y ajoute la nouvelle entrée valorisée à son prix unitaire d'achat ;
3° on divise la somme obtenue en 2° par le nombre d'unités du nouveau stock.
Pour la première entrée, le CMUP est naturellement égal au prix unitaire !
Pour appliquer la méthode proposée, il est indispensable d'ajouter une colonne à la table tMouvements pour y stocker le CMUP.
V-B. Algorithme CMUP▲

V-C. Le code CMUP▲
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.
Public Function CalculerCMUP(Produit As Long, NumMVT As Long, _
Optional Nombre As Double, _
Optional PU As Double) As Double
On Error GoTo GestionErreurs
Dim oRst As Recordset
Dim sSQL As String
Dim dSorties As Double
Dim dStockDernEntree As Double
Dim dNouveauCMUP As Double
'Créer le Recordset de tMouvements
Set oRst = CurrentDb.OpenRecordset("SELECT * FROM tMouvements " _
& "WHERE tProduitsFK=" _
& Produit & " AND tMouvementsPK<" _
& NumMVT & ";", dbOpenDynaset)
'Cumul des sorties depuis la dernière entrée
oRst.MoveLast
Do While oRst("Mvt") < 0
dSorties = dSorties + oRst("Mvt")
oRst.MovePrevious
Loop
'Ici, on est sur la dernière entrée
If Nombre > 0 Then
'C'est donc la comptabilisation d'une nouvelle entrée
'Nous calculons le nouveau CMUP
'1° déterminer le stock après cette dernière entrée
dStockDernEntree = DSum("Mvt", "tMouvements", "tProduitsFK=" & Produit _
& " AND tMouvementsPK <=" & oRst("tMouvementsPK"))
'2° Calcul du Nouveau CMUP
' C'est ce qui reste du stock précédent valorisé au CMUP d'alors,
'auquel on ajoute la nouvelle entrée (Nombre*PU)
'le tout divisé par le nouveau stock
CalculerCMUP = ((dStockDernEntree + dSorties) * oRst("CMUP") + (Nombre * PU)) _
/ (dStockDernEntree + dSorties + Nombre)
Else
'C'est une sortie
'Elle est valorisée au dernier CMUP calculé (celui sur lequel on est positionné
CalculerCMUP = oRst("CMUP")
End If
'Libérer la mémoire
oRst.Close
Set oRst = Nothing
GestionErreurs:
Select Case Err.Number
Case 0 'pas d'erreur
Case 3021 'première entrée
CalculerCMUP = PU
Set oRst = Nothing
Case Else
MsgBox "Erreur dans EntreeCMUP N° " & Err.Number & " " & Err.Description
End Select
End Function
V-D. Une requête pour mettre à jour la colonne CMUP de la table tMouvements▲

UPDATE tMouvements SET tMouvements.CMUP = calculerCMUP([tProduitsFK],[tMouvementsPK],[Mvt],nz([PUEntree],0));
VI. Calculer la valeur du stock à une date donnée▲
VI-A. Une astuce pour exploiter ce qui précède▲
On va faire comme s'il s'agissait de comptabiliser une sortie de tout le stock présent à cette date.
VI-B. Algorithme▲

VI-C. Le code de chaque méthode▲
Public Function ValStockFIFO(LaDate As Date, Produit As Long) As Double
Dim dStock As Double
Dim lDernNumMvt As Long
'Quel stock à cette date
dStock = Nz(DSum("Mvt", "tMouvements", "MvtDate<=#" _
& Format(LaDate, "mm/dd/yyyy") _
& "# AND tProduitsFK=" & Produit), 0)
'tMouvementPK du dernier Mvt à cette date
lDernNumMvt = DMax("tMouvementsPK", "tMouvements", "MvtDate<=#" _
& Format(LaDate, "mm/dd/yyyy") _
& "# AND tProduitsFK=" & Produit)
'Simuler la sortie pour déterminer la valeur unitaire
ValStockFIFO = CalculerPUSortieFifo(Produit, lDernNumMvt + 1, dStock) * dStock
End Function
Public Function ValStockLIFO(LaDate As Date, Produit As Long) As Double
Dim dStock As Double
Dim lDernNumMvt As Long
'Quel stock à cette date
dStock = DSum("Mvt", "tMouvements", "MvtDate<=#" _
& Format(LaDate, "mm/dd/yyyy") _
& "# AND tProduitsFK=" & Produit)
'tMouvementPK du dernier Mvt à cette date
lDernNumMvt = DMax("tMouvementsPK", "tMouvements", "MvtDate<=#" _
& Format(LaDate, "mm/dd/yyyy") _
& "# AND tProduitsFK=" & Produit)
'Simuler la sortie pour déterminer la valeur unitaire
ValStockLIFO = CalculerPUSortieLifo(Produit, lDernNumMvt + 1, dStock) * dStock
End Function
Public Function ValStockCMUP(LaDate As Date, Produit As Long) As Double
Dim dStock As Double
Dim lDernNumMvt As Long
'Quel stock à cette date
dStock = DSum("Mvt", "tMouvements", "MvtDate<=#" _
& Format(LaDate, "mm/dd/yyyy") _
& "# AND tProduitsFK=" & Produit)
'tMouvementPK du dernier Mvt à cette date
lDernNumMvt = DMax("tMouvementsPK", "tMouvements", "MvtDate<=#" _
& Format(LaDate, "mm/dd/yyyy") _
& "# AND tProduitsFK=" & Produit)
'Simuler la sortie pour déterminer la valeur unitaire
'N.B. Dans CalculerCMUP(), une sortie = un nombre négatif => ici,moins dStock
ValStockCMUP = CalculerCMUP(Produit, lDernNumMvt + 1, -dStock) * dStock
End FunctionVI-D. Exemples d'utilisation▲
Considérons le cas du produit 123 pour l'évaluation du stock au 30/4/17 :

VII. Téléchargement▲
La base de données au format Access2000 est ici.




