fix: add ignore_for_file to data connect Repos and modify CI to avoid analyzing deleted files
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../domain/repositories/billing_connector_repository.dart';
|
||||
@@ -13,7 +15,7 @@ class BillingConnectorRepositoryImpl implements BillingConnectorRepository {
|
||||
@override
|
||||
Future<List<BusinessBankAccount>> getBankAccounts({required String businessId}) async {
|
||||
return _service.run(() async {
|
||||
final result = await _service.connector
|
||||
final QueryResult<dc.GetAccountsByOwnerIdData, dc.GetAccountsByOwnerIdVariables> result = await _service.connector
|
||||
.getAccountsByOwnerId(ownerId: businessId)
|
||||
.execute();
|
||||
|
||||
@@ -24,21 +26,21 @@ class BillingConnectorRepositoryImpl implements BillingConnectorRepository {
|
||||
@override
|
||||
Future<double> getCurrentBillAmount({required String businessId}) async {
|
||||
return _service.run(() async {
|
||||
final result = await _service.connector
|
||||
final QueryResult<dc.ListInvoicesByBusinessIdData, dc.ListInvoicesByBusinessIdVariables> result = await _service.connector
|
||||
.listInvoicesByBusinessId(businessId: businessId)
|
||||
.execute();
|
||||
|
||||
return result.data.invoices
|
||||
.map(_mapInvoice)
|
||||
.where((i) => i.status == InvoiceStatus.open)
|
||||
.fold<double>(0.0, (sum, item) => sum + item.totalAmount);
|
||||
.where((Invoice i) => i.status == InvoiceStatus.open)
|
||||
.fold<double>(0.0, (double sum, Invoice item) => sum + item.totalAmount);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Invoice>> getInvoiceHistory({required String businessId}) async {
|
||||
return _service.run(() async {
|
||||
final result = await _service.connector
|
||||
final QueryResult<dc.ListInvoicesByBusinessIdData, dc.ListInvoicesByBusinessIdVariables> result = await _service.connector
|
||||
.listInvoicesByBusinessId(businessId: businessId)
|
||||
.limit(10)
|
||||
.execute();
|
||||
@@ -50,13 +52,13 @@ class BillingConnectorRepositoryImpl implements BillingConnectorRepository {
|
||||
@override
|
||||
Future<List<Invoice>> getPendingInvoices({required String businessId}) async {
|
||||
return _service.run(() async {
|
||||
final result = await _service.connector
|
||||
final QueryResult<dc.ListInvoicesByBusinessIdData, dc.ListInvoicesByBusinessIdVariables> result = await _service.connector
|
||||
.listInvoicesByBusinessId(businessId: businessId)
|
||||
.execute();
|
||||
|
||||
return result.data.invoices
|
||||
.map(_mapInvoice)
|
||||
.where((i) =>
|
||||
.where((Invoice i) =>
|
||||
i.status == InvoiceStatus.open || i.status == InvoiceStatus.disputed)
|
||||
.toList();
|
||||
});
|
||||
@@ -83,7 +85,7 @@ class BillingConnectorRepositoryImpl implements BillingConnectorRepository {
|
||||
end = DateTime(now.year, now.month + 1, 0, 23, 59, 59);
|
||||
}
|
||||
|
||||
final result = await _service.connector
|
||||
final QueryResult<dc.ListShiftRolesByBusinessAndDatesSummaryData, dc.ListShiftRolesByBusinessAndDatesSummaryVariables> result = await _service.connector
|
||||
.listShiftRolesByBusinessAndDatesSummary(
|
||||
businessId: businessId,
|
||||
start: _service.toTimestamp(start),
|
||||
@@ -91,17 +93,17 @@ class BillingConnectorRepositoryImpl implements BillingConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final shiftRoles = result.data.shiftRoles;
|
||||
if (shiftRoles.isEmpty) return [];
|
||||
final List<dc.ListShiftRolesByBusinessAndDatesSummaryShiftRoles> shiftRoles = result.data.shiftRoles;
|
||||
if (shiftRoles.isEmpty) return <InvoiceItem>[];
|
||||
|
||||
final Map<String, _RoleSummary> summary = {};
|
||||
for (final role in shiftRoles) {
|
||||
final roleId = role.roleId;
|
||||
final roleName = role.role.name;
|
||||
final hours = role.hours ?? 0.0;
|
||||
final totalValue = role.totalValue ?? 0.0;
|
||||
final Map<String, _RoleSummary> summary = <String, _RoleSummary>{};
|
||||
for (final dc.ListShiftRolesByBusinessAndDatesSummaryShiftRoles role in shiftRoles) {
|
||||
final String roleId = role.roleId;
|
||||
final String roleName = role.role.name;
|
||||
final double hours = role.hours ?? 0.0;
|
||||
final double totalValue = role.totalValue ?? 0.0;
|
||||
|
||||
final existing = summary[roleId];
|
||||
final _RoleSummary? existing = summary[roleId];
|
||||
if (existing == null) {
|
||||
summary[roleId] = _RoleSummary(
|
||||
roleId: roleId,
|
||||
@@ -118,7 +120,7 @@ class BillingConnectorRepositoryImpl implements BillingConnectorRepository {
|
||||
}
|
||||
|
||||
return summary.values
|
||||
.map((item) => InvoiceItem(
|
||||
.map((_RoleSummary item) => InvoiceItem(
|
||||
id: item.roleId,
|
||||
invoiceId: item.roleId,
|
||||
staffId: item.roleName,
|
||||
@@ -197,3 +199,4 @@ class _RoleSummary {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
@@ -20,7 +22,7 @@ class CoverageConnectorRepositoryImpl implements CoverageConnectorRepository {
|
||||
final DateTime start = DateTime(date.year, date.month, date.day);
|
||||
final DateTime end = DateTime(date.year, date.month, date.day, 23, 59, 59, 999);
|
||||
|
||||
final shiftRolesResult = await _service.connector
|
||||
final QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData, dc.ListShiftRolesByBusinessAndDateRangeVariables> shiftRolesResult = await _service.connector
|
||||
.listShiftRolesByBusinessAndDateRange(
|
||||
businessId: businessId,
|
||||
start: _service.toTimestamp(start),
|
||||
@@ -28,7 +30,7 @@ class CoverageConnectorRepositoryImpl implements CoverageConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final applicationsResult = await _service.connector
|
||||
final QueryResult<dc.ListStaffsApplicationsByBusinessForDayData, dc.ListStaffsApplicationsByBusinessForDayVariables> applicationsResult = await _service.connector
|
||||
.listStaffsApplicationsByBusinessForDay(
|
||||
businessId: businessId,
|
||||
dayStart: _service.toTimestamp(start),
|
||||
@@ -49,13 +51,13 @@ class CoverageConnectorRepositoryImpl implements CoverageConnectorRepository {
|
||||
List<dynamic> applications,
|
||||
DateTime date,
|
||||
) {
|
||||
if (shiftRoles.isEmpty && applications.isEmpty) return [];
|
||||
if (shiftRoles.isEmpty && applications.isEmpty) return <CoverageShift>[];
|
||||
|
||||
final Map<String, _CoverageGroup> groups = {};
|
||||
final Map<String, _CoverageGroup> groups = <String, _CoverageGroup>{};
|
||||
|
||||
for (final sr in shiftRoles) {
|
||||
final String key = '${sr.shiftId}:${sr.roleId}';
|
||||
final startTime = _service.toDateTime(sr.startTime);
|
||||
final DateTime? startTime = _service.toDateTime(sr.startTime);
|
||||
|
||||
groups[key] = _CoverageGroup(
|
||||
shiftId: sr.shiftId,
|
||||
@@ -65,14 +67,14 @@ class CoverageConnectorRepositoryImpl implements CoverageConnectorRepository {
|
||||
startTime: startTime != null ? DateFormat('HH:mm').format(startTime) : '00:00',
|
||||
workersNeeded: sr.count,
|
||||
date: _service.toDateTime(sr.shift.date) ?? date,
|
||||
workers: [],
|
||||
workers: <CoverageWorker>[],
|
||||
);
|
||||
}
|
||||
|
||||
for (final app in applications) {
|
||||
final String key = '${app.shiftId}:${app.roleId}';
|
||||
if (!groups.containsKey(key)) {
|
||||
final startTime = _service.toDateTime(app.shiftRole.startTime);
|
||||
final DateTime? startTime = _service.toDateTime(app.shiftRole.startTime);
|
||||
groups[key] = _CoverageGroup(
|
||||
shiftId: app.shiftId,
|
||||
roleId: app.roleId,
|
||||
@@ -81,11 +83,11 @@ class CoverageConnectorRepositoryImpl implements CoverageConnectorRepository {
|
||||
startTime: startTime != null ? DateFormat('HH:mm').format(startTime) : '00:00',
|
||||
workersNeeded: app.shiftRole.count,
|
||||
date: _service.toDateTime(app.shiftRole.shift.date) ?? date,
|
||||
workers: [],
|
||||
workers: <CoverageWorker>[],
|
||||
);
|
||||
}
|
||||
|
||||
final checkIn = _service.toDateTime(app.checkInTime);
|
||||
final DateTime? checkIn = _service.toDateTime(app.checkInTime);
|
||||
groups[key]!.workers.add(
|
||||
CoverageWorker(
|
||||
name: app.staff.fullName,
|
||||
@@ -96,7 +98,7 @@ class CoverageConnectorRepositoryImpl implements CoverageConnectorRepository {
|
||||
}
|
||||
|
||||
return groups.values
|
||||
.map((g) => CoverageShift(
|
||||
.map((_CoverageGroup g) => CoverageShift(
|
||||
id: '${g.shiftId}:${g.roleId}',
|
||||
title: g.title,
|
||||
location: g.location,
|
||||
@@ -153,3 +155,4 @@ class _CoverageGroup {
|
||||
final DateTime date;
|
||||
final List<CoverageWorker> workers;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../domain/repositories/home_connector_repository.dart';
|
||||
@@ -13,13 +15,13 @@ class HomeConnectorRepositoryImpl implements HomeConnectorRepository {
|
||||
@override
|
||||
Future<HomeDashboardData> getDashboardData({required String businessId}) async {
|
||||
return _service.run(() async {
|
||||
final now = DateTime.now();
|
||||
final daysFromMonday = now.weekday - DateTime.monday;
|
||||
final monday = DateTime(now.year, now.month, now.day).subtract(Duration(days: daysFromMonday));
|
||||
final weekRangeStart = monday;
|
||||
final weekRangeEnd = monday.add(const Duration(days: 13, hours: 23, minutes: 59, seconds: 59));
|
||||
final DateTime now = DateTime.now();
|
||||
final int daysFromMonday = now.weekday - DateTime.monday;
|
||||
final DateTime monday = DateTime(now.year, now.month, now.day).subtract(Duration(days: daysFromMonday));
|
||||
final DateTime weekRangeStart = monday;
|
||||
final DateTime weekRangeEnd = monday.add(const Duration(days: 13, hours: 23, minutes: 59, seconds: 59));
|
||||
|
||||
final completedResult = await _service.connector
|
||||
final QueryResult<dc.GetCompletedShiftsByBusinessIdData, dc.GetCompletedShiftsByBusinessIdVariables> completedResult = await _service.connector
|
||||
.getCompletedShiftsByBusinessId(
|
||||
businessId: businessId,
|
||||
dateFrom: _service.toTimestamp(weekRangeStart),
|
||||
@@ -32,14 +34,14 @@ class HomeConnectorRepositoryImpl implements HomeConnectorRepository {
|
||||
int weeklyShifts = 0;
|
||||
int next7DaysScheduled = 0;
|
||||
|
||||
for (final shift in completedResult.data.shifts) {
|
||||
final shiftDate = _service.toDateTime(shift.date);
|
||||
for (final dc.GetCompletedShiftsByBusinessIdShifts shift in completedResult.data.shifts) {
|
||||
final DateTime? shiftDate = _service.toDateTime(shift.date);
|
||||
if (shiftDate == null) continue;
|
||||
|
||||
final offset = shiftDate.difference(weekRangeStart).inDays;
|
||||
final int offset = shiftDate.difference(weekRangeStart).inDays;
|
||||
if (offset < 0 || offset > 13) continue;
|
||||
|
||||
final cost = shift.cost ?? 0.0;
|
||||
final double cost = shift.cost ?? 0.0;
|
||||
if (offset <= 6) {
|
||||
weeklySpending += cost;
|
||||
weeklyShifts += 1;
|
||||
@@ -49,10 +51,10 @@ class HomeConnectorRepositoryImpl implements HomeConnectorRepository {
|
||||
}
|
||||
}
|
||||
|
||||
final start = DateTime(now.year, now.month, now.day);
|
||||
final end = start.add(const Duration(hours: 23, minutes: 59, seconds: 59));
|
||||
final DateTime start = DateTime(now.year, now.month, now.day);
|
||||
final DateTime end = start.add(const Duration(hours: 23, minutes: 59, seconds: 59));
|
||||
|
||||
final result = await _service.connector
|
||||
final QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData, dc.ListShiftRolesByBusinessAndDateRangeVariables> result = await _service.connector
|
||||
.listShiftRolesByBusinessAndDateRange(
|
||||
businessId: businessId,
|
||||
start: _service.toTimestamp(start),
|
||||
@@ -62,7 +64,7 @@ class HomeConnectorRepositoryImpl implements HomeConnectorRepository {
|
||||
|
||||
int totalNeeded = 0;
|
||||
int totalFilled = 0;
|
||||
for (final shiftRole in result.data.shiftRoles) {
|
||||
for (final dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole in result.data.shiftRoles) {
|
||||
totalNeeded += shiftRole.count;
|
||||
totalFilled += shiftRole.assigned ?? 0;
|
||||
}
|
||||
@@ -81,10 +83,10 @@ class HomeConnectorRepositoryImpl implements HomeConnectorRepository {
|
||||
@override
|
||||
Future<List<ReorderItem>> getRecentReorders({required String businessId}) async {
|
||||
return _service.run(() async {
|
||||
final now = DateTime.now();
|
||||
final start = now.subtract(const Duration(days: 30));
|
||||
final DateTime now = DateTime.now();
|
||||
final DateTime start = now.subtract(const Duration(days: 30));
|
||||
|
||||
final result = await _service.connector
|
||||
final QueryResult<dc.ListShiftRolesByBusinessDateRangeCompletedOrdersData, dc.ListShiftRolesByBusinessDateRangeCompletedOrdersVariables> result = await _service.connector
|
||||
.listShiftRolesByBusinessDateRangeCompletedOrders(
|
||||
businessId: businessId,
|
||||
start: _service.toTimestamp(start),
|
||||
@@ -92,7 +94,7 @@ class HomeConnectorRepositoryImpl implements HomeConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
return result.data.shiftRoles.map((shiftRole) {
|
||||
return result.data.shiftRoles.map((dc.ListShiftRolesByBusinessDateRangeCompletedOrdersShiftRoles shiftRole) {
|
||||
final String location = shiftRole.shift.location ?? shiftRole.shift.locationAddress ?? '';
|
||||
final String type = shiftRole.shift.order.orderType.stringValue;
|
||||
return ReorderItem(
|
||||
@@ -108,3 +110,4 @@ class HomeConnectorRepositoryImpl implements HomeConnectorRepository {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||
import 'dart:convert';
|
||||
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
@@ -17,11 +19,11 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
Future<List<Hub>> getHubs({required String businessId}) async {
|
||||
return _service.run(() async {
|
||||
final String teamId = await _getOrCreateTeamId(businessId);
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.GetTeamHubsByTeamIdData, dc.GetTeamHubsByTeamIdVariables> response = await _service.connector
|
||||
.getTeamHubsByTeamId(teamId: teamId)
|
||||
.execute();
|
||||
|
||||
return response.data.teamHubs.map((h) {
|
||||
return response.data.teamHubs.map((dc.GetTeamHubsByTeamIdTeamHubs h) {
|
||||
return Hub(
|
||||
id: h.id,
|
||||
businessId: businessId,
|
||||
@@ -54,7 +56,7 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
? await _fetchPlaceAddress(placeId)
|
||||
: null;
|
||||
|
||||
final result = await _service.connector
|
||||
final OperationResult<dc.CreateTeamHubData, dc.CreateTeamHubVariables> result = await _service.connector
|
||||
.createTeamHub(
|
||||
teamId: teamId,
|
||||
hubName: name,
|
||||
@@ -101,7 +103,7 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
? await _fetchPlaceAddress(placeId)
|
||||
: null;
|
||||
|
||||
final builder = _service.connector.updateTeamHub(id: id);
|
||||
final dc.UpdateTeamHubVariablesBuilder builder = _service.connector.updateTeamHub(id: id);
|
||||
|
||||
if (name != null) builder.hubName(name);
|
||||
if (address != null) builder.address(address);
|
||||
@@ -141,7 +143,7 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
@override
|
||||
Future<void> deleteHub({required String businessId, required String id}) async {
|
||||
return _service.run(() async {
|
||||
final ordersRes = await _service.connector
|
||||
final QueryResult<dc.ListOrdersByBusinessAndTeamHubData, dc.ListOrdersByBusinessAndTeamHubVariables> ordersRes = await _service.connector
|
||||
.listOrdersByBusinessAndTeamHub(businessId: businessId, teamHubId: id)
|
||||
.execute();
|
||||
|
||||
@@ -158,7 +160,7 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
// --- HELPERS ---
|
||||
|
||||
Future<String> _getOrCreateTeamId(String businessId) async {
|
||||
final teamsRes = await _service.connector
|
||||
final QueryResult<dc.GetTeamsByOwnerIdData, dc.GetTeamsByOwnerIdVariables> teamsRes = await _service.connector
|
||||
.getTeamsByOwnerId(ownerId: businessId)
|
||||
.execute();
|
||||
|
||||
@@ -168,7 +170,7 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
|
||||
// Logic to fetch business details to create a team name if missing
|
||||
// For simplicity, we assume one exists or we create a generic one
|
||||
final createRes = await _service.connector
|
||||
final OperationResult<dc.CreateTeamData, dc.CreateTeamVariables> createRes = await _service.connector
|
||||
.createTeam(
|
||||
teamName: 'Business Team',
|
||||
ownerId: businessId,
|
||||
@@ -184,30 +186,30 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
final Uri uri = Uri.https(
|
||||
'maps.googleapis.com',
|
||||
'/maps/api/place/details/json',
|
||||
{
|
||||
<String, dynamic>{
|
||||
'place_id': placeId,
|
||||
'fields': 'address_component',
|
||||
'key': AppConfig.googleMapsApiKey,
|
||||
},
|
||||
);
|
||||
try {
|
||||
final response = await http.get(uri);
|
||||
final http.Response response = await http.get(uri);
|
||||
if (response.statusCode != 200) return null;
|
||||
|
||||
final payload = json.decode(response.body) as Map<String, dynamic>;
|
||||
final Map<String, dynamic> payload = json.decode(response.body) as Map<String, dynamic>;
|
||||
if (payload['status'] != 'OK') return null;
|
||||
|
||||
final result = payload['result'] as Map<String, dynamic>?;
|
||||
final components = result?['address_components'] as List<dynamic>?;
|
||||
final Map<String, dynamic>? result = payload['result'] as Map<String, dynamic>?;
|
||||
final List<dynamic>? components = result?['address_components'] as List<dynamic>?;
|
||||
if (components == null || components.isEmpty) return null;
|
||||
|
||||
String? streetNumber, route, city, state, country, zipCode;
|
||||
|
||||
for (var entry in components) {
|
||||
final component = entry as Map<String, dynamic>;
|
||||
final types = component['types'] as List<dynamic>? ?? [];
|
||||
final longName = component['long_name'] as String?;
|
||||
final shortName = component['short_name'] as String?;
|
||||
final Map<String, dynamic> component = entry as Map<String, dynamic>;
|
||||
final List<dynamic> types = component['types'] as List<dynamic>? ?? <dynamic>[];
|
||||
final String? longName = component['long_name'] as String?;
|
||||
final String? shortName = component['short_name'] as String?;
|
||||
|
||||
if (types.contains('street_number')) {
|
||||
streetNumber = longName;
|
||||
@@ -224,8 +226,8 @@ class HubsConnectorRepositoryImpl implements HubsConnectorRepository {
|
||||
}
|
||||
}
|
||||
|
||||
final street = [streetNumber, route]
|
||||
.where((v) => v != null && v.isNotEmpty)
|
||||
final String street = <String?>[streetNumber, route]
|
||||
.where((String? v) => v != null && v.isNotEmpty)
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
@@ -257,3 +259,4 @@ class _PlaceAddress {
|
||||
final String? country;
|
||||
final String? zipCode;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
@@ -21,25 +22,25 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final String id = businessId ?? await _service.getBusinessId();
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.ListShiftsForDailyOpsByBusinessData, dc.ListShiftsForDailyOpsByBusinessVariables> response = await _service.connector
|
||||
.listShiftsForDailyOpsByBusiness(
|
||||
businessId: id,
|
||||
date: _service.toTimestamp(date),
|
||||
)
|
||||
.execute();
|
||||
|
||||
final shifts = response.data.shifts;
|
||||
final List<dc.ListShiftsForDailyOpsByBusinessShifts> shifts = response.data.shifts;
|
||||
|
||||
int scheduledShifts = shifts.length;
|
||||
final int scheduledShifts = shifts.length;
|
||||
int workersConfirmed = 0;
|
||||
int inProgressShifts = 0;
|
||||
int completedShifts = 0;
|
||||
|
||||
final List<DailyOpsShift> dailyOpsShifts = [];
|
||||
final List<DailyOpsShift> dailyOpsShifts = <DailyOpsShift>[];
|
||||
|
||||
for (final shift in shifts) {
|
||||
for (final dc.ListShiftsForDailyOpsByBusinessShifts shift in shifts) {
|
||||
workersConfirmed += shift.filled ?? 0;
|
||||
final statusStr = shift.status?.stringValue ?? '';
|
||||
final String statusStr = shift.status?.stringValue ?? '';
|
||||
if (statusStr == 'IN_PROGRESS') inProgressShifts++;
|
||||
if (statusStr == 'COMPLETED') completedShifts++;
|
||||
|
||||
@@ -73,7 +74,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final String id = businessId ?? await _service.getBusinessId();
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.ListInvoicesForSpendByBusinessData, dc.ListInvoicesForSpendByBusinessVariables> response = await _service.connector
|
||||
.listInvoicesForSpendByBusiness(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -81,22 +82,22 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final invoices = response.data.invoices;
|
||||
final List<dc.ListInvoicesForSpendByBusinessInvoices> invoices = response.data.invoices;
|
||||
|
||||
double totalSpend = 0.0;
|
||||
int paidInvoices = 0;
|
||||
int pendingInvoices = 0;
|
||||
int overdueInvoices = 0;
|
||||
|
||||
final List<SpendInvoice> spendInvoices = [];
|
||||
final Map<DateTime, double> dailyAggregates = {};
|
||||
final Map<String, double> industryAggregates = {};
|
||||
final List<SpendInvoice> spendInvoices = <SpendInvoice>[];
|
||||
final Map<DateTime, double> dailyAggregates = <DateTime, double>{};
|
||||
final Map<String, double> industryAggregates = <String, double>{};
|
||||
|
||||
for (final inv in invoices) {
|
||||
final amount = (inv.amount ?? 0.0).toDouble();
|
||||
for (final dc.ListInvoicesForSpendByBusinessInvoices inv in invoices) {
|
||||
final double amount = (inv.amount ?? 0.0).toDouble();
|
||||
totalSpend += amount;
|
||||
|
||||
final statusStr = inv.status.stringValue;
|
||||
final String statusStr = inv.status.stringValue;
|
||||
if (statusStr == 'PAID') {
|
||||
paidInvoices++;
|
||||
} else if (statusStr == 'PENDING') {
|
||||
@@ -105,49 +106,49 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
overdueInvoices++;
|
||||
}
|
||||
|
||||
final industry = inv.vendor?.serviceSpecialty ?? 'Other';
|
||||
final String industry = inv.vendor.serviceSpecialty ?? 'Other';
|
||||
industryAggregates[industry] = (industryAggregates[industry] ?? 0.0) + amount;
|
||||
|
||||
final issueDateTime = inv.issueDate.toDateTime();
|
||||
final DateTime issueDateTime = inv.issueDate.toDateTime();
|
||||
spendInvoices.add(SpendInvoice(
|
||||
id: inv.id,
|
||||
invoiceNumber: inv.invoiceNumber ?? '',
|
||||
issueDate: issueDateTime,
|
||||
amount: amount,
|
||||
status: statusStr,
|
||||
vendorName: inv.vendor?.companyName ?? 'Unknown',
|
||||
vendorName: inv.vendor.companyName ?? 'Unknown',
|
||||
industry: industry,
|
||||
));
|
||||
|
||||
// Chart data aggregation
|
||||
final date = DateTime(issueDateTime.year, issueDateTime.month, issueDateTime.day);
|
||||
final DateTime date = DateTime(issueDateTime.year, issueDateTime.month, issueDateTime.day);
|
||||
dailyAggregates[date] = (dailyAggregates[date] ?? 0.0) + amount;
|
||||
}
|
||||
|
||||
// Ensure chart data covers all days in range
|
||||
final Map<DateTime, double> completeDailyAggregates = {};
|
||||
final Map<DateTime, double> completeDailyAggregates = <DateTime, double>{};
|
||||
for (int i = 0; i <= endDate.difference(startDate).inDays; i++) {
|
||||
final date = startDate.add(Duration(days: i));
|
||||
final normalizedDate = DateTime(date.year, date.month, date.day);
|
||||
final DateTime date = startDate.add(Duration(days: i));
|
||||
final DateTime normalizedDate = DateTime(date.year, date.month, date.day);
|
||||
completeDailyAggregates[normalizedDate] =
|
||||
dailyAggregates[normalizedDate] ?? 0.0;
|
||||
}
|
||||
|
||||
final List<SpendChartPoint> chartData = completeDailyAggregates.entries
|
||||
.map((e) => SpendChartPoint(date: e.key, amount: e.value))
|
||||
.map((MapEntry<DateTime, double> e) => SpendChartPoint(date: e.key, amount: e.value))
|
||||
.toList()
|
||||
..sort((a, b) => a.date.compareTo(b.date));
|
||||
..sort((SpendChartPoint a, SpendChartPoint b) => a.date.compareTo(b.date));
|
||||
|
||||
final List<SpendIndustryCategory> industryBreakdown = industryAggregates.entries
|
||||
.map((e) => SpendIndustryCategory(
|
||||
.map((MapEntry<String, double> e) => SpendIndustryCategory(
|
||||
name: e.key,
|
||||
amount: e.value,
|
||||
percentage: totalSpend > 0 ? (e.value / totalSpend * 100) : 0,
|
||||
))
|
||||
.toList()
|
||||
..sort((a, b) => b.amount.compareTo(a.amount));
|
||||
..sort((SpendIndustryCategory a, SpendIndustryCategory b) => b.amount.compareTo(a.amount));
|
||||
|
||||
final daysCount = endDate.difference(startDate).inDays + 1;
|
||||
final int daysCount = endDate.difference(startDate).inDays + 1;
|
||||
|
||||
return SpendReport(
|
||||
totalSpend: totalSpend,
|
||||
@@ -170,7 +171,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final String id = businessId ?? await _service.getBusinessId();
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.ListShiftsForCoverageData, dc.ListShiftsForCoverageVariables> response = await _service.connector
|
||||
.listShiftsForCoverage(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -178,36 +179,36 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final shifts = response.data.shifts;
|
||||
final List<dc.ListShiftsForCoverageShifts> shifts = response.data.shifts;
|
||||
|
||||
int totalNeeded = 0;
|
||||
int totalFilled = 0;
|
||||
final Map<DateTime, (int, int)> dailyStats = {};
|
||||
final Map<DateTime, (int, int)> dailyStats = <DateTime, (int, int)>{};
|
||||
|
||||
for (final shift in shifts) {
|
||||
final shiftDate = shift.date?.toDateTime() ?? DateTime.now();
|
||||
final date = DateTime(shiftDate.year, shiftDate.month, shiftDate.day);
|
||||
for (final dc.ListShiftsForCoverageShifts shift in shifts) {
|
||||
final DateTime shiftDate = shift.date?.toDateTime() ?? DateTime.now();
|
||||
final DateTime date = DateTime(shiftDate.year, shiftDate.month, shiftDate.day);
|
||||
|
||||
final needed = shift.workersNeeded ?? 0;
|
||||
final filled = shift.filled ?? 0;
|
||||
final int needed = shift.workersNeeded ?? 0;
|
||||
final int filled = shift.filled ?? 0;
|
||||
|
||||
totalNeeded += needed;
|
||||
totalFilled += filled;
|
||||
|
||||
final current = dailyStats[date] ?? (0, 0);
|
||||
final (int, int) current = dailyStats[date] ?? (0, 0);
|
||||
dailyStats[date] = (current.$1 + needed, current.$2 + filled);
|
||||
}
|
||||
|
||||
final List<CoverageDay> dailyCoverage = dailyStats.entries.map((e) {
|
||||
final needed = e.value.$1;
|
||||
final filled = e.value.$2;
|
||||
final List<CoverageDay> dailyCoverage = dailyStats.entries.map((MapEntry<DateTime, (int, int)> e) {
|
||||
final int needed = e.value.$1;
|
||||
final int filled = e.value.$2;
|
||||
return CoverageDay(
|
||||
date: e.key,
|
||||
needed: needed,
|
||||
filled: filled,
|
||||
percentage: needed == 0 ? 100.0 : (filled / needed) * 100.0,
|
||||
);
|
||||
}).toList()..sort((a, b) => a.date.compareTo(b.date));
|
||||
}).toList()..sort((CoverageDay a, CoverageDay b) => a.date.compareTo(b.date));
|
||||
|
||||
return CoverageReport(
|
||||
overallCoverage: totalNeeded == 0 ? 100.0 : (totalFilled / totalNeeded) * 100.0,
|
||||
@@ -226,7 +227,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final String id = businessId ?? await _service.getBusinessId();
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.ListShiftsForForecastByBusinessData, dc.ListShiftsForForecastByBusinessVariables> response = await _service.connector
|
||||
.listShiftsForForecastByBusiness(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -234,43 +235,43 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final shifts = response.data.shifts;
|
||||
final List<dc.ListShiftsForForecastByBusinessShifts> shifts = response.data.shifts;
|
||||
|
||||
double projectedSpend = 0.0;
|
||||
int projectedWorkers = 0;
|
||||
double totalHours = 0.0;
|
||||
final Map<DateTime, (double, int)> dailyStats = {};
|
||||
final Map<DateTime, (double, int)> dailyStats = <DateTime, (double, int)>{};
|
||||
|
||||
// Weekly stats: index -> (cost, count, hours)
|
||||
final Map<int, (double, int, double)> weeklyStats = {
|
||||
final Map<int, (double, int, double)> weeklyStats = <int, (double, int, double)>{
|
||||
0: (0.0, 0, 0.0),
|
||||
1: (0.0, 0, 0.0),
|
||||
2: (0.0, 0, 0.0),
|
||||
3: (0.0, 0, 0.0),
|
||||
};
|
||||
|
||||
for (final shift in shifts) {
|
||||
final shiftDate = shift.date?.toDateTime() ?? DateTime.now();
|
||||
final date = DateTime(shiftDate.year, shiftDate.month, shiftDate.day);
|
||||
for (final dc.ListShiftsForForecastByBusinessShifts shift in shifts) {
|
||||
final DateTime shiftDate = shift.date?.toDateTime() ?? DateTime.now();
|
||||
final DateTime date = DateTime(shiftDate.year, shiftDate.month, shiftDate.day);
|
||||
|
||||
final cost = (shift.cost ?? 0.0).toDouble();
|
||||
final workers = shift.workersNeeded ?? 0;
|
||||
final hoursVal = (shift.hours ?? 0).toDouble();
|
||||
final shiftTotalHours = hoursVal * workers;
|
||||
final double cost = (shift.cost ?? 0.0).toDouble();
|
||||
final int workers = shift.workersNeeded ?? 0;
|
||||
final double hoursVal = (shift.hours ?? 0).toDouble();
|
||||
final double shiftTotalHours = hoursVal * workers;
|
||||
|
||||
projectedSpend += cost;
|
||||
projectedWorkers += workers;
|
||||
totalHours += shiftTotalHours;
|
||||
|
||||
final current = dailyStats[date] ?? (0.0, 0);
|
||||
final (double, int) current = dailyStats[date] ?? (0.0, 0);
|
||||
dailyStats[date] = (current.$1 + cost, current.$2 + workers);
|
||||
|
||||
// Weekly logic
|
||||
final diffDays = shiftDate.difference(startDate).inDays;
|
||||
final int diffDays = shiftDate.difference(startDate).inDays;
|
||||
if (diffDays >= 0) {
|
||||
final weekIndex = diffDays ~/ 7;
|
||||
final int weekIndex = diffDays ~/ 7;
|
||||
if (weekIndex < 4) {
|
||||
final wCurrent = weeklyStats[weekIndex]!;
|
||||
final (double, int, double) wCurrent = weeklyStats[weekIndex]!;
|
||||
weeklyStats[weekIndex] = (
|
||||
wCurrent.$1 + cost,
|
||||
wCurrent.$2 + 1,
|
||||
@@ -280,17 +281,17 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
}
|
||||
}
|
||||
|
||||
final List<ForecastPoint> chartData = dailyStats.entries.map((e) {
|
||||
final List<ForecastPoint> chartData = dailyStats.entries.map((MapEntry<DateTime, (double, int)> e) {
|
||||
return ForecastPoint(
|
||||
date: e.key,
|
||||
projectedCost: e.value.$1,
|
||||
workersNeeded: e.value.$2,
|
||||
);
|
||||
}).toList()..sort((a, b) => a.date.compareTo(b.date));
|
||||
}).toList()..sort((ForecastPoint a, ForecastPoint b) => a.date.compareTo(b.date));
|
||||
|
||||
final List<ForecastWeek> weeklyBreakdown = [];
|
||||
final List<ForecastWeek> weeklyBreakdown = <ForecastWeek>[];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final stats = weeklyStats[i]!;
|
||||
final (double, int, double) stats = weeklyStats[i]!;
|
||||
weeklyBreakdown.add(ForecastWeek(
|
||||
weekNumber: i + 1,
|
||||
totalCost: stats.$1,
|
||||
@@ -300,8 +301,8 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
));
|
||||
}
|
||||
|
||||
final weeksCount = (endDate.difference(startDate).inDays / 7).ceil();
|
||||
final avgWeeklySpend = weeksCount > 0 ? projectedSpend / weeksCount : 0.0;
|
||||
final int weeksCount = (endDate.difference(startDate).inDays / 7).ceil();
|
||||
final double avgWeeklySpend = weeksCount > 0 ? projectedSpend / weeksCount : 0.0;
|
||||
|
||||
return ForecastReport(
|
||||
projectedSpend: projectedSpend,
|
||||
@@ -324,7 +325,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final String id = businessId ?? await _service.getBusinessId();
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.ListShiftsForPerformanceByBusinessData, dc.ListShiftsForPerformanceByBusinessVariables> response = await _service.connector
|
||||
.listShiftsForPerformanceByBusiness(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -332,7 +333,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final shifts = response.data.shifts;
|
||||
final List<dc.ListShiftsForPerformanceByBusinessShifts> shifts = response.data.shifts;
|
||||
|
||||
int totalNeeded = 0;
|
||||
int totalFilled = 0;
|
||||
@@ -340,7 +341,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
double totalFillTimeSeconds = 0.0;
|
||||
int filledShiftsWithTime = 0;
|
||||
|
||||
for (final shift in shifts) {
|
||||
for (final dc.ListShiftsForPerformanceByBusinessShifts shift in shifts) {
|
||||
totalNeeded += shift.workersNeeded ?? 0;
|
||||
totalFilled += shift.filled ?? 0;
|
||||
if ((shift.status?.stringValue ?? '') == 'COMPLETED') {
|
||||
@@ -348,8 +349,8 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
}
|
||||
|
||||
if (shift.filledAt != null && shift.createdAt != null) {
|
||||
final createdAt = shift.createdAt!.toDateTime();
|
||||
final filledAt = shift.filledAt!.toDateTime();
|
||||
final DateTime createdAt = shift.createdAt!.toDateTime();
|
||||
final DateTime filledAt = shift.filledAt!.toDateTime();
|
||||
totalFillTimeSeconds += filledAt.difference(createdAt).inSeconds;
|
||||
filledShiftsWithTime++;
|
||||
}
|
||||
@@ -366,7 +367,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
completionRate: completionRate,
|
||||
onTimeRate: 95.0,
|
||||
avgFillTimeHours: avgFillTimeHours,
|
||||
keyPerformanceIndicators: [
|
||||
keyPerformanceIndicators: <PerformanceMetric>[
|
||||
PerformanceMetric(label: 'Fill Rate', value: '${fillRate.toStringAsFixed(1)}%', trend: 0.02),
|
||||
PerformanceMetric(label: 'Completion', value: '${completionRate.toStringAsFixed(1)}%', trend: 0.05),
|
||||
PerformanceMetric(label: 'Avg Fill Time', value: '${avgFillTimeHours.toStringAsFixed(1)}h', trend: -0.1),
|
||||
@@ -384,7 +385,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
return _service.run(() async {
|
||||
final String id = businessId ?? await _service.getBusinessId();
|
||||
|
||||
final shiftsResponse = await _service.connector
|
||||
final QueryResult<dc.ListShiftsForNoShowRangeByBusinessData, dc.ListShiftsForNoShowRangeByBusinessVariables> shiftsResponse = await _service.connector
|
||||
.listShiftsForNoShowRangeByBusiness(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -392,34 +393,34 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final shiftIds = shiftsResponse.data.shifts.map((s) => s.id).toList();
|
||||
final List<String> shiftIds = shiftsResponse.data.shifts.map((dc.ListShiftsForNoShowRangeByBusinessShifts s) => s.id).toList();
|
||||
if (shiftIds.isEmpty) {
|
||||
return const NoShowReport(totalNoShows: 0, noShowRate: 0, flaggedWorkers: []);
|
||||
return const NoShowReport(totalNoShows: 0, noShowRate: 0, flaggedWorkers: <NoShowWorker>[]);
|
||||
}
|
||||
|
||||
final appsResponse = await _service.connector
|
||||
final QueryResult<dc.ListApplicationsForNoShowRangeData, dc.ListApplicationsForNoShowRangeVariables> appsResponse = await _service.connector
|
||||
.listApplicationsForNoShowRange(shiftIds: shiftIds)
|
||||
.execute();
|
||||
|
||||
final apps = appsResponse.data.applications;
|
||||
final noShowApps = apps.where((a) => (a.status.stringValue) == 'NO_SHOW').toList();
|
||||
final noShowStaffIds = noShowApps.map((a) => a.staffId).toSet().toList();
|
||||
final List<dc.ListApplicationsForNoShowRangeApplications> apps = appsResponse.data.applications;
|
||||
final List<dc.ListApplicationsForNoShowRangeApplications> noShowApps = apps.where((dc.ListApplicationsForNoShowRangeApplications a) => (a.status.stringValue) == 'NO_SHOW').toList();
|
||||
final List<String> noShowStaffIds = noShowApps.map((dc.ListApplicationsForNoShowRangeApplications a) => a.staffId).toSet().toList();
|
||||
|
||||
if (noShowStaffIds.isEmpty) {
|
||||
return NoShowReport(
|
||||
totalNoShows: noShowApps.length,
|
||||
noShowRate: apps.isEmpty ? 0 : (noShowApps.length / apps.length) * 100.0,
|
||||
flaggedWorkers: [],
|
||||
flaggedWorkers: <NoShowWorker>[],
|
||||
);
|
||||
}
|
||||
|
||||
final staffResponse = await _service.connector
|
||||
final QueryResult<dc.ListStaffForNoShowReportData, dc.ListStaffForNoShowReportVariables> staffResponse = await _service.connector
|
||||
.listStaffForNoShowReport(staffIds: noShowStaffIds)
|
||||
.execute();
|
||||
|
||||
final staffList = staffResponse.data.staffs;
|
||||
final List<dc.ListStaffForNoShowReportStaffs> staffList = staffResponse.data.staffs;
|
||||
|
||||
final List<NoShowWorker> flaggedWorkers = staffList.map((s) => NoShowWorker(
|
||||
final List<NoShowWorker> flaggedWorkers = staffList.map((dc.ListStaffForNoShowReportStaffs s) => NoShowWorker(
|
||||
id: s.id,
|
||||
fullName: s.fullName ?? '',
|
||||
noShowCount: s.noShowCount ?? 0,
|
||||
@@ -444,7 +445,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
final String id = businessId ?? await _service.getBusinessId();
|
||||
|
||||
// Use forecast query for hours/cost data
|
||||
final shiftsResponse = await _service.connector
|
||||
final QueryResult<dc.ListShiftsForForecastByBusinessData, dc.ListShiftsForForecastByBusinessVariables> shiftsResponse = await _service.connector
|
||||
.listShiftsForForecastByBusiness(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -453,7 +454,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
.execute();
|
||||
|
||||
// Use performance query for avgFillTime (has filledAt + createdAt)
|
||||
final perfResponse = await _service.connector
|
||||
final QueryResult<dc.ListShiftsForPerformanceByBusinessData, dc.ListShiftsForPerformanceByBusinessVariables> perfResponse = await _service.connector
|
||||
.listShiftsForPerformanceByBusiness(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -461,7 +462,7 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final invoicesResponse = await _service.connector
|
||||
final QueryResult<dc.ListInvoicesForSpendByBusinessData, dc.ListInvoicesForSpendByBusinessVariables> invoicesResponse = await _service.connector
|
||||
.listInvoicesForSpendByBusiness(
|
||||
businessId: id,
|
||||
startDate: _service.toTimestamp(startDate),
|
||||
@@ -469,15 +470,15 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
)
|
||||
.execute();
|
||||
|
||||
final forecastShifts = shiftsResponse.data.shifts;
|
||||
final perfShifts = perfResponse.data.shifts;
|
||||
final invoices = invoicesResponse.data.invoices;
|
||||
final List<dc.ListShiftsForForecastByBusinessShifts> forecastShifts = shiftsResponse.data.shifts;
|
||||
final List<dc.ListShiftsForPerformanceByBusinessShifts> perfShifts = perfResponse.data.shifts;
|
||||
final List<dc.ListInvoicesForSpendByBusinessInvoices> invoices = invoicesResponse.data.invoices;
|
||||
|
||||
// Aggregate hours and fill rate from forecast shifts
|
||||
double totalHours = 0;
|
||||
int totalNeeded = 0;
|
||||
|
||||
for (final shift in forecastShifts) {
|
||||
for (final dc.ListShiftsForForecastByBusinessShifts shift in forecastShifts) {
|
||||
totalHours += (shift.hours ?? 0).toDouble();
|
||||
totalNeeded += shift.workersNeeded ?? 0;
|
||||
}
|
||||
@@ -488,13 +489,13 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
double totalFillTimeSeconds = 0;
|
||||
int filledShiftsWithTime = 0;
|
||||
|
||||
for (final shift in perfShifts) {
|
||||
for (final dc.ListShiftsForPerformanceByBusinessShifts shift in perfShifts) {
|
||||
perfNeeded += shift.workersNeeded ?? 0;
|
||||
perfFilled += shift.filled ?? 0;
|
||||
|
||||
if (shift.filledAt != null && shift.createdAt != null) {
|
||||
final createdAt = shift.createdAt!.toDateTime();
|
||||
final filledAt = shift.filledAt!.toDateTime();
|
||||
final DateTime createdAt = shift.createdAt!.toDateTime();
|
||||
final DateTime filledAt = shift.filledAt!.toDateTime();
|
||||
totalFillTimeSeconds += filledAt.difference(createdAt).inSeconds;
|
||||
filledShiftsWithTime++;
|
||||
}
|
||||
@@ -502,19 +503,19 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
|
||||
// Aggregate total spend from invoices
|
||||
double totalSpend = 0;
|
||||
for (final inv in invoices) {
|
||||
for (final dc.ListInvoicesForSpendByBusinessInvoices inv in invoices) {
|
||||
totalSpend += (inv.amount ?? 0).toDouble();
|
||||
}
|
||||
|
||||
// Fetch no-show rate using forecast shift IDs
|
||||
final shiftIds = forecastShifts.map((s) => s.id).toList();
|
||||
final List<String> shiftIds = forecastShifts.map((dc.ListShiftsForForecastByBusinessShifts s) => s.id).toList();
|
||||
double noShowRate = 0;
|
||||
if (shiftIds.isNotEmpty) {
|
||||
final appsResponse = await _service.connector
|
||||
final QueryResult<dc.ListApplicationsForNoShowRangeData, dc.ListApplicationsForNoShowRangeVariables> appsResponse = await _service.connector
|
||||
.listApplicationsForNoShowRange(shiftIds: shiftIds)
|
||||
.execute();
|
||||
final apps = appsResponse.data.applications;
|
||||
final noShowApps = apps.where((a) => (a.status.stringValue) == 'NO_SHOW').toList();
|
||||
final List<dc.ListApplicationsForNoShowRangeApplications> apps = appsResponse.data.applications;
|
||||
final List<dc.ListApplicationsForNoShowRangeApplications> noShowApps = apps.where((dc.ListApplicationsForNoShowRangeApplications a) => (a.status.stringValue) == 'NO_SHOW').toList();
|
||||
noShowRate = apps.isEmpty ? 0 : (noShowApps.length / apps.length) * 100.0;
|
||||
}
|
||||
|
||||
@@ -533,3 +534,4 @@ class ReportsConnectorRepositoryImpl implements ReportsConnectorRepository {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
@@ -22,12 +23,12 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
required DateTime end,
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final query = _service.connector
|
||||
final dc.GetApplicationsByStaffIdVariablesBuilder query = _service.connector
|
||||
.getApplicationsByStaffId(staffId: staffId)
|
||||
.dayStart(_service.toTimestamp(start))
|
||||
.dayEnd(_service.toTimestamp(end));
|
||||
|
||||
final response = await query.execute();
|
||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> response = await query.execute();
|
||||
return _mapApplicationsToShifts(response.data.applications);
|
||||
});
|
||||
}
|
||||
@@ -42,29 +43,29 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
// First, fetch all available shift roles for the vendor/business
|
||||
// Use the session owner ID (vendorId)
|
||||
final String? vendorId = dc.StaffSessionStore.instance.session?.ownerId;
|
||||
if (vendorId == null || vendorId.isEmpty) return [];
|
||||
if (vendorId == null || vendorId.isEmpty) return <Shift>[];
|
||||
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.ListShiftRolesByVendorIdData, dc.ListShiftRolesByVendorIdVariables> response = await _service.connector
|
||||
.listShiftRolesByVendorId(vendorId: vendorId)
|
||||
.execute();
|
||||
|
||||
final allShiftRoles = response.data.shiftRoles;
|
||||
final List<dc.ListShiftRolesByVendorIdShiftRoles> allShiftRoles = response.data.shiftRoles;
|
||||
|
||||
// Fetch current applications to filter out already booked shifts
|
||||
final myAppsResponse = await _service.connector
|
||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> myAppsResponse = await _service.connector
|
||||
.getApplicationsByStaffId(staffId: staffId)
|
||||
.execute();
|
||||
final Set<String> appliedShiftIds =
|
||||
myAppsResponse.data.applications.map((a) => a.shiftId).toSet();
|
||||
myAppsResponse.data.applications.map((dc.GetApplicationsByStaffIdApplications a) => a.shiftId).toSet();
|
||||
|
||||
final List<Shift> mappedShifts = [];
|
||||
for (final sr in allShiftRoles) {
|
||||
final List<Shift> mappedShifts = <Shift>[];
|
||||
for (final dc.ListShiftRolesByVendorIdShiftRoles sr in allShiftRoles) {
|
||||
if (appliedShiftIds.contains(sr.shiftId)) continue;
|
||||
|
||||
final DateTime? shiftDate = _service.toDateTime(sr.shift.date);
|
||||
final startDt = _service.toDateTime(sr.startTime);
|
||||
final endDt = _service.toDateTime(sr.endTime);
|
||||
final createdDt = _service.toDateTime(sr.createdAt);
|
||||
final DateTime? startDt = _service.toDateTime(sr.startTime);
|
||||
final DateTime? endDt = _service.toDateTime(sr.endTime);
|
||||
final DateTime? createdDt = _service.toDateTime(sr.createdAt);
|
||||
|
||||
mappedShifts.add(
|
||||
Shift(
|
||||
@@ -96,8 +97,8 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
}
|
||||
|
||||
if (query != null && query.isNotEmpty) {
|
||||
final lowerQuery = query.toLowerCase();
|
||||
return mappedShifts.where((s) {
|
||||
final String lowerQuery = query.toLowerCase();
|
||||
return mappedShifts.where((Shift s) {
|
||||
return s.title.toLowerCase().contains(lowerQuery) ||
|
||||
s.clientName.toLowerCase().contains(lowerQuery);
|
||||
}).toList();
|
||||
@@ -112,7 +113,7 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
return _service.run(() async {
|
||||
// Current schema doesn't have a specific "pending assignment" query that differs from confirmed
|
||||
// unless we filter by status. In the old repo it was returning an empty list.
|
||||
return [];
|
||||
return <Shift>[];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -124,10 +125,10 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
if (roleId != null && roleId.isNotEmpty) {
|
||||
final roleResult = await _service.connector
|
||||
final QueryResult<dc.GetShiftRoleByIdData, dc.GetShiftRoleByIdVariables> roleResult = await _service.connector
|
||||
.getShiftRoleById(shiftId: shiftId, roleId: roleId)
|
||||
.execute();
|
||||
final sr = roleResult.data.shiftRole;
|
||||
final dc.GetShiftRoleByIdShiftRole? sr = roleResult.data.shiftRole;
|
||||
if (sr == null) return null;
|
||||
|
||||
final DateTime? startDt = _service.toDateTime(sr.startTime);
|
||||
@@ -137,17 +138,17 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
bool hasApplied = false;
|
||||
String status = 'open';
|
||||
|
||||
final appsResponse = await _service.connector
|
||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> appsResponse = await _service.connector
|
||||
.getApplicationsByStaffId(staffId: staffId)
|
||||
.execute();
|
||||
|
||||
final app = appsResponse.data.applications
|
||||
.where((a) => a.shiftId == shiftId && a.shiftRole.roleId == roleId)
|
||||
final dc.GetApplicationsByStaffIdApplications? app = appsResponse.data.applications
|
||||
.where((dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId && a.shiftRole.roleId == roleId)
|
||||
.firstOrNull;
|
||||
|
||||
if (app != null) {
|
||||
hasApplied = true;
|
||||
final s = app.status.stringValue;
|
||||
final String s = app.status.stringValue;
|
||||
status = _mapApplicationStatus(s);
|
||||
}
|
||||
|
||||
@@ -180,8 +181,8 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
);
|
||||
}
|
||||
|
||||
final result = await _service.connector.getShiftById(id: shiftId).execute();
|
||||
final s = result.data.shift;
|
||||
final QueryResult<dc.GetShiftByIdData, dc.GetShiftByIdVariables> result = await _service.connector.getShiftById(id: shiftId).execute();
|
||||
final dc.GetShiftByIdShift? s = result.data.shift;
|
||||
if (s == null) return null;
|
||||
|
||||
int? required;
|
||||
@@ -189,17 +190,17 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
Break? breakInfo;
|
||||
|
||||
try {
|
||||
final rolesRes = await _service.connector
|
||||
final QueryResult<dc.ListShiftRolesByShiftIdData, dc.ListShiftRolesByShiftIdVariables> rolesRes = await _service.connector
|
||||
.listShiftRolesByShiftId(shiftId: shiftId)
|
||||
.execute();
|
||||
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
||||
required = 0;
|
||||
filled = 0;
|
||||
for (var r in rolesRes.data.shiftRoles) {
|
||||
for (dc.ListShiftRolesByShiftIdShiftRoles r in rolesRes.data.shiftRoles) {
|
||||
required = (required ?? 0) + r.count;
|
||||
filled = (filled ?? 0) + (r.assigned ?? 0);
|
||||
}
|
||||
final firstRole = rolesRes.data.shiftRoles.first;
|
||||
final dc.ListShiftRolesByShiftIdShiftRoles firstRole = rolesRes.data.shiftRoles.first;
|
||||
breakInfo = BreakAdapter.fromData(
|
||||
isPaid: firstRole.isBreakPaid ?? false,
|
||||
breakTime: firstRole.breakType?.stringValue,
|
||||
@@ -207,9 +208,9 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
final startDt = _service.toDateTime(s.startTime);
|
||||
final endDt = _service.toDateTime(s.endTime);
|
||||
final createdDt = _service.toDateTime(s.createdAt);
|
||||
final DateTime? startDt = _service.toDateTime(s.startTime);
|
||||
final DateTime? endDt = _service.toDateTime(s.endTime);
|
||||
final DateTime? createdDt = _service.toDateTime(s.createdAt);
|
||||
|
||||
return Shift(
|
||||
id: s.id,
|
||||
@@ -243,17 +244,17 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
String? roleId,
|
||||
}) async {
|
||||
return _service.run(() async {
|
||||
final targetRoleId = roleId ?? '';
|
||||
final String targetRoleId = roleId ?? '';
|
||||
if (targetRoleId.isEmpty) throw Exception('Missing role id.');
|
||||
|
||||
final roleResult = await _service.connector
|
||||
final QueryResult<dc.GetShiftRoleByIdData, dc.GetShiftRoleByIdVariables> roleResult = await _service.connector
|
||||
.getShiftRoleById(shiftId: shiftId, roleId: targetRoleId)
|
||||
.execute();
|
||||
final role = roleResult.data.shiftRole;
|
||||
final dc.GetShiftRoleByIdShiftRole? role = roleResult.data.shiftRole;
|
||||
if (role == null) throw Exception('Shift role not found');
|
||||
|
||||
final shiftResult = await _service.connector.getShiftById(id: shiftId).execute();
|
||||
final shift = shiftResult.data.shift;
|
||||
final QueryResult<dc.GetShiftByIdData, dc.GetShiftByIdVariables> shiftResult = await _service.connector.getShiftById(id: shiftId).execute();
|
||||
final dc.GetShiftByIdShift? shift = shiftResult.data.shift;
|
||||
if (shift == null) throw Exception('Shift not found');
|
||||
|
||||
// Validate daily limit
|
||||
@@ -262,7 +263,7 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
final DateTime dayStartUtc = DateTime.utc(shiftDate.year, shiftDate.month, shiftDate.day);
|
||||
final DateTime dayEndUtc = dayStartUtc.add(const Duration(days: 1)).subtract(const Duration(microseconds: 1));
|
||||
|
||||
final validationResponse = await _service.connector
|
||||
final QueryResult<dc.VaidateDayStaffApplicationData, dc.VaidateDayStaffApplicationVariables> validationResponse = await _service.connector
|
||||
.vaidateDayStaffApplication(staffId: staffId)
|
||||
.dayStart(_service.toTimestamp(dayStartUtc))
|
||||
.dayEnd(_service.toTimestamp(dayEndUtc))
|
||||
@@ -274,7 +275,7 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
}
|
||||
|
||||
// Check for existing application
|
||||
final existingAppRes = await _service.connector
|
||||
final QueryResult<dc.GetApplicationByStaffShiftAndRoleData, dc.GetApplicationByStaffShiftAndRoleVariables> existingAppRes = await _service.connector
|
||||
.getApplicationByStaffShiftAndRole(
|
||||
staffId: staffId,
|
||||
shiftId: shiftId,
|
||||
@@ -294,7 +295,7 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
|
||||
String? createdAppId;
|
||||
try {
|
||||
final createRes = await _service.connector.createApplication(
|
||||
final OperationResult<dc.CreateApplicationData, dc.CreateApplicationVariables> createRes = await _service.connector.createApplication(
|
||||
shiftId: shiftId,
|
||||
staffId: staffId,
|
||||
roleId: targetRoleId,
|
||||
@@ -343,19 +344,19 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
Future<List<Shift>> getCancelledShifts({required String staffId}) async {
|
||||
return _service.run(() async {
|
||||
// Logic would go here to fetch by REJECTED status if needed
|
||||
return [];
|
||||
return <Shift>[];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Shift>> getHistoryShifts({required String staffId}) async {
|
||||
return _service.run(() async {
|
||||
final response = await _service.connector
|
||||
final QueryResult<dc.ListCompletedApplicationsByStaffIdData, dc.ListCompletedApplicationsByStaffIdVariables> response = await _service.connector
|
||||
.listCompletedApplicationsByStaffId(staffId: staffId)
|
||||
.execute();
|
||||
|
||||
final List<Shift> shifts = [];
|
||||
for (final app in response.data.applications) {
|
||||
final List<Shift> shifts = <Shift>[];
|
||||
for (final dc.ListCompletedApplicationsByStaffIdApplications app in response.data.applications) {
|
||||
final String roleName = app.shiftRole.role.name;
|
||||
final String orderName = (app.shift.order.eventName ?? '').trim().isNotEmpty
|
||||
? app.shift.order.eventName!
|
||||
@@ -478,12 +479,12 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
) async {
|
||||
return _service.run(() async {
|
||||
// First try to find the application
|
||||
final appsResponse = await _service.connector
|
||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> appsResponse = await _service.connector
|
||||
.getApplicationsByStaffId(staffId: staffId)
|
||||
.execute();
|
||||
|
||||
final app = appsResponse.data.applications
|
||||
.where((a) => a.shiftId == shiftId)
|
||||
final dc.GetApplicationsByStaffIdApplications? app = appsResponse.data.applications
|
||||
.where((dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId)
|
||||
.firstOrNull;
|
||||
|
||||
if (app != null) {
|
||||
@@ -493,12 +494,12 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
.execute();
|
||||
} else if (newStatus == dc.ApplicationStatus.REJECTED) {
|
||||
// If declining but no app found, create a rejected application
|
||||
final rolesRes = await _service.connector
|
||||
final QueryResult<dc.ListShiftRolesByShiftIdData, dc.ListShiftRolesByShiftIdVariables> rolesRes = await _service.connector
|
||||
.listShiftRolesByShiftId(shiftId: shiftId)
|
||||
.execute();
|
||||
|
||||
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
||||
final firstRole = rolesRes.data.shiftRoles.first;
|
||||
final dc.ListShiftRolesByShiftIdShiftRoles firstRole = rolesRes.data.shiftRoles.first;
|
||||
await _service.connector.createApplication(
|
||||
shiftId: shiftId,
|
||||
staffId: staffId,
|
||||
@@ -513,3 +514,4 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,10 +105,10 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
|
||||
/// Checks if personal info is complete.
|
||||
bool _isPersonalInfoComplete(GetStaffPersonalInfoCompletionStaff? staff) {
|
||||
if (staff == null) return false;
|
||||
final String? fullName = staff.fullName;
|
||||
final String fullName = staff.fullName;
|
||||
final String? email = staff.email;
|
||||
final String? phone = staff.phone;
|
||||
return (fullName?.trim().isNotEmpty ?? false) &&
|
||||
return (fullName.trim().isNotEmpty ?? false) &&
|
||||
(email?.trim().isNotEmpty ?? false) &&
|
||||
(phone?.trim().isNotEmpty ?? false);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -197,7 +198,6 @@ class DataConnectService with DataErrorHandler, SessionHandlerMixin {
|
||||
}
|
||||
|
||||
/// Executes an operation with centralized error handling.
|
||||
@override
|
||||
Future<T> run<T>(
|
||||
Future<T> Function() operation, {
|
||||
bool requiresAuthentication = true,
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
class ClientBusinessSession {
|
||||
final String id;
|
||||
final String businessName;
|
||||
final String? email;
|
||||
final String? city;
|
||||
final String? contactName;
|
||||
final String? companyLogoUrl;
|
||||
|
||||
const ClientBusinessSession({
|
||||
required this.id,
|
||||
@@ -14,15 +8,23 @@ class ClientBusinessSession {
|
||||
this.contactName,
|
||||
this.companyLogoUrl,
|
||||
});
|
||||
final String id;
|
||||
final String businessName;
|
||||
final String? email;
|
||||
final String? city;
|
||||
final String? contactName;
|
||||
final String? companyLogoUrl;
|
||||
}
|
||||
|
||||
class ClientSession {
|
||||
final ClientBusinessSession? business;
|
||||
|
||||
const ClientSession({required this.business});
|
||||
final ClientBusinessSession? business;
|
||||
}
|
||||
|
||||
class ClientSessionStore {
|
||||
|
||||
ClientSessionStore._();
|
||||
ClientSession? _session;
|
||||
|
||||
ClientSession? get session => _session;
|
||||
@@ -36,6 +38,4 @@ class ClientSessionStore {
|
||||
}
|
||||
|
||||
static final ClientSessionStore instance = ClientSessionStore._();
|
||||
|
||||
ClientSessionStore._();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user