updated the sheet

This commit is contained in:
2026-08-28 18:16:28 +05:30
parent 69f4f3e909
commit 074cc0eccf
43 changed files with 2617 additions and 1201 deletions

View File

@@ -3,15 +3,15 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:audioplayers/audioplayers.dart';
import 'dart:math' as math;
import 'package:miler/providers/pickuplog/pickuplog_provider.dart';
import 'package:miler/data/api_config.dart';
import 'package:miler/data/miler_api.dart';
import 'package:miler/data/device_telemetry.dart';
import 'package:miler/providers/notifications/notificationservce.dart';
import 'package:miler/views/helpers/constants/apiconstants.dart';
import 'package:miler/utils/kalman_filter.dart';
/// Background service for managing active pickup logs
@@ -20,7 +20,6 @@ class BackgroundCollectionLog {
static DateTime? _lastUpdateTime;
static final CreatePickupLogProvider _logProvider = CreatePickupLogProvider();
static final http.Client _httpClient = http.Client();
static final FlutterTts _tts = FlutterTts();
static final AudioPlayer _proximityPlayer = AudioPlayer();
@@ -60,38 +59,39 @@ class BackgroundCollectionLog {
final now = DateTime.now();
final today = '${now.year}-${_pad(now.month)}-${_pad(now.day)}';
// Build API URL using v1 endpoint (getpickup)
final bool isLive = ApiConstants.mainRoute == 'live';
final baseUrl = isLive
? ApiConstants.currentPickupLive
: ApiConstants.currentPickupDev;
final uri = Uri.parse(baseUrl).replace(
queryParameters: {
'userid': userId.toString(),
'fromdate': today,
'todate': today,
't': DateTime.now().millisecondsSinceEpoch.toString(),
},
);
debugPrint('[ACTIVE_COLLECTION_LOG][BG] Fetching Pickup: $uri');
// Fetch pickup from API using v1 endpoint
final pickups = await _fetchPickupsFromApi(uri);
// ── This asked the pre-v1 backend, with the wrong question ──
//
// It built a `pickups/getpickups` URL on the retired backend by hand
// and fetched it with a bare `_httpClient.get` — **no bearer token**, and
// `userid` in the query string, so the rider's day was readable by id
// from an unauthenticated call.
//
// It could never have worked anyway. The filter below looked for
// `orderstatus == 'active'`, and `active` is not a value this app's own
// vocabulary contains: [ApiConfig.legacyStatusFromNew] maps the v1
// statuses to `assigned` / `accepted` / `arrived` / `picked`, and never to
// `active`. So on every tick this returned 0 items and fell through to
// the shift-end check — which is exactly what the device log shows.
//
// `GET /miler/bookings` is the v1 question, asked with the token, about
// the rider the token names. The filter is the real vocabulary: work the
// rider has taken on and not yet collected.
final res = await MilerApi.bookings(date: today);
final pickups = res.list;
debugPrint(
'[ACTIVE_COLLECTION_LOG][BG] API returned ${pickups.length} items',
'[ACTIVE_COLLECTION_LOG][BG] /miler/bookings -> ${res.status}, '
'${pickups.length} items',
);
// Filter for active bookings only
const inProgress = {'accepted', 'arrived'};
final activeOrders = pickups.whereType<Map<String, dynamic>>().where((
Booking,
) {
final status = (Booking['orderstatus']?.toString().toLowerCase() ?? '')
.trim();
final isActive = status == 'active';
return isActive;
final status = ApiConfig.legacyStatusFromNew(
(Booking['status'] ?? Booking['orderstatus'])?.toString(),
);
return inProgress.contains(status);
}).toList();
if (activeOrders.isEmpty) {
@@ -189,7 +189,9 @@ class BackgroundCollectionLog {
// so every per-stop breadcrumb reached the console with a position and
// nothing to explain a gap in the trail. Read from the same place the
// rider heartbeat reads it. See [DeviceTelemetry].
final telemetry = await DeviceTelemetry.read(locationService: coords['status']);
final telemetry = await DeviceTelemetry.read(
locationService: coords['status'],
);
// Build payload with coordinates and timestamp
payloadWithCoords = <String, dynamic>{
@@ -211,11 +213,6 @@ class BackgroundCollectionLog {
'logstatus': await _resolveLogStatus(orderId, coords),
};
// Determine API endpoint
final url = ApiConstants.mainRoute == 'live'
? ApiConstants.createPickupLogLive
: ApiConstants.createPickupLogDev;
// Post the log
debugPrint('[ACTIVE_COLLECTION_LOG][BG] Posting pickup log for $orderId');
@@ -427,33 +424,6 @@ class BackgroundCollectionLog {
}
/// Fetch pickup from API using v1 endpoint
static Future<List<dynamic>> _fetchPickupsFromApi(Uri uri) async {
try {
final res = await _httpClient
.get(uri)
.timeout(const Duration(seconds: 10));
if (res.statusCode >= 200 && res.statusCode < 300) {
final decoded = json.decode(res.body);
final data = decoded is Map<String, dynamic>
? (decoded['details'] ?? decoded['data'] ?? decoded)
: decoded;
if (data is List) {
return data;
}
if (data is Map && data['items'] is List) {
return data['items'] as List;
}
return [];
}
return [];
} catch (e) {
return [];
}
}
/// Helper to pad numbers with leading zero
static String _pad(int n) => n.toString().padLeft(2, '0');
@@ -533,10 +503,6 @@ class BackgroundCollectionLog {
final List<String> remaining = [];
bool anySuccess = false;
final url = ApiConstants.mainRoute == 'live'
? ApiConstants.createPickupLogLive
: ApiConstants.createPickupLogDev;
for (final itemStr in queue) {
try {
final Map<String, dynamic> item = jsonDecode(itemStr);
@@ -761,14 +727,18 @@ class BackgroundCollectionLog {
'[AUTO_SHIFT_END] 🚀 Initiating auto-break and offline sequence...',
);
// 1. Get Required IDs
// ── The four ids and the fix are no longer this function's business ──
//
// `userid`, `partnerid`, `shiftid`, `logid` and a position were assembled
// here to fill a legacy break-log body. On v1 the break and the duty end
// are both keyed on the **token**: `POST /miler/breaks/start` opens a
// break for whoever the session names, and `PUT /miler/duty/end` closes
// whichever duty log is open for them. Sending an id would not make it
// more correct; it is what let the old call be made unauthenticated.
//
// `userid` is still read, as the one guard worth keeping: no signed-in
// rider, nothing to end.
final int userid = prefs.getInt('userid') ?? prefs.getInt('userId') ?? 0;
final int partnerid =
prefs.getInt('partnerid') ?? prefs.getInt('partnerId') ?? 0;
final int shiftid =
prefs.getInt('shiftid') ?? prefs.getInt('shiftId') ?? 0;
final int logid = prefs.getInt('logid') ?? prefs.getInt('logId') ?? 0;
if (userid == 0) {
debugPrint(
'[AUTO_SHIFT_END] ❌ Missing userid, cannot create break log',
@@ -776,61 +746,28 @@ class BackgroundCollectionLog {
return;
}
// 2. Get Location
final Map<String, String>? coords = await _getCoordinatesWithFallback();
final String lat = coords?['lat'] ?? '0';
final String lng = coords?['lng'] ?? '0';
// 3. Prepare Break Log Payload
final now = DateTime.now();
final int localBreakId =
(DateTime.now().millisecondsSinceEpoch % 900) + 100; // Random-ish ID
final String breakdate =
'${now.year}-${_pad(now.month)}-${_pad(now.day)} ${_pad(now.hour)}:${_pad(now.minute)}:${_pad(now.second)}';
final String breakstart =
'${_pad(now.hour)}:${_pad(now.minute)}:${_pad(now.second)}';
final payload = <String, dynamic>{
"breakid": localBreakId,
"logid": logid,
"breakdate": breakdate,
"userid": userid,
"partnerid": partnerid,
"shiftid": shiftid,
"breakstart": breakstart,
"breakend": "",
"breakhours": 0.0,
"latitude": lat,
"longitude": lng,
};
// 4. Call API to Create Break
final url = ApiConstants.mainRoute == 'live'
? ApiConstants.createBreakRiderLogLive
: ApiConstants.createBreakRiderLogDev;
debugPrint('[AUTO_SHIFT_END] Creating break log: $url');
// We use a separate provider instance or http call if needed,
// but _logProvider is for pickup logs. We need a generic post or use http directly.
// Since we don't have BreakRiderLogProvider here, we'll use http directly for simplicity and isolation.
// 4. Close the shift with a break, on v1.
//
// This hand-posted the retired backend's `partners/createbreaklog`
// with a bare `_httpClient.post` and no bearer
// token — an unauthenticated write keyed on a `userid` in the
// body. `POST /miler/breaks/start` is the v1 endpoint, and
// [MilerApi] attaches the session.
debugPrint('[AUTO_SHIFT_END] Creating break log (v1)');
try {
final response = await _httpClient
.post(
Uri.parse(url),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(payload),
)
.timeout(const Duration(seconds: 10));
final res = await MilerApi.startBreak('Shift_End');
if (response.statusCode >= 200 && response.statusCode < 300) {
if (res.ok) {
debugPrint('[AUTO_SHIFT_END] ✅ Break log created successfully');
// Parse response to get server break ID if needed, but mainly we just need to go offline
final body = jsonDecode(response.body);
final det = (body['details'] is Map) ? body['details'] : body;
final serverBreakId = det['breakid'];
final serverBreakId = res.map['breaklogid'] ?? res.map['breakid'];
if (serverBreakId != null) {
await prefs.setInt(
@@ -842,7 +779,7 @@ class BackgroundCollectionLog {
await prefs.setInt('break_start_epoch', now.millisecondsSinceEpoch);
} else {
debugPrint(
'[AUTO_SHIFT_END] ⚠️ Failed to create break log: ${response.statusCode}',
'[AUTO_SHIFT_END] ⚠️ Failed to create break log: ${res.status}',
);
}
} catch (e) {
@@ -854,27 +791,15 @@ class BackgroundCollectionLog {
await prefs.setInt('onduty', 0);
await prefs.setBool('online', false);
// 6. Update Rider Log (Set Duty = 0)
// We should also update the main rider log to say onduty=0
final updateUrl = ApiConstants.mainRoute == 'live'
? ApiConstants.updateRiderLogLive
: ApiConstants.updateRiderLogDev;
final updatePayload = {
"userid": userid,
"onduty": 0,
"latitude": lat,
"longitude": lng,
};
// 6. End duty, on v1.
//
// Was an unauthenticated POST of `{userid, onduty: 0}` to the
// legacy `updateriderlog`. `PUT /miler/duty/end` closes whichever
// duty log is open for the token's rider, which is the same fact
// without the id or the open door.
try {
await _httpClient
.post(
Uri.parse(updateUrl),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(updatePayload),
)
.timeout(const Duration(seconds: 5));
await MilerApi.endDuty();
debugPrint('[AUTO_SHIFT_END] ✅ Rider status updated to Offline');
} catch (_) {}

View File

@@ -6,11 +6,11 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'dart:math' as math;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:miler/views/helpers/constants/apiconstants.dart';
import 'package:miler/providers/Riderlog/riderlog_provider.dart';
import 'package:miler/background/backgroundservice.dart';
import 'package:geolocator/geolocator.dart';
import 'package:miler/data/device_telemetry.dart';
import 'package:miler/data/heartbeat.dart';
import 'package:miler/utils/kalman_filter.dart';
import 'package:miler/utils/mqtt_service.dart';
import 'package:miler/views/helpers/constants/mqtt_constants.dart';
@@ -149,7 +149,7 @@ class _BackgroundRiderLog {
// ✅ Check if there are active pickup to set status
final bool hasActivePickups = prefs.getBool('has_live_pickup') ?? false;
final String riderStatus = hasActivePickups ? 'active' : 'idle';
final String riderStatus = heartbeatStatus(hasActiveWork: hasActivePickups);
final now = DateTime.now();
final iso = _formatDateTimeFull(now);
@@ -226,9 +226,6 @@ class _BackgroundRiderLog {
payload['lastname'] = lastName.trim();
}
final base = ApiConstants.mainRoute == 'live'
? ApiConstants.createRiderLogLive
: ApiConstants.createRiderLogDev;
final provider = CreateRiderLogProvider();
final resp = await provider.createRiderLog(payload);
@@ -304,7 +301,7 @@ class _BackgroundRiderLog {
// 5. Lane: Status
mqttService.updateStatus(
riderStatus == 'active' ? 'Active' : MqttConstants.statusOnline,
riderStatus == 'On_Pickup' ? 'Active' : MqttConstants.statusOnline,
);
// 6. Lane: Periodic Log (Comprehensive Snapshot)