From 3698d26208f6572c71e47182e11353e357a5274f Mon Sep 17 00:00:00 2001 From: Josepablo C Date: Tue, 22 Jul 2025 20:01:21 -0600 Subject: [PATCH] feat(driver): Adding a driver endpoint for fetching loads --- v1/src/apps/private/index.js | 2 + v1/src/apps/private/loads_driver/routes.js | 9 + v1/src/apps/private/loads_driver/services.js | 322 +++++++++++++++++++ 3 files changed, 333 insertions(+) create mode 100644 v1/src/apps/private/loads_driver/routes.js create mode 100644 v1/src/apps/private/loads_driver/services.js diff --git a/v1/src/apps/private/index.js b/v1/src/apps/private/index.js index 4e26bf9..1f62546 100644 --- a/v1/src/apps/private/index.js +++ b/v1/src/apps/private/index.js @@ -12,6 +12,7 @@ const branches = require('./branches/routes.js'); const companies = require('./companies/routes.js'); const loadAttachments = require('./load-attachments/routes.js'); const loads = require('./loads/routes.js'); +const loads_driver = require('./loads_driver/routes.js'); const proposals = require('./proposals/routes.js'); const users = require('./users/routes.js'); const vehicles = require('./vehicles/routes.js'); @@ -26,6 +27,7 @@ router.use('/branches', branches); router.use('/companies', companies); router.use('/load-attachments', loadAttachments ); router.use('/loads', loads); +router.use('/loads_driver', loads_driver); router.use('/notifications', notifications); router.use('/proposals', proposals); router.use('/users', users); diff --git a/v1/src/apps/private/loads_driver/routes.js b/v1/src/apps/private/loads_driver/routes.js new file mode 100644 index 0000000..6d61bc0 --- /dev/null +++ b/v1/src/apps/private/loads_driver/routes.js @@ -0,0 +1,9 @@ +'use strict'; +const router = require('express').Router(); +const services= require('./services.js'); + +router.get('/find', services.findList); +router.patch('/:id', services.patchLoad); +router.get('/:id', services.getById); + +module.exports = router; diff --git a/v1/src/apps/private/loads_driver/services.js b/v1/src/apps/private/loads_driver/services.js new file mode 100644 index 0000000..7a924de --- /dev/null +++ b/v1/src/apps/private/loads_driver/services.js @@ -0,0 +1,322 @@ +"use strict"; +const { getModel } = require( '../../../lib/Models' ); +const { getPagination, genKey, normalizeDate } = require( '../../../lib/Misc.js' ); +const { GenericHandler } = require( '../../../lib/Handlers/Generic.handler.js' ); +const { onPatchEvent } = require('../../../lib/Handlers/Loads.handler'); +const Model = getModel('loads'); +const CompanyModel = getModel('companies'); +const ProposalsModel = getModel('proposals'); +const branchesModel = getModel('branches'); + +const carrier_projection = [ + 'company_name', + 'company_code', + 'createdAt', + 'rfc' +]; + +const vehicle_projection = [ + 'vehicle_code', + 'truck_type', + 'driver', + 'categories', + 'circulation_serial_number', + 'trailer_plate_1', + 'trailer_plate_2', + 'notes', + 'city', + 'available_in' +]; +const user_projection = ['first_name','last_name','middle_name']; +const populate_list = [ + 'product', + 'company', + 'categories', + 'origin_warehouse', + 'destination_warehouse', + {path:'carrier',select: carrier_projection }, + {path:'vehicle',select: vehicle_projection }, + {path:'driver',select: user_projection }, + {path:'bidder',select: user_projection }, +]; +const generic = new GenericHandler( Model, null, populate_list ); + +function getAndFilterList( query ){ + const filter_list = []; + const { + company, + carrier, + vehicle, + driver, + status, + posted_by, + posted_by_name, + load_status, + published_date, + loaded_date, + transit_date, + categories, + product, + shipment_code, + origin_warehouse, + destination_warehouse, + est_loading_date, + est_unloading_date, + alert_list, + truck_type, + state, + city, + } = query; + + if( company ){ filter_list.push( { company } ); } + if( carrier ){ filter_list.push( { carrier } ); } + if( vehicle ){ filter_list.push( { vehicle } ); } + if( driver ){ filter_list.push( { driver } ); } + if( status ){ filter_list.push( { status } ); } + if( posted_by ) { filter_list.push({ posted_by }); } + if( posted_by_name ) { filter_list.push({ posted_by_name }); } + if( load_status ) { filter_list.push({ load_status }); } + if( published_date ) { filter_list.push({ published_date }); } + if( loaded_date ) { filter_list.push({ loaded_date }); } + if( transit_date ) { filter_list.push({ transit_date }); } + if( categories ) { filter_list.push({ categories }); } + if( product ) { filter_list.push({ product }); } + if( shipment_code ) { filter_list.push({ shipment_code }); } + if( origin_warehouse ) { filter_list.push({ origin_warehouse }); } + if( destination_warehouse ) { filter_list.push({ destination_warehouse }); } + if( alert_list ) { filter_list.push({ alert_list }); } + if( truck_type ) { filter_list.push({ truck_type }); } + + if( state ) { + filter_list.push({ + $or:[ + { "origin.state": state }, + { "destination.state": state }, + ] + }); + } + + if( city ) { + filter_list.push({ + $or:[ + { "origin.city": city }, + { "destination.city": city }, + ] + }); + } + + if( est_loading_date ) { + if( (est_loading_date.gte == undefined) || (est_loading_date.gte == null) ){ + throw "est_loading_date[gte] is required"; + } + if( (est_loading_date.lte == undefined) || (est_loading_date.lte == null) ){ + throw "est_loading_date[lte] is required"; + } + filter_list.push({ + "est_loading_date" : { + $gte : normalizeDate( est_loading_date["gte"] ), + $lte : normalizeDate( est_loading_date["lte"] ) + } + }); + } + + if( est_unloading_date ) { + if( (est_unloading_date.gte == undefined) || (est_unloading_date.gte == null) ){ + throw "est_unloading_date[gte] is required"; + } + if( (est_unloading_date.lte == undefined) || (est_unloading_date.lte == null) ){ + throw "est_unloading_date[lte] is required"; + } + filter_list.push({ + "est_unloading_date" : { + $gte : normalizeDate( est_unloading_date["gte"] ), + $lte : normalizeDate( est_unloading_date["lte"] ) + } + }); + } + + if( filter_list.length == 0 ){ + return null; + } + return filter_list; +} + +async function findLoads( query ){ + const { $sort, company_name } = query; + const { page, elements } = getPagination( query ); + const andFilterList = getAndFilterList( query ) || []; + + let filter; + + if( company_name ){ + /* Populate list of company ids with match on the company_name */ + const company_list = await CompanyModel.find( { company_name }, [ "id" ] ); + const or_company_list = [] + company_list.forEach( (item) =>{ + or_company_list.push({"company" : item.id}); + }) + andFilterList.push({ + $or : or_company_list + }); + } + + if( andFilterList.length > 0 ){ + filter = { $and : andFilterList }; + }else{ + filter = null; + } + + const { total , limit, skip, data } = await generic.getList( page , elements, filter, null, $sort ); + + const load_list = data; + + for(let i=0; i { + try{ + const query = req.query || {}; + const retVal = await findLoads( query ); + res.send( retVal ); + }catch(error){ + console.error( error ); + return res.status( 500 ).send({ error }); + } +}; + +const getById = async(req, res) => { + try{ + const elementId = req.params.id; + res.send( await findElementById( elementId ) ); + }catch(error){ + console.error( error ); + return res.status( 500 ).send({ error }); + } +}; + +function normalizeDatesFromData( data ){ + let fields2normalize = [ + "contract_start_date", + "contract_end_date", + "est_loading_date", + "est_unloading_date", + "published_date", + "loaded_date", + "transit_date", + "delivered_date", + ]; + for( const item of fields2normalize ){ + if( Object.hasOwn( data, item ) ){ + data[ item ] = normalizeDate( data[ item ] ) + } + } + return data; +} + +function getDataToModify( data ){ + /** + * Take the only fields from model that + * should be modifiable by the client. + * The rest are populated on demand by the event handlers. + */ + let data_fields = { + alert_list : null, + origin_warehouse : null, + destination_warehouse : null, + origin: null, + origin_geo: null, + destination: null, + destination_geo: null, + categories : null, + product : null, + truck_type : null, + tyre_type : null, + weight : null, + estimated_cost : null, + distance : null, + actual_cost : null, + status : null, + load_status : null, + contract_start_date : null, + contract_end_date : null, + est_loading_date : null, + est_unloading_date : null, + published_date : null, + loaded_date : null, + transit_date : null, + delivered_date : null, + load_status_updated : null, + notes : null, + payment_term : null, + terms_and_conditions : null, + }; + + let filtered_data = {}; + + if( Object.keys( data_fields ).length === 0 ){ + throw "nothing to change"; + } + + for ( const [key, value] of Object.entries( data_fields ) ) { + if( Object.hasOwn( data, key ) ){ + filtered_data[ key ] = data[ key ]; + } + } + + if( Object.keys( filtered_data ).length === 0 ){ + throw "nothing to change"; + } + + return normalizeDatesFromData( filtered_data ); +} + +const patchLoad = async(req, res) => { + try{ + const elementId = req.params.id; + const load = await findElementById( elementId ); + + if( !load ){ + throw "You can't modify this load"; + } + + if( !req.body ){ + throw "load data not sent"; + } + + const data = getDataToModify( req.body ); + + await Model.findByIdAndUpdate( elementId , data ); + + await onPatchEvent( req.context.userId, elementId, data ); + + return res.send( await Model.findById( elementId ) ); + }catch(error){ + console.error( error ); + return res.status( 500 ).send({ error }); + } +}; + +module.exports = { findList, getById, patchLoad };