Datenbank aufgefüllt und Einträge angepasst (bei 'Wie')

This commit is contained in:
2026-02-27 15:38:07 +00:00
parent 064036a74e
commit ebd031ee58
9 changed files with 233 additions and 92 deletions

View File

@@ -8,22 +8,27 @@ Dies ist die modernisierte Version des alten PHP/jQuery-basierten Ausgaben-Progr
## Features ## Features
- **Eingabe-Tab**: Erfassen von Ausgaben mit: - **Zwei Tabs für verschiedene Ausgabenkategorien:**
- **Haushalt (TYP=0)**: Zahlungsarten EC-R, EC-B, bar-R, bar-B, Einnahme, Überweisung
- **Privat (TYP=1)**: Zahlungsarten bar, EC, VISA, Master, Einnahme, Überweisung
- **Eingabe**: Erfassen von Ausgaben mit:
- Datum (mit automatischem Wochentag) - Datum (mit automatischem Wochentag)
- Wo (Geschäft/Ort) - Wo (Geschäft/Ort)
- Was (Beschreibung) - Was (Beschreibung)
- Wieviel (Betrag in Euro) - Wieviel (Betrag in Euro)
- Wie (Zahlungsart: bar, EC, VISA, MASTER, Einnahme, Überweisung) - Wie (Zahlungsart - abhängig vom aktiven Tab)
- Monatsstatistiken - Monatsstatistiken (TYP-spezifisch)
- Letzte 10 Einträge - Letzte 10 Einträge des aktiven TYPs
- **Listen-Tab**: Vollständige Auflistung aller Einträge mit: - **Listen-Ansicht**: Vollständige Auflistung aller Einträge mit:
- Bearbeiten-Funktion - Bearbeiten-Funktion
- Löschen-Funktion - Löschen-Funktion
- Sortierung nach Datum (absteigend) - Sortierung nach Datum (absteigend)
- Filterung nach TYP (Haushalt/Privat)
- **Statistik-Tab**: Monatliche Auswertungen mit: - **Monatliche Statistiken**:
- Gesamtausgaben - Gesamtausgaben pro TYP
- Aufschlüsselung nach Zahlungsart - Aufschlüsselung nach Zahlungsart
- Einnahmen - Einnahmen
- Überweisungen - Überweisungen
@@ -84,20 +89,22 @@ Die Anwendung verwendet die Tabelle `Ausgaben` mit folgenden Feldern:
- `ID` (auto_increment) - `ID` (auto_increment)
- `Datum` (date) - `Datum` (date)
- `WochTag` (varchar)
- `Wo` (varchar) - Geschäft/Ort - `Wo` (varchar) - Geschäft/Ort
- `Was` (varchar) - Beschreibung - `Was` (varchar) - Beschreibung
- `Wieviel` (decimal) - Betrag - `Wieviel` (decimal) - Betrag
- `Wie` (varchar) - Zahlungsart - `Wie` (varchar) - Zahlungsart
- `TYP` (tinyint) - 0=Haushalt, 1=Privat
- `OK` (tinyint) - Kontrollstatus - `OK` (tinyint) - Kontrollstatus
**Hinweis:** Der Wochentag (`WochTag`) wird nicht in der Datenbank gespeichert, sondern dynamisch aus dem `Datum`-Feld berechnet.
## API Endpoints ## API Endpoints
- `GET /api/ausgaben` - Einträge abrufen (mit limit, startDate, month, year params) - `GET /api/ausgaben` - Einträge abrufen (mit limit, startDate, month, year, typ params)
- `POST /api/ausgaben` - Neuen Eintrag erstellen - `POST /api/ausgaben` - Neuen Eintrag erstellen (mit TYP)
- `PUT /api/ausgaben/[id]` - Eintrag aktualisieren - `PUT /api/ausgaben/[id]` - Eintrag aktualisieren (mit TYP)
- `DELETE /api/ausgaben/[id]` - Eintrag löschen - `DELETE /api/ausgaben/[id]` - Eintrag löschen
- `GET /api/ausgaben/stats` - Monatsstatistiken (mit month, year params) - `GET /api/ausgaben/stats` - Monatsstatistiken (mit month, year, typ params)
## Migration von der alten Version ## Migration von der alten Version

View File

@@ -10,23 +10,23 @@ export async function PUT(
try { try {
const { id } = await context.params; const { id } = await context.params;
const body = await request.json(); const body = await request.json();
const { Datum, WochTag, Wo, Was, Wieviel, Wie, OK } = body; const { Datum, Wo, Was, Wieviel, Wie, TYP, OK } = body;
const pool = getDbPool(); const pool = getDbPool();
const query = ` const query = `
UPDATE Ausgaben UPDATE Ausgaben
SET Datum = ?, WochTag = ?, Wo = ?, Was = ?, Wieviel = ?, Wie = ?, OK = ? SET Datum = ?, Wo = ?, Was = ?, Wieviel = ?, Wie = ?, TYP = ?, OK = ?
WHERE ID = ? WHERE ID = ?
`; `;
const [result] = await pool.query<ResultSetHeader>(query, [ const [result] = await pool.query<ResultSetHeader>(query, [
Datum, Datum,
WochTag,
Wo, Wo,
Was, Was,
parseFloat(Wieviel), parseFloat(Wieviel),
Wie, Wie,
TYP,
OK || 0, OK || 0,
parseInt(id), parseInt(id),
]); ]);

View File

@@ -10,20 +10,41 @@ export async function GET(request: Request) {
const startDate = searchParams.get('startDate'); const startDate = searchParams.get('startDate');
const month = searchParams.get('month'); const month = searchParams.get('month');
const year = searchParams.get('year'); const year = searchParams.get('year');
const typ = searchParams.get('typ');
const pool = getDbPool(); const pool = getDbPool();
let query = 'SELECT * FROM Ausgaben'; let query = `SELECT *,
CASE DAYOFWEEK(Datum)
WHEN 1 THEN 'Sonntag'
WHEN 2 THEN 'Montag'
WHEN 3 THEN 'Dienstag'
WHEN 4 THEN 'Mittwoch'
WHEN 5 THEN 'Donnerstag'
WHEN 6 THEN 'Freitag'
WHEN 7 THEN 'Samstag'
END as WochTag
FROM Ausgaben`;
const params: any[] = []; const params: any[] = [];
const conditions: string[] = [];
if (typ !== null && typ !== undefined) {
conditions.push('TYP = ?');
params.push(parseInt(typ));
}
if (month && year) { if (month && year) {
query += ' WHERE YEAR(Datum) = ? AND MONTH(Datum) = ?'; conditions.push('YEAR(Datum) = ? AND MONTH(Datum) = ?');
params.push(year, month); params.push(year, month);
} else if (startDate) { } else if (startDate) {
query += ' WHERE Datum >= ?'; conditions.push('Datum >= ?');
params.push(startDate); params.push(startDate);
} }
if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
}
query += ' ORDER BY Datum DESC, ID DESC LIMIT ?'; query += ' ORDER BY Datum DESC, ID DESC LIMIT ?';
params.push(parseInt(limit)); params.push(parseInt(limit));
@@ -46,9 +67,9 @@ export async function GET(request: Request) {
export async function POST(request: Request) { export async function POST(request: Request) {
try { try {
const body = await request.json(); const body = await request.json();
const { Datum, WochTag, Wo, Was, Wieviel, Wie, OK } = body; const { Datum, Wo, Was, Wieviel, Wie, TYP, OK } = body;
if (!Datum || !Wo || !Was || !Wieviel || !Wie) { if (!Datum || !Wo || !Was || !Wieviel || !Wie || TYP === undefined) {
return NextResponse.json( return NextResponse.json(
{ success: false, error: 'Missing required fields' }, { success: false, error: 'Missing required fields' },
{ status: 400 } { status: 400 }
@@ -58,17 +79,17 @@ export async function POST(request: Request) {
const pool = getDbPool(); const pool = getDbPool();
const query = ` const query = `
INSERT INTO Ausgaben (Datum, WochTag, Wo, Was, Wieviel, Wie, OK) INSERT INTO Ausgaben (Datum, Wo, Was, Wieviel, Wie, TYP, OK)
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?)
`; `;
const [result] = await pool.query<ResultSetHeader>(query, [ const [result] = await pool.query<ResultSetHeader>(query, [
Datum, Datum,
WochTag,
Wo, Wo,
Was, Was,
parseFloat(Wieviel), parseFloat(Wieviel),
Wie, Wie,
TYP,
OK || 0, OK || 0,
]); ]);

View File

@@ -8,53 +8,73 @@ export async function GET(request: Request) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const month = searchParams.get('month'); const month = searchParams.get('month');
const year = searchParams.get('year'); const year = searchParams.get('year');
const typ = searchParams.get('typ');
if (!month || !year) { if (!month || !year || typ === null) {
return NextResponse.json( return NextResponse.json(
{ success: false, error: 'Month and year are required' }, { success: false, error: 'Month, year and typ are required' },
{ status: 400 } { status: 400 }
); );
} }
const pool = getDbPool(); const pool = getDbPool();
// Get total ausgaben and breakdown by payment type // Get total ausgaben and breakdown by payment type based on TYP
const query = ` let query: string;
if (parseInt(typ) === 0) {
// Haushalt - unterstützt beide Varianten: mit/ohne Bindestrich und Ein/Einnahme, Uber/Ueber
query = `
SELECT SELECT
SUM(CASE WHEN Wie IN ('EC-R', 'EC-B', 'bar-R', 'bar-B', 'Ueber') THEN Wieviel ELSE 0 END) as totalAusgaben, SUM(CASE WHEN Wie IN ('EC-R', 'ECR', 'EC-B', 'ECB', 'bar-R', 'barR', 'bar-B', 'barB', 'Ueber', 'Uber') THEN Wieviel ELSE 0 END) as totalAusgaben,
SUM(CASE WHEN Wie = 'EC-R' THEN Wieviel ELSE 0 END) as ECR, SUM(CASE WHEN Wie IN ('EC-R', 'ECR') THEN Wieviel ELSE 0 END) as ECR,
SUM(CASE WHEN Wie = 'EC-B' THEN Wieviel ELSE 0 END) as ECB, SUM(CASE WHEN Wie IN ('EC-B', 'ECB') THEN Wieviel ELSE 0 END) as ECB,
SUM(CASE WHEN Wie = 'bar-R' THEN Wieviel ELSE 0 END) as barR, SUM(CASE WHEN Wie IN ('bar-R', 'barR') THEN Wieviel ELSE 0 END) as barR,
SUM(CASE WHEN Wie = 'bar-B' THEN Wieviel ELSE 0 END) as barB, SUM(CASE WHEN Wie IN ('bar-B', 'barB') THEN Wieviel ELSE 0 END) as barB,
SUM(CASE WHEN Wie = 'Einnahme' THEN Wieviel ELSE 0 END) as Einnahmen, SUM(CASE WHEN Wie IN ('Einnahme', 'Ein') THEN Wieviel ELSE 0 END) as Einnahmen,
SUM(CASE WHEN Wie = 'Ueber' THEN Wieviel ELSE 0 END) as Ueberweisungen SUM(CASE WHEN Wie IN ('Ueber', 'Uber') THEN Wieviel ELSE 0 END) as Ueberweisungen
FROM Ausgaben FROM Ausgaben
WHERE YEAR(Datum) = ? AND MONTH(Datum) = ? WHERE YEAR(Datum) = ? AND MONTH(Datum) = ? AND TYP = 0
`; `;
} else {
// Privat - unterstützt Uber/Ueber für Überweisung
query = `
SELECT
SUM(CASE WHEN Wie IN ('bar', 'EC', 'VISA', 'MASTER', 'Uber', 'Ueber') THEN Wieviel ELSE 0 END) as totalAusgaben,
SUM(CASE WHEN Wie = 'bar' THEN Wieviel ELSE 0 END) as bar,
SUM(CASE WHEN Wie = 'EC' THEN Wieviel ELSE 0 END) as EC,
SUM(CASE WHEN Wie = 'VISA' THEN Wieviel ELSE 0 END) as VISA,
SUM(CASE WHEN Wie = 'MASTER' THEN Wieviel ELSE 0 END) as MASTER,
SUM(CASE WHEN Wie = 'Einnahme' THEN Wieviel ELSE 0 END) as Einnahmen,
SUM(CASE WHEN Wie IN ('Uber', 'Ueber') THEN Wieviel ELSE 0 END) as Ueberweisungen
FROM Ausgaben
WHERE YEAR(Datum) = ? AND MONTH(Datum) = ? AND TYP = 1
`;
}
const [rows] = await pool.query<RowDataPacket[]>(query, [year, month]); const [rows] = await pool.query<RowDataPacket[]>(query, [year, month]);
const data = rows[0] || { const data = rows[0] || {};
totalAusgaben: 0,
ECR: 0,
ECB: 0,
barR: 0,
barB: 0,
Einnahmen: 0,
Ueberweisungen: 0,
};
// Convert string values from MySQL to numbers // Convert string values from MySQL to numbers
const parsedData = { const parsedData: any = {
totalAusgaben: parseFloat(data.totalAusgaben) || 0, totalAusgaben: parseFloat(data.totalAusgaben) || 0,
ECR: parseFloat(data.ECR) || 0,
ECB: parseFloat(data.ECB) || 0,
barR: parseFloat(data.barR) || 0,
barB: parseFloat(data.barB) || 0,
Einnahmen: parseFloat(data.Einnahmen) || 0, Einnahmen: parseFloat(data.Einnahmen) || 0,
Ueberweisungen: parseFloat(data.Ueberweisungen) || 0, Ueberweisungen: parseFloat(data.Ueberweisungen) || 0,
}; };
if (parseInt(typ) === 0) {
parsedData.ECR = parseFloat(data.ECR) || 0;
parsedData.ECB = parseFloat(data.ECB) || 0;
parsedData.barR = parseFloat(data.barR) || 0;
parsedData.barB = parseFloat(data.barB) || 0;
} else {
parsedData.bar = parseFloat(data.bar) || 0;
parsedData.EC = parseFloat(data.EC) || 0;
parsedData.VISA = parseFloat(data.VISA) || 0;
parsedData.MASTER = parseFloat(data.MASTER) || 0;
}
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
data: parsedData, data: parsedData,

View File

@@ -7,6 +7,7 @@ import { AusgabenEntry } from '@/types/ausgaben';
import packageJson from '@/package.json'; import packageJson from '@/package.json';
export default function Home() { export default function Home() {
const [activeTab, setActiveTab] = useState(0); // 0 = Haushalt, 1 = Privat
const [entries, setEntries] = useState<AusgabenEntry[]>([]); const [entries, setEntries] = useState<AusgabenEntry[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [selectedEntry, setSelectedEntry] = useState<AusgabenEntry | null>(null); const [selectedEntry, setSelectedEntry] = useState<AusgabenEntry | null>(null);
@@ -15,12 +16,13 @@ export default function Home() {
useEffect(() => { useEffect(() => {
fetchRecentEntries(); fetchRecentEntries();
}, []); setSelectedEntry(null); // Clear selected entry when switching tabs
}, [activeTab]);
const fetchRecentEntries = async () => { const fetchRecentEntries = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
const response = await fetch('/api/ausgaben?limit=10', { const response = await fetch(`/api/ausgaben?limit=10&typ=${activeTab}`, {
cache: 'no-store', cache: 'no-store',
headers: { headers: {
'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache',
@@ -58,9 +60,33 @@ export default function Home() {
<main className="max-w-7xl mx-auto border-2 border-black rounded-lg p-6 bg-[#FFFFDD]"> <main className="max-w-7xl mx-auto border-2 border-black rounded-lg p-6 bg-[#FFFFDD]">
<h1 className="text-3xl font-bold text-center mb-6">Ausgaben - Log</h1> <h1 className="text-3xl font-bold text-center mb-6">Ausgaben - Log</h1>
{/* Tab Navigation */}
<div className="flex gap-2 mb-6">
<button
onClick={() => setActiveTab(0)}
className={`flex-1 py-3 px-6 rounded-lg font-semibold transition-colors ${
activeTab === 0
? 'bg-[#85B7D7] text-black'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
Haushalt
</button>
<button
onClick={() => setActiveTab(1)}
className={`flex-1 py-3 px-6 rounded-lg font-semibold transition-colors ${
activeTab === 1
? 'bg-[#85B7D7] text-black'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
Privat
</button>
</div>
<div> <div>
<h2 className="text-xl font-semibold mb-4">Eingabe</h2> <h2 className="text-xl font-semibold mb-4">Eingabe</h2>
<AusgabenForm onSuccess={handleSuccess} selectedEntry={selectedEntry} /> <AusgabenForm onSuccess={handleSuccess} selectedEntry={selectedEntry} typ={activeTab} />
<div className="mt-6 bg-white border border-black rounded-lg shadow-md p-6"> <div className="mt-6 bg-white border border-black rounded-lg shadow-md p-6">
<h3 className="text-xl font-semibold mb-4">Letzte 10 Einträge</h3> <h3 className="text-xl font-semibold mb-4">Letzte 10 Einträge</h3>

View File

@@ -1,21 +1,26 @@
'use client'; 'use client';
import { useState, useEffect } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { CreateAusgabenEntry, AusgabenEntry, ZAHLUNGSARTEN, Zahlungsart, MonthlyStats } from '@/types/ausgaben'; import { CreateAusgabenEntry, AusgabenEntry, ZAHLUNGSARTEN_HAUSHALT, ZAHLUNGSARTEN_PRIVAT, MonthlyStats } from '@/types/ausgaben';
interface AusgabenFormProps { interface AusgabenFormProps {
onSuccess: () => void; onSuccess: () => void;
selectedEntry?: AusgabenEntry | null; selectedEntry?: AusgabenEntry | null;
typ: number; // 0 = Haushalt, 1 = Privat
} }
export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormProps) { export default function AusgabenForm({ onSuccess, selectedEntry, typ }: AusgabenFormProps) {
const zahlungsarten = typ === 0 ? ZAHLUNGSARTEN_HAUSHALT : ZAHLUNGSARTEN_PRIVAT;
const defaultZahlungsart = typ === 0 ? 'ECR' : 'bar';
const [formData, setFormData] = useState<CreateAusgabenEntry>({ const [formData, setFormData] = useState<CreateAusgabenEntry>({
Datum: '', Datum: '',
WochTag: '', WochTag: '',
Wo: '', Wo: '',
Was: '', Was: '',
Wieviel: '', Wieviel: '',
Wie: 'EC-R', Wie: defaultZahlungsart,
TYP: typ,
OK: 0, OK: 0,
}); });
@@ -28,22 +33,12 @@ export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormP
const [year, setYear] = useState(''); const [year, setYear] = useState('');
const [isLoadingStats, setIsLoadingStats] = useState(false); const [isLoadingStats, setIsLoadingStats] = useState(false);
// Initialize stats with current month/year const fetchStats = useCallback(async (y: string, m: string) => {
useEffect(() => {
const now = new Date();
const currentMonth = String(now.getMonth() + 1).padStart(2, '0');
const currentYear = String(now.getFullYear());
setMonth(currentMonth);
setYear(currentYear);
fetchStats(currentYear, currentMonth);
}, []);
const fetchStats = async (y: string, m: string) => {
if (!y || !m) return; if (!y || !m) return;
setIsLoadingStats(true); setIsLoadingStats(true);
try { try {
const response = await fetch(`/api/ausgaben/stats?year=${y}&month=${m}`); const response = await fetch(`/api/ausgaben/stats?year=${y}&month=${m}&typ=${typ}`);
const data = await response.json(); const data = await response.json();
if (data.success) { if (data.success) {
setStats(data.data); setStats(data.data);
@@ -53,16 +48,30 @@ export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormP
} finally { } finally {
setIsLoadingStats(false); setIsLoadingStats(false);
} }
}; }, [typ]);
// Initialize month/year on first load
useEffect(() => {
const now = new Date();
const currentMonth = String(now.getMonth() + 1).padStart(2, '0');
const currentYear = String(now.getFullYear());
setMonth(currentMonth);
setYear(currentYear);
}, []);
// Fetch stats when month, year, or typ changes
useEffect(() => {
if (month && year) {
fetchStats(year, month);
}
}, [month, year, typ, fetchStats]);
const handleMonthChange = (newMonth: string) => { const handleMonthChange = (newMonth: string) => {
setMonth(newMonth); setMonth(newMonth);
fetchStats(year, newMonth);
}; };
const handleYearChange = (newYear: string) => { const handleYearChange = (newYear: string) => {
setYear(newYear); setYear(newYear);
fetchStats(newYear, month);
}; };
const formatAmount = (amount: number | null) => { const formatAmount = (amount: number | null) => {
@@ -85,6 +94,7 @@ export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormP
Was: selectedEntry.Was, Was: selectedEntry.Was,
Wieviel: selectedEntry.Wieviel.toString(), Wieviel: selectedEntry.Wieviel.toString(),
Wie: selectedEntry.Wie, Wie: selectedEntry.Wie,
TYP: selectedEntry.TYP,
OK: selectedEntry.OK || 0, OK: selectedEntry.OK || 0,
}); });
@@ -99,11 +109,12 @@ export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormP
...prev, ...prev,
Datum: dateStr, Datum: dateStr,
WochTag: weekday, WochTag: weekday,
TYP: typ,
})); }));
setEditId(null); setEditId(null);
} }
}, [selectedEntry]); }, [selectedEntry, typ]);
const getWeekday = (date: Date): string => { const getWeekday = (date: Date): string => {
const weekdays = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']; const weekdays = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
@@ -167,7 +178,8 @@ export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormP
Wo: '', Wo: '',
Was: '', Was: '',
Wieviel: '', Wieviel: '',
Wie: 'EC-R', Wie: defaultZahlungsart,
TYP: typ,
OK: 0, OK: 0,
}); });
@@ -240,11 +252,11 @@ export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormP
<td className="p-2 w-38"> <td className="p-2 w-38">
<select <select
value={formData.Wie} value={formData.Wie}
onChange={(e) => setFormData({ ...formData, Wie: e.target.value as Zahlungsart })} onChange={(e) => setFormData({ ...formData, Wie: e.target.value })}
className="w-full px-2 py-1 text-base rounded border-2 border-gray-400 bg-white focus:border-blue-500 focus:outline-none" className="w-full px-2 py-1 text-base rounded border-2 border-gray-400 bg-white focus:border-blue-500 focus:outline-none"
required required
> >
{ZAHLUNGSARTEN.map((za) => ( {zahlungsarten.map((za) => (
<option key={za.value} value={za.value}> <option key={za.value} value={za.value}>
{za.label} {za.label}
</option> </option>
@@ -281,7 +293,7 @@ export default function AusgabenForm({ onSuccess, selectedEntry }: AusgabenFormP
</td> </td>
</tr> </tr>
<tr> <tr>
<td colSpan={6} className="p-3 pt-6 border-t border-gray-300"> <td colSpan={6} className="p-3 pt-6 border-t border-black">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex gap-4 items-center"> <div className="flex gap-4 items-center">
<label className="font-semibold">Monat:</label> <label className="font-semibold">Monat:</label>

View File

@@ -5,12 +5,20 @@
CREATE TABLE IF NOT EXISTS `Ausgaben` ( CREATE TABLE IF NOT EXISTS `Ausgaben` (
`ID` int(11) NOT NULL AUTO_INCREMENT, `ID` int(11) NOT NULL AUTO_INCREMENT,
`Datum` date NOT NULL, `Datum` date NOT NULL,
`WochTag` varchar(20) DEFAULT NULL,
`Wo` varchar(255) DEFAULT NULL, `Wo` varchar(255) DEFAULT NULL,
`Was` varchar(500) DEFAULT NULL, `Was` varchar(500) DEFAULT NULL,
`Wieviel` decimal(10,2) NOT NULL, `Wieviel` decimal(10,2) NOT NULL,
`Wie` varchar(50) DEFAULT NULL, `Wie` varchar(50) DEFAULT NULL,
`TYP` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0=Haushalt, 1=Privat',
`OK` tinyint(1) DEFAULT 0, `OK` tinyint(1) DEFAULT 0,
PRIMARY KEY (`ID`), PRIMARY KEY (`ID`),
KEY `idx_datum` (`Datum`) KEY `idx_datum` (`Datum`),
KEY `idx_typ` (`TYP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Upgrade existing table: Add TYP column if it doesn't exist
ALTER TABLE `Ausgaben` ADD COLUMN IF NOT EXISTS `TYP` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0=Haushalt, 1=Privat' AFTER `Wie`;
ALTER TABLE `Ausgaben` ADD INDEX IF NOT EXISTS `idx_typ` (`TYP`);
-- Remove WochTag column if it exists (no longer stored in DB, calculated from Datum)
-- ALTER TABLE `Ausgaben` DROP COLUMN IF EXISTS `WochTag`;

View File

@@ -8,6 +8,7 @@ export interface AusgabenEntry {
Was: string; Was: string;
Wieviel: number; Wieviel: number;
Wie: string; Wie: string;
TYP: number;
OK?: number; OK?: number;
} }
@@ -18,26 +19,49 @@ export interface CreateAusgabenEntry {
Was: string; Was: string;
Wieviel: string | number; Wieviel: string | number;
Wie: string; Wie: string;
TYP: number;
OK?: number; OK?: number;
} }
export interface MonthlyStats { export interface MonthlyStats {
totalAusgaben: number; totalAusgaben: number;
ECR: number; ECR?: number;
ECB: number; ECB?: number;
barR: number; barR?: number;
barB: number; barB?: number;
bar?: number;
EC?: number;
VISA?: number;
MASTER?: number;
Einnahmen: number; Einnahmen: number;
Ueberweisungen: number; Ueberweisungen: number;
} }
export type Zahlungsart = 'EC-R' | 'EC-B' | 'bar-R' | 'bar-B' | 'Einnahme' | 'Ueber'; // Haushalt Zahlungsarten (TYP = 0)
// Verwende alte Formate ohne Bindestriche für Kompatibilität mit bestehenden Daten
export type ZahlungsartHaushalt = 'ECR' | 'ECB' | 'barR' | 'barB' | 'Ein' | 'Uber';
export const ZAHLUNGSARTEN: { value: Zahlungsart; label: string }[] = [ export const ZAHLUNGSARTEN_HAUSHALT: { value: ZahlungsartHaushalt; label: string }[] = [
{ value: 'EC-R', label: 'EC-R' }, { value: 'ECR', label: 'EC-R' },
{ value: 'EC-B', label: 'EC-B' }, { value: 'ECB', label: 'EC-B' },
{ value: 'bar-R', label: 'bar-R' }, { value: 'barR', label: 'bar-R' },
{ value: 'bar-B', label: 'bar-B' }, { value: 'barB', label: 'bar-B' },
{ value: 'Einnahme', label: 'Einnahme' }, { value: 'Ein', label: 'Einnahme' },
{ value: 'Ueber', label: 'Überweisung' }, { value: 'Uber', label: 'Überweisung' },
]; ];
// Privat Zahlungsarten (TYP = 1)
export type ZahlungsartPrivat = 'bar' | 'EC' | 'VISA' | 'MASTER' | 'Einnahme' | 'Uber';
export const ZAHLUNGSARTEN_PRIVAT: { value: ZahlungsartPrivat; label: string }[] = [
{ value: 'bar', label: 'bar' },
{ value: 'EC', label: 'EC' },
{ value: 'VISA', label: 'VISA' },
{ value: 'MASTER', label: 'Master' },
{ value: 'Einnahme', label: 'Einnahme' },
{ value: 'Uber', label: 'Überweisung' },
];
// Legacy exports for backward compatibility
export type Zahlungsart = ZahlungsartHaushalt | ZahlungsartPrivat;
export const ZAHLUNGSARTEN = ZAHLUNGSARTEN_HAUSHALT;

23
upgrade_typ.sql Normal file
View File

@@ -0,0 +1,23 @@
-- Upgrade-Skript: TYP-Spalte zur Ausgaben-Tabelle hinzufügen
-- Dieses Skript fügt die TYP-Spalte zur bestehenden Tabelle hinzu
-- TYP-Spalte hinzufügen (falls nicht vorhanden)
ALTER TABLE `Ausgaben`
ADD COLUMN IF NOT EXISTS `TYP` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0=Haushalt, 1=Privat' AFTER `Wie`;
-- Index für TYP hinzufügen (falls nicht vorhanden)
ALTER TABLE `Ausgaben`
ADD INDEX IF NOT EXISTS `idx_typ` (`TYP`);
-- Bestehende Einträge: Setze TYP=0 (Haushalt) für alle Einträge ohne TYP
UPDATE `Ausgaben`
SET `TYP` = 0
WHERE `TYP` IS NULL OR `TYP` = 0;
-- Optional: WochTag-Spalte entfernen (falls vorhanden)
-- Der Wochentag wird jetzt dynamisch aus dem Datum berechnet
-- Kommentieren Sie die folgende Zeile aus, wenn Sie die Spalte entfernen möchten:
-- ALTER TABLE `Ausgaben` DROP COLUMN IF EXISTS `WochTag`;
-- Zeige Struktur der Tabelle
DESCRIBE `Ausgaben`;