| const prisma = require('../config/database'); |
| const Joi = require('joi'); |
|
|
| |
| |
| |
| const getAssignedDeliveries = async (req, res) => { |
| try { |
| const deliveries = await prisma.delivery.findMany({ |
| where: { |
| driverId: req.user.id, |
| status: { |
| not: 'COMPLETED', |
| }, |
| }, |
| include: { |
| shipment: true, |
| truck: true, |
| }, |
| orderBy: { |
| createdAt: 'desc', |
| }, |
| }); |
|
|
| res.status(200).json({ |
| success: true, |
| data: deliveries, |
| }); |
| } catch (error) { |
| console.error('Get assigned deliveries error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to fetch assigned deliveries', |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const acceptDelivery = async (req, res) => { |
| try { |
| const { id } = req.params; |
|
|
| const delivery = await prisma.delivery.findUnique({ |
| where: { id }, |
| include: { shipment: true }, |
| }); |
|
|
| if (!delivery || delivery.driverId !== req.user.id) { |
| return res.status(404).json({ |
| success: false, |
| message: 'Delivery not found', |
| }); |
| } |
|
|
| if (delivery.status !== 'PENDING') { |
| return res.status(400).json({ |
| success: false, |
| message: 'Delivery is already accepted or in progress', |
| }); |
| } |
|
|
| |
| const updatedDelivery = await prisma.$transaction([ |
| prisma.delivery.update({ |
| where: { id }, |
| data: { status: 'EN_ROUTE_TO_PICKUP' }, |
| }), |
| prisma.shipment.update({ |
| where: { id: delivery.shipmentId }, |
| data: { status: 'DRIVER_ACCEPTED' }, |
| }), |
| ]); |
|
|
| res.status(200).json({ |
| success: true, |
| message: 'Delivery accepted', |
| data: updatedDelivery[0], |
| }); |
| } catch (error) { |
| console.error('Accept delivery error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to accept delivery', |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const rejectDelivery = async (req, res) => { |
| try { |
| const { id } = req.params; |
|
|
| const delivery = await prisma.delivery.findUnique({ |
| where: { id }, |
| }); |
|
|
| if (!delivery || delivery.driverId !== req.user.id) { |
| return res.status(404).json({ |
| success: false, |
| message: 'Delivery not found', |
| }); |
| } |
|
|
| |
| await prisma.$transaction([ |
| prisma.delivery.update({ |
| where: { id }, |
| data: { status: 'CANCELLED' }, |
| }), |
| prisma.shipment.update({ |
| where: { id: delivery.shipmentId }, |
| data: { status: 'DRIVER_REJECTED' }, |
| }), |
| ]); |
|
|
| res.status(200).json({ |
| success: true, |
| message: 'Delivery rejected', |
| }); |
| } catch (error) { |
| console.error('Reject delivery error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to reject delivery', |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const startDelivery = async (req, res) => { |
| try { |
| const { id } = req.params; |
|
|
| const delivery = await prisma.delivery.findUnique({ |
| where: { id }, |
| }); |
|
|
| if (!delivery || delivery.driverId !== req.user.id) { |
| return res.status(404).json({ |
| success: false, |
| message: 'Delivery not found', |
| }); |
| } |
|
|
| const updatedDelivery = await prisma.delivery.update({ |
| where: { id }, |
| data: { status: 'IN_TRANSIT' }, |
| }); |
|
|
| |
| await prisma.shipment.update({ |
| where: { id: delivery.shipmentId }, |
| data: { status: 'IN_TRANSIT' }, |
| }); |
|
|
| res.status(200).json({ |
| success: true, |
| message: 'Delivery started', |
| data: updatedDelivery, |
| }); |
| } catch (error) { |
| console.error('Start delivery error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to start delivery', |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const pickupCargo = async (req, res) => { |
| try { |
| const { id } = req.params; |
|
|
| const delivery = await prisma.delivery.findUnique({ |
| where: { id }, |
| }); |
|
|
| if (!delivery || delivery.driverId !== req.user.id) { |
| return res.status(404).json({ |
| success: false, |
| message: 'Delivery not found', |
| }); |
| } |
|
|
| const updatedDelivery = await prisma.delivery.update({ |
| where: { id }, |
| data: { |
| status: 'CARGO_LOADED', |
| pickupTime: new Date(), |
| }, |
| }); |
|
|
| res.status(200).json({ |
| success: true, |
| message: 'Cargo marked as picked up', |
| data: updatedDelivery, |
| }); |
| } catch (error) { |
| console.error('Pickup cargo error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to mark cargo as picked up', |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const completeDelivery = async (req, res) => { |
| try { |
| const { id } = req.params; |
|
|
| const delivery = await prisma.delivery.findUnique({ |
| where: { id }, |
| include: { shipment: true }, |
| }); |
|
|
| if (!delivery || delivery.driverId !== req.user.id) { |
| return res.status(404).json({ |
| success: false, |
| message: 'Delivery not found', |
| }); |
| } |
|
|
| |
| const updatedDelivery = await prisma.delivery.update({ |
| where: { id }, |
| data: { |
| status: 'COMPLETED', |
| completedAt: new Date(), |
| dropTime: new Date(), |
| }, |
| }); |
|
|
| |
| if (delivery.shipmentId) { |
| await prisma.shipment.update({ |
| where: { id: delivery.shipmentId }, |
| data: { status: 'COMPLETED' }, |
| }); |
| } |
|
|
| |
| const transaction = await prisma.transaction.create({ |
| data: { |
| driverId: req.user.id, |
| deliveryId: id, |
| amount: delivery.totalEarnings || 0, |
| type: 'BASE_DELIVERY', |
| description: `Payment for delivery ${id}`, |
| route: `${delivery.pickupLocation} → ${delivery.dropLocation}`, |
| }, |
| }); |
|
|
| |
| await prisma.user.update({ |
| where: { id: req.user.id }, |
| data: { |
| totalEarnings: { increment: delivery.totalEarnings || 0 }, |
| weeklyEarnings: { increment: delivery.totalEarnings || 0 }, |
| deliveriesCount: { increment: 1 }, |
| totalDistanceKm: { increment: delivery.distanceKm || 0 }, |
| }, |
| }); |
|
|
| res.status(200).json({ |
| success: true, |
| message: 'Delivery completed successfully', |
| data: { |
| delivery: updatedDelivery, |
| transaction, |
| }, |
| }); |
| } catch (error) { |
| console.error('Complete delivery error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to complete delivery', |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const uploadPhotos = async (req, res) => { |
| try { |
| const { id } = req.params; |
| const { photos } = req.body; |
|
|
| if (!photos || !Array.isArray(photos)) { |
| return res.status(400).json({ |
| success: false, |
| message: 'Photos are required as an array of URLs', |
| }); |
| } |
|
|
| const delivery = await prisma.delivery.findUnique({ |
| where: { id }, |
| }); |
|
|
| if (!delivery || delivery.driverId !== req.user.id) { |
| return res.status(404).json({ |
| success: false, |
| message: 'Delivery not found', |
| }); |
| } |
|
|
| |
| |
| |
|
|
| res.status(200).json({ |
| success: true, |
| message: 'Photos uploaded successfully (simulated)', |
| count: photos.length, |
| }); |
| } catch (error) { |
| console.error('Upload photos error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to upload photos', |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const createDelivery = async (req, res) => { |
| try { |
| const { |
| pickupLocation, |
| pickupLat, |
| pickupLng, |
| pickupTime, |
| deliveryLocation, |
| deliveryLat, |
| deliveryLng, |
| deliveryTime, |
| cargoType, |
| cargoWeight, |
| cargoVolumeLtrs, |
| cargoValue, |
| specialInstructions, |
| courierCompanyId, |
| dispatcherId, |
| distanceKm, |
| postalCode, |
| timeWindowStart, |
| timeWindowEnd |
| } = req.body; |
|
|
| |
| if (!pickupLocation || !deliveryLocation || !cargoType || !cargoWeight || !cargoVolumeLtrs) { |
| return res.status(400).json({ |
| success: false, |
| message: 'Missing required fields: pickupLocation, deliveryLocation, cargoType, cargoWeight, cargoVolumeLtrs' |
| }); |
| } |
|
|
| |
| if (!pickupLat || !pickupLng || !deliveryLat || !deliveryLng) { |
| return res.status(400).json({ |
| success: false, |
| message: 'Missing required coordinates: pickupLat, pickupLng, deliveryLat, deliveryLng' |
| }); |
| } |
|
|
| |
| const finalDispatcherId = dispatcherId || (req.user ? req.user.id : null); |
| if (!finalDispatcherId) { |
| return res.status(400).json({ |
| success: false, |
| message: 'dispatcherId is required' |
| }); |
| } |
|
|
| |
| const packageId = `PKG-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`; |
|
|
| |
| const delivery = await prisma.delivery.create({ |
| data: { |
| packageId, |
| pickupLocation, |
| pickupLat: parseFloat(pickupLat), |
| pickupLng: parseFloat(pickupLng), |
| pickupTime: pickupTime ? new Date(pickupTime) : null, |
| dropLocation: deliveryLocation, |
| dropLat: parseFloat(deliveryLat), |
| dropLng: parseFloat(deliveryLng), |
| dropTime: deliveryTime ? new Date(deliveryTime) : null, |
| cargoType, |
| cargoWeight: parseFloat(cargoWeight), |
| cargoVolumeLtrs: parseFloat(cargoVolumeLtrs), |
| distanceKm: distanceKm ? parseFloat(distanceKm) : null, |
| postalCode: postalCode || null, |
| timeWindowStart: timeWindowStart ? new Date(timeWindowStart) : null, |
| timeWindowEnd: timeWindowEnd ? new Date(timeWindowEnd) : null, |
| |
| courierCompanyId: courierCompanyId || null, |
| status: 'PENDING', |
| dispatcherId: finalDispatcherId |
| } |
| }); |
|
|
| res.status(201).json({ |
| success: true, |
| message: 'Delivery created successfully', |
| data: delivery |
| }); |
| } catch (error) { |
| console.error('Create delivery error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to create delivery', |
| error: error.message |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| const getUnassignedDeliveries = async (req, res) => { |
| try { |
| const { courierCompanyId } = req.query; |
|
|
| if (!courierCompanyId) { |
| return res.status(400).json({ |
| success: false, |
| message: 'courierCompanyId is required', |
| }); |
| } |
|
|
| const whereClause = { |
| status: 'PENDING', |
| driverId: null, |
| truckId: null, |
| optimizedRouteId: null, |
| OR: [ |
| { courierCompanyId: courierCompanyId }, |
| { courierCompanyId: null } |
| ] |
| }; |
|
|
| const [count, deliveries] = await Promise.all([ |
| prisma.delivery.count({ where: whereClause }), |
| prisma.delivery.findMany({ |
| where: whereClause, |
| include: { |
| shipment: true, |
| }, |
| orderBy: { |
| createdAt: 'desc', |
| }, |
| }), |
| ]); |
|
|
| res.status(200).json({ |
| success: true, |
| data: { |
| count, |
| deliveries, |
| }, |
| }); |
| } catch (error) { |
| console.error('Get unassigned deliveries error:', error); |
| res.status(500).json({ |
| success: false, |
| message: 'Failed to fetch unassigned deliveries', |
| error: error.message, |
| }); |
| } |
| }; |
|
|
| module.exports = { |
| getAssignedDeliveries, |
| acceptDelivery, |
| rejectDelivery, |
| startDelivery, |
| pickupCargo, |
| completeDelivery, |
| uploadPhotos, |
| createDelivery, |
| getUnassignedDeliveries, |
| }; |