Hallo zusammen,
ich möchte hier eine SQL-basierte Lösung zur stichtagsbezogenen Lagerbewertung nach HGB teilen, die ich für JTL-Wawi (MSSQL) umgesetzt habe.
Ziel war keine warenwirtschaftliche Bewertung, sondern eine bilanzielle Stichtagsbewertung zur Übergabe an Buchhaltung / Steuerberater.
Bestand
Summe(Wareneingang) – Summe(Warenausgang bis Stichtag)
Anschaffungskosten (AK)
Summe(Eingangsmenge × EK)
– Summe(Ausgangsmenge × EK)
Marktwert (Vergleichswert)
Bestand × Vergleichspreis (hier: Artikel.fEKNetto)
Bilanzwert (HGB)
MIN(Anschaffungskosten, Marktwert)
➡️ Bewertung erfolgt artikelweise, nicht pauschal.
➡️ Durchschnittsbewertung (keine FIFO-Simulation, HGB-zulässig).
Stichtagslogik sauber über:
dErstellt < DATEADD(DAY, 1, @Stichtag)
Grüße
scenix26
ich möchte hier eine SQL-basierte Lösung zur stichtagsbezogenen Lagerbewertung nach HGB teilen, die ich für JTL-Wawi (MSSQL) umgesetzt habe.
Ziel war keine warenwirtschaftliche Bewertung, sondern eine bilanzielle Stichtagsbewertung zur Übergabe an Buchhaltung / Steuerberater.
🎯 Zielsetzung
- Bewertung der Lagerbestände zum festen Stichtag
- HGB-konform nach § 253 HGB
- Anwendung des strengen Niederstwertprinzips
- keine pauschalen Altersabschläge
- keine Rückschreibung in JTL, reine Auswertung
🧠 Bewertungslogik (Kurzfassung)
Bestand
Summe(Wareneingang) – Summe(Warenausgang bis Stichtag)
Anschaffungskosten (AK)
Summe(Eingangsmenge × EK)
– Summe(Ausgangsmenge × EK)
Marktwert (Vergleichswert)
Bestand × Vergleichspreis (hier: Artikel.fEKNetto)
Bilanzwert (HGB)
MIN(Anschaffungskosten, Marktwert)
➡️ Bewertung erfolgt artikelweise, nicht pauschal.
➡️ Durchschnittsbewertung (keine FIFO-Simulation, HGB-zulässig).
🗂️ Verwendete Tabellen (Auszug)
- tWarenLagerEingang
- tWarenLagerAusgang
- tArtikel
- tArtikelBeschreibung
- tWarenLager
- tWarenLagerPlatz
Stichtagslogik sauber über:
dErstellt < DATEADD(DAY, 1, @Stichtag)
💶 Wichtiger Hinweis zur Formatierung
- Alle Berechnungen erfolgen numerisch (DECIMAL)
- FORMAT('C','de-DE') ausschließlich in der finalen SELECT-Ausgabe
- Keine Aggregationen auf formatierten Feldern
→ verhindert Klassiker wie Fehler 8114 nvarchar → numeric
📄 Ausgabe
- Bestand
- Anschaffungskosten (AK)
- Marktwert
- HGB-Bilanzwert (Niederstwert)
- Letztes Lieferdatum
- Altersklasse (nur Dokumentation, nicht wertrelevant)
⚠️ Abgrenzung
- JTL-interne Lagerwerte (GLD etc.) werden bewusst nicht verwendet
- Das Script dient ausschließlich der Bilanzierung
- Keine automatische Rückschreibung nach JTL
Grüße
scenix26
SQL:
DECLARE @Stichtag DATE = CONVERT(DATE, '2025-12-31');
DECLARE @warenlager VARCHAR(500) = 'WMS';
SELECT tartikel.cartnr AS
Artikelnummer,
Max(tartikelbeschreibung.cname) AS
Artikelname,
twarenlager.cname AS Lager,
/* ===== Bestand ===== */
Cast(Sum(twarenlagereingang.fanzahl) - ( Sum(
Isnull(Warenausgang.fanzahl, 0.0))
) AS
DECIMAL(18, 0)) AS Bestand,
/* ===== Anschaffungskosten (AK) ===== */
Format(Cast(Round(Sum(twarenlagereingang.fanzahl *
twarenlagereingang.fekeinzel)
-
Sum(Isnull(Warenausgang.fanzahl, 0.0) *
twarenlagereingang.fekeinzel), 2) AS
DECIMAL(
18, 2)), 'C', 'de-DE') AS
Anschaffungskosten,
/* ===== Marktwert ===== */
Format(Cast(Round(Sum(twarenlagereingang.fanzahl) - Sum(
Isnull(Warenausgang.fanzahl, 0.0)),
2) AS DECIMAL(18, 2)), 'C', 'de-DE') AS Marktwert
,
/* ===== Niederstwert (HGB) ===== */
Format(Cast(Round(CASE
WHEN ( ( Sum(twarenlagereingang.fanzahl) - Sum(
Isnull(Warenausgang.fanzahl, 0.0)) ) *
Isnull(vk.feknetto, 0) ) < ( Sum(
twarenlagereingang.fanzahl *
twarenlagereingang.fekeinzel) -
Sum(
Isnull(Warenausgang.fanzahl, 0.0) *
twarenlagereingang.fekeinzel) )
THEN (
( Sum(twarenlagereingang.fanzahl) - Sum(
Isnull(Warenausgang.fanzahl, 0.0))
) *
Isnull
(vk.feknetto, 0) )
ELSE ( Sum(twarenlagereingang.fanzahl *
twarenlagereingang.fekeinzel) -
Sum(Isnull(Warenausgang.fanzahl, 0.0) *
twarenlagereingang.fekeinzel) )
END, 2) AS DECIMAL(18, 2)), 'C', 'de-DE') AS
HGB_Bilanzwert,
/* ===== Letzter Zugang ===== */
Max(twarenlagereingang.dgeliefertam) AS
LetztesLieferdatum,
/* ===== Altersindikator (NICHT wertrelevant) ===== */
CASE
WHEN Year(Max(twarenlagereingang.dgeliefertam)) = Year(@Stichtag) THEN
'0–12 Monate'
WHEN Year(Max(twarenlagereingang.dgeliefertam)) = Year(@Stichtag) - 1
THEN
'13–24 Monate'
WHEN Year(Max(twarenlagereingang.dgeliefertam)) = Year(@Stichtag) - 2
THEN
'25–36 Monate'
ELSE 'älter als 12 Monate'
END AS
Altersklasse
FROM dbo.twarenlagereingang
JOIN dbo.tartikel
ON twarenlagereingang.kartikel = tartikel.kartikel
JOIN dbo.tspracheused
ON nstandard = 1
JOIN dbo.tartikelbeschreibung
ON tartikel.kartikel = tartikelbeschreibung.kartikel
AND tspracheused.ksprache = tartikelbeschreibung.ksprache
AND tartikelbeschreibung.kplattform = 1
JOIN dbo.twarenlagerplatz
ON twarenlagereingang.kwarenlagerplatz =
twarenlagerplatz.kwarenlagerplatz
JOIN dbo.twarenlager
ON twarenlagerplatz.kwarenlager = twarenlager.kwarenlager
LEFT JOIN tartikel vk
ON vk.kartikel = tartikel.kartikel
LEFT JOIN (SELECT twarenlagerausgang.kwarenlagereingang,
Sum(twarenlagerausgang.fanzahl) AS fAnzahl
FROM dbo.twarenlagerausgang
WHERE twarenlagerausgang.derstellt <
Dateadd(day, 1, @Stichtag)
GROUP BY twarenlagerausgang.kwarenlagereingang) Warenausgang
ON twarenlagereingang.kwarenlagereingang =
Warenausgang.kwarenlagereingang
WHERE twarenlagereingang.derstellt < Dateadd(day, 1, @Stichtag)
AND twarenlager.cname = @warenlager
GROUP BY tartikel.cartnr,
twarenlager.cname,
vk.feknetto
HAVING Sum(twarenlagereingang.fanzahl - Isnull(Warenausgang.fanzahl, 0.0)) > 0
ORDER BY letzteslieferdatum ASC