You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
6.2 KiB
Dart

import 'dart:typed_data';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/work_day.dart';
import '../models/work_interval.dart';
import '../models/month_start.dart';
import '../utils/helpers.dart';
class BookingApi {
final http.Client client;
BookingApi({required this.client});
/// Monatliche Buchungen holen (für YYYY-MM)
Future<List<WorkDay>> getBookingList(DateTime monthStart) async {
final y = monthStart.year.toString().padLeft(4, '0');
final m = monthStart.month.toString().padLeft(2, '0');
final uri = Uri.parse(
'https://api.windesign.at/workinghours.php?module=booking&function=getList&date=$y-$m',
);
final res = await client.get(uri);
if (res.statusCode != 200) {
throw Exception('booking/getList failed: ${res.statusCode} ${res.body}');
}
final map = jsonDecode(res.body) as Map<String, dynamic>;
if (map['error'] == true) {
throw Exception('booking/getList error: ${map['errmsg']}');
}
final list = (map['bookings'] as List?) ?? const [];
final items = <WorkDay>[];
for (final e in list) {
final row = e as Map<String, dynamic>;
final dayStr = (row['bookingDay'] as String?) ?? '';
final date = DateTime.tryParse(dayStr);
if (date == null) continue;
String? code = (row['code'] as String?)?.trim();
if (code != null && code.isEmpty) code = null;
final starts = <String?>[
row['come1'] as String?, row['come2'] as String?,
row['come3'] as String?, row['come4'] as String?, row['come5'] as String?,
];
final ends = <String?>[
row['leave1'] as String?, row['leave2'] as String?,
row['leave3'] as String?, row['leave4'] as String?, row['leave5'] as String?,
];
final intervals = <WorkInterval>[];
for (int i = 0; i < 5; i++) {
final s = parseDbTime(starts[i]);
final e2 = parseDbTime(ends[i]);
if (s != null && e2 != null) {
intervals.add(WorkInterval(s, e2));
}
}
items.add(WorkDay(
date: DateTime(date.year, date.month, date.day),
intervals: intervals,
targetMinutes: 0, // wird im UI mit Tagesplan/Feiertag ersetzt
code: code,
));
}
return items;
}
/// Monatliche Startdaten (Startsaldo, Vacation, Overtime, Correction)
Future<MonthStart> getMonthStart(DateTime monthStart) async {
final y = monthStart.year.toString().padLeft(4, '0');
final m = monthStart.month.toString().padLeft(2, '0');
final uri = Uri.parse(
'https://api.windesign.at/workinghours.php'
'?module=monthlybooking&function=getList&date=$y-$m-01',
);
final res = await client.get(uri);
if (res.statusCode != 200) {
throw Exception('monthlybooking/getList failed: ${res.statusCode} ${res.body}');
}
final map = jsonDecode(res.body) as Map<String, dynamic>;
if (map['error'] == true) {
throw Exception('monthlybooking/getList error: ${map['errmsg']}');
}
return MonthStart.fromJson(map);
}
/// Einen Tag speichern: date (YYYY-MM-DD), code, come1..leave5 ("HH:mm" oder null/leer)
Future<void> saveDay(WorkDay day) async {
final y = day.date.year.toString().padLeft(4, '0');
final m = day.date.month.toString().padLeft(2, '0');
final d = day.date.day.toString().padLeft(2, '0');
final dateStr = '$y-$m-$d';
// Intervalle in 5 Slots abbilden
final starts = List<String?>.filled(5, null);
final ends = List<String?>.filled(5, null);
for (int i = 0; i < day.intervals.length && i < 5; i++) {
starts[i] = fmtTimeOfDay(day.intervals[i].start);
ends[i] = fmtTimeOfDay(day.intervals[i].end);
}
// Codes, die Zeiten serverseitig leeren
final lockCodes = {'G', 'U', 'SU', 'K'};
final payload = <String, dynamic>{
'module': 'booking',
'function': 'saveDay',
'date': dateStr,
'code': day.code, // null → wird als NULL gespeichert
'come1': lockCodes.contains(day.code) ? null : starts[0],
'leave1': lockCodes.contains(day.code) ? null : ends[0],
'come2': lockCodes.contains(day.code) ? null : starts[1],
'leave2': lockCodes.contains(day.code) ? null : ends[1],
'come3': lockCodes.contains(day.code) ? null : starts[2],
'leave3': lockCodes.contains(day.code) ? null : ends[2],
'come4': lockCodes.contains(day.code) ? null : starts[3],
'leave4': lockCodes.contains(day.code) ? null : ends[3],
'come5': lockCodes.contains(day.code) ? null : starts[4],
'leave5': lockCodes.contains(day.code) ? null : ends[4],
};
final uri = Uri.parse('https://api.windesign.at/workinghours.php');
final res = await client.post(
uri,
headers: {'Content-Type': 'application/json'},
body: jsonEncode(payload),
);
if (res.statusCode != 200) {
throw Exception('booking/saveDay failed: ${res.statusCode} ${res.body}');
}
final map = jsonDecode(res.body) as Map<String, dynamic>;
if (map['error'] == true) {
throw Exception('booking/saveDay error: ${map['errmsg']}');
}
}
/// Startwerte für einen Monat speichern (Upsert auf monthlybooking).
/// `monthStart` = 1. des Monats.
Future<void> saveMonthStart(
DateTime monthStart, {
required int starthours,
required int startvacation,
int overtime = 0,
int correction = 0,
}) async {
final y = monthStart.year.toString().padLeft(4, '0');
final m = monthStart.month.toString().padLeft(2, '0');
final d = '01'; // normalize
final uri = Uri.parse('https://api.windesign.at/workinghours.php');
final payload = {
'module': 'monthlybooking',
'function': 'saveStart',
'date': '$y-$m-$d',
'starthours': starthours,
'startvacation': startvacation,
'overtime': overtime,
'correction': correction,
};
final res = await client.post(
uri,
headers: {'Content-Type': 'application/json'},
body: jsonEncode(payload),
);
if (res.statusCode != 200) {
throw Exception('monthlybooking/saveStart failed: ${res.statusCode} ${res.body}');
}
final map = jsonDecode(res.body) as Map<String, dynamic>;
if (map['error'] == true) {
throw Exception('monthlybooking/saveStart error: ${map['errmsg']}');
}
}
}