Files
TimeReg/lib/services/time_service.dart
T
steinhelge c829f78984 Initial commit: TimeReg Flutter app med Firebase backend
- Opprettet Flutter-prosjekt med alle nødvendige avhengigheter
- Implementert datamodeller (User, TimeRegistration, TariffProfile, Deviation, AuditLog)
- Implementert tjenester (AuthService, TimeService)
- Implementert Riverpod providers for state management
- Opprettet autentiseringsskjermer (login, signup, reset password)
- Opprettet hjemmeskjerm med timer-funksjonalitet
- Opprettet placeholder-skjermer for historikk, rapporter og profil
- Lagt til norsk dokumentasjon i README
2025-11-24 20:52:27 +01:00

323 lines
9.0 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:uuid/uuid.dart';
import '../models/time_registration.dart';
class TimeService {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final Uuid _uuid = const Uuid();
// Start ny timeregistrering (stempling)
Future<String> startTimer({
required String userId,
required String organizationId,
RegistrationType type = RegistrationType.ordinary,
String? projectId,
String? customerId,
String? comment,
}) async {
try {
final registration = TimeRegistration(
id: _uuid.v4(),
userId: userId,
organizationId: organizationId,
startTime: DateTime.now(),
endTime: null, // Ikke avsluttet ennå
duration: 0,
type: type,
projectId: projectId,
customerId: customerId,
comment: comment,
breaks: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
createdBy: userId,
isManual: false,
);
await _firestore
.collection('time_registrations')
.doc(registration.id)
.set(registration.toFirestore());
return registration.id;
} catch (e) {
throw Exception('Kunne ikke starte timer: $e');
}
}
// Stopp pågående timeregistrering
Future<void> stopTimer(String registrationId, String userId) async {
try {
final doc = await _firestore
.collection('time_registrations')
.doc(registrationId)
.get();
if (!doc.exists) {
throw Exception('Timeregistrering ikke funnet');
}
final registration = TimeRegistration.fromFirestore(doc);
final endTime = DateTime.now();
final duration = endTime.difference(registration.startTime).inMinutes;
await _firestore
.collection('time_registrations')
.doc(registrationId)
.update({
'endTime': Timestamp.fromDate(endTime),
'duration': duration,
'updatedAt': Timestamp.fromDate(DateTime.now()),
'lastModifiedBy': userId,
});
} catch (e) {
throw Exception('Kunne ikke stoppe timer: $e');
}
}
// Opprett manuell timeregistrering
Future<String> createManualEntry({
required String userId,
required String organizationId,
required DateTime startTime,
required DateTime endTime,
RegistrationType type = RegistrationType.ordinary,
List<TimeBreak> breaks = const [],
String? projectId,
String? customerId,
String? comment,
}) async {
try {
final duration = endTime.difference(startTime).inMinutes;
final registration = TimeRegistration(
id: _uuid.v4(),
userId: userId,
organizationId: organizationId,
startTime: startTime,
endTime: endTime,
duration: duration,
type: type,
projectId: projectId,
customerId: customerId,
comment: comment,
breaks: breaks,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
createdBy: userId,
isManual: true,
);
await _firestore
.collection('time_registrations')
.doc(registration.id)
.set(registration.toFirestore());
return registration.id;
} catch (e) {
throw Exception('Kunne ikke opprette timeregistrering: $e');
}
}
// Oppdater eksisterende timeregistrering
Future<void> updateRegistration({
required String registrationId,
required String userId,
DateTime? startTime,
DateTime? endTime,
RegistrationType? type,
List<TimeBreak>? breaks,
String? projectId,
String? customerId,
String? comment,
}) async {
try {
final doc = await _firestore
.collection('time_registrations')
.doc(registrationId)
.get();
if (!doc.exists) {
throw Exception('Timeregistrering ikke funnet');
}
final registration = TimeRegistration.fromFirestore(doc);
final Map<String, dynamic> updates = {
'updatedAt': Timestamp.fromDate(DateTime.now()),
'lastModifiedBy': userId,
};
if (startTime != null) {
updates['startTime'] = Timestamp.fromDate(startTime);
}
if (endTime != null) {
updates['endTime'] = Timestamp.fromDate(endTime);
}
if (type != null) {
updates['type'] = type.name;
}
if (breaks != null) {
updates['breaks'] = breaks.map((b) => b.toMap()).toList();
}
if (projectId != null) {
updates['projectId'] = projectId;
}
if (customerId != null) {
updates['customerId'] = customerId;
}
if (comment != null) {
updates['comment'] = comment;
}
// Beregn ny varighet hvis start eller slutt er endret
final newStartTime = startTime ?? registration.startTime;
final newEndTime = endTime ?? registration.endTime;
if (newEndTime != null) {
updates['duration'] = newEndTime.difference(newStartTime).inMinutes;
}
await _firestore
.collection('time_registrations')
.doc(registrationId)
.update(updates);
} catch (e) {
throw Exception('Kunne ikke oppdatere timeregistrering: $e');
}
}
// Slett timeregistrering
Future<void> deleteRegistration(String registrationId) async {
try {
await _firestore
.collection('time_registrations')
.doc(registrationId)
.delete();
} catch (e) {
throw Exception('Kunne ikke slette timeregistrering: $e');
}
}
// Hent alle timeregistreringer for bruker i en periode
Future<List<TimeRegistration>> getRegistrations({
required String userId,
DateTime? startDate,
DateTime? endDate,
}) async {
try {
Query query = _firestore
.collection('time_registrations')
.where('userId', isEqualTo: userId);
if (startDate != null) {
query = query.where('startTime',
isGreaterThanOrEqualTo: Timestamp.fromDate(startDate));
}
if (endDate != null) {
query = query.where('startTime',
isLessThanOrEqualTo: Timestamp.fromDate(endDate));
}
final snapshot = await query.orderBy('startTime', descending: true).get();
return snapshot.docs
.map((doc) => TimeRegistration.fromFirestore(doc))
.toList();
} catch (e) {
throw Exception('Kunne ikke hente timeregistreringer: $e');
}
}
// Hent timeregistreringer for en spesifikk dag
Future<List<TimeRegistration>> getRegistrationsByDay({
required String userId,
required DateTime date,
}) async {
final startOfDay = DateTime(date.year, date.month, date.day);
final endOfDay = DateTime(date.year, date.month, date.day, 23, 59, 59);
return getRegistrations(
userId: userId,
startDate: startOfDay,
endDate: endOfDay,
);
}
// Hent pågående timeregistrering
Future<TimeRegistration?> getActiveRegistration(String userId) async {
try {
final snapshot = await _firestore
.collection('time_registrations')
.where('userId', isEqualTo: userId)
.where('endTime', isNull: true)
.limit(1)
.get();
if (snapshot.docs.isNotEmpty) {
return TimeRegistration.fromFirestore(snapshot.docs.first);
}
return null;
} catch (e) {
throw Exception('Kunne ikke hente aktiv timeregistrering: $e');
}
}
// Stream av timeregistreringer for bruker
Stream<List<TimeRegistration>> registrationsStream({
required String userId,
DateTime? startDate,
DateTime? endDate,
}) {
Query query = _firestore
.collection('time_registrations')
.where('userId', isEqualTo: userId);
if (startDate != null) {
query = query.where('startTime',
isGreaterThanOrEqualTo: Timestamp.fromDate(startDate));
}
if (endDate != null) {
query = query.where('startTime',
isLessThanOrEqualTo: Timestamp.fromDate(endDate));
}
return query.orderBy('startTime', descending: true).snapshots().map(
(snapshot) => snapshot.docs
.map((doc) => TimeRegistration.fromFirestore(doc))
.toList(),
);
}
// Beregn total arbeidstid for en periode
Future<Map<String, int>> calculateTotalHours({
required String userId,
required DateTime startDate,
required DateTime endDate,
}) async {
final registrations = await getRegistrations(
userId: userId,
startDate: startDate,
endDate: endDate,
);
int totalMinutes = 0;
int ordinaryMinutes = 0;
int overtimeMinutes = 0;
for (final reg in registrations) {
if (reg.endTime != null) {
totalMinutes += reg.netWorkMinutes;
if (reg.type == RegistrationType.ordinary) {
ordinaryMinutes += reg.netWorkMinutes;
} else if (reg.type == RegistrationType.overtime) {
overtimeMinutes += reg.netWorkMinutes;
}
}
}
return {
'total': totalMinutes,
'ordinary': ordinaryMinutes,
'overtime': overtimeMinutes,
};
}
}