const fs = require('fs'); const moment = require('moment'); const Validator = require('validatorjs'); const path = require("path"); const axios = require('axios').default; const url = require('url'); const mysql = require('mysql'); const response = require('../config/response'); const Helper = require('../library/LibHelper'); // const LibPassword = require('../library/LibPassword'); const LibJwt = require('../library/LibJwt'); const LibHelper = require('../library/LibHelper'); const LibFirebase = require('../library/LibFirebase'); const LibCurl = require('../library/LibCurl'); const LibFile = require('../library/LibFile'); const LibMail = require('../library/LibMail'); const LibLogReqResApi = require('../library/LibLogReqResApi'); const LibImgGeotagging = require('../library/LibImgGeotagging'); const GpsTracksModels = require('../models/GpsTracksModels'); const DriversModels = require('../models/DriversModels'); const OrdersModels = require('../models/OrdersModels'); const OrdersPickupsModels = require('../models/OrdersPickupsModels'); const OrdersDropsModels = require('../models/OrdersDropsModels'); const OrdersPckDropsModels = require('../models/OrdersPckDropsModels'); const OrdersDriversUploadsModels = require('../models/OrdersDriversUploadsModels'); const OrdersCheckpointsModels = require('../models/OrdersCheckpointsModels'); const VhcModels = require('../models/VhcModels'); const ZoneModels = require('../models/ZoneModels'); const UsersModels = require('../models/UsersModels'); const OrdersAItemsModels = require('../models/OrdersAItemsModels'); const OrdersTerminsModels = require('../models/OrdersTerminsModels'); const LogbookKeysModels = require('../models/LogbookKeysModels'); const LogbookOrdersModels = require('../models/LogbookOrdersModels'); const OrdersLogsTfModels = require('../models/OrdersLogsTfModels'); const DanaModels = require('../models/DanaModels'); Validator.useLang('id'); moment.locale('id'); class ServiceDriverController { // AUTHENTIFICATION async login(req, res) { let apiRes = {} try { const now = moment().unix() // input validation const input = { phone: req.body.phone, device_id: req.body.device_id, fcm_token: req.body.fcm_token, lat: req.body.lat, lng: req.body.lng, brand: req.body.brand || '0', product: req.body.product || '0', model: req.body.model || '0', type: req.body.type || '0', manufacture: req.body.manufacture || '0', android_id: req.body.android_id || '0', host: req.body.host || '0', hardware: req.body.hardware || '0', display: req.body.display || '0', board: req.body.board || '0', bootloader: req.body.bootloader || '0', v_sdk_int: req.body.v_sdk_int || '0', v_preview_sdk_int: req.body.v_preview_sdk_int || '0', v_release: req.body.v_release || '0', v_incremental: req.body.v_incremental || '0', v_codename: req.body.v_codename || '0', v_base_os: req.body.v_base_os || '0', v_security_patch: req.body.v_security_patch || '0', // password: req.body.password, }; const rulesInput = { phone: 'required|integer', device_id: 'required|string|max:16', fcm_token: 'required|string|max:255', lat: 'required|string|max:125', lng: 'required|string|max:125', brand: 'string|max:125', product: 'string|max:125', model: 'string|max:125', type: 'string|max:125', manufacture: 'string|max:125', android_id: 'string|max:125', host: 'string|max:125', hardware: 'string|max:125', display: 'string|max:125', board: 'string|max:125', bootloader: 'string|max:125', v_sdk_int: 'string|max:125', v_preview_sdk_int: 'string|max:125', v_release: 'string|max:125', v_incremental: 'string|max:125', v_codename: 'string|max:125', v_base_os: 'string|max:125', v_security_patch: 'string|max:125', // password: 'required|string', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } // login const getDrivers = await DriversModels.findPhone(input.phone); if (getDrivers.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_1'; apiRes.meta.message = 'account not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } // const isPwValid = await LibPassword.checkPw(getDrivers[0].password, input.password); // if (!isPwValid) { // apiRes.meta = JSON.parse(JSON.stringify(response['wrong_password'].meta)); // return res.status(200).json(apiRes); // } /** * create or update t_users based on t_drivers data without password, with role as driver * create or update t_phone_devices * case multiple device => update hanya berlaku jika device_id(IMEI) sama, jadi did bisa duplicate namun device_id(IMEI) tidak ada yang duplicate * case single device => update hanya berlaku jika did sama, jadi tidak ada did dan device_id(IMEI) yang duplicate * harus tentuin salah satu mau single / multiple, soalnya nanti bingung ngambil fcm_tokennya * is_login ? taro di t_phone_devices aja, jadi ketauan kalo dia logout * login_lat & login_lng ? taro di t_phone_devices aja, jadi ketauan device ini adanya dimana * jika sedang ada yang login maka driver tidak bisa dihapus */ // yang sekarang 1 device 1 driver const checkDeviceByDid = await DriversModels.getDeviceByDid(getDrivers[0].id); let phone_device_id = 0; if (checkDeviceByDid.length < 1) { const checkDeviceByImei = await DriversModels.getDeviceByImei(input.device_id); if (checkDeviceByImei.length < 1) { const insDevice = { ...input }; insDevice.did = getDrivers[0].id; insDevice.is_active = DriversModels.IS_ACTIVE; insDevice.is_login = DriversModels.IS_LOGIN; insDevice.login_lat = input.lat; insDevice.login_lng = input.lng; insDevice.login_at = now; insDevice.crt = now; insDevice.updt = now; delete insDevice.phone; delete insDevice.lat; delete insDevice.lng; const insPhoneDevice = await DriversModels.insPhoneDevice(insDevice); phone_device_id = insPhoneDevice.insertId; } else { if (checkDeviceByImei[0].is_login === DriversModels.IS_LOGIN) { if (checkDeviceByImei[0].device_id !== input.device_id) { // || checkDeviceByImei[0].phone !== input.phone apiRes = JSON.parse(JSON.stringify(response[400])); apiRes.meta.code += '_2'; apiRes.meta.message = 'account is used by another phone, please contact customer service'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } } const updtDevice = { ...input }; updtDevice.did = getDrivers[0].id; updtDevice.is_active = DriversModels.IS_ACTIVE; updtDevice.is_login = DriversModels.IS_LOGIN; updtDevice.login_lat = input.lat; updtDevice.login_lng = input.lng; updtDevice.login_at = now; updtDevice.updt = now; delete updtDevice.phone; delete updtDevice.lat; delete updtDevice.lng; const updtPhoneDevice = await DriversModels.updtPhoneDevice(updtDevice, checkDeviceByImei[0].id); phone_device_id = checkDeviceByImei[0].id; } } else { if (checkDeviceByDid[0].is_login === DriversModels.IS_LOGIN) { if (checkDeviceByDid[0].device_id !== input.device_id) { // || checkDeviceByDid[0].phone !== input.phone apiRes = JSON.parse(JSON.stringify(response[400])); apiRes.meta.code += '_2'; apiRes.meta.message = 'account is used by another phone, please contact customer service'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } } const updtDevice = { ...input }; updtDevice.did = getDrivers[0].id; updtDevice.is_active = DriversModels.IS_ACTIVE; updtDevice.is_login = DriversModels.IS_LOGIN; updtDevice.login_lat = input.lat; updtDevice.login_lng = input.lng; updtDevice.login_at = now; updtDevice.updt = now; delete updtDevice.phone; delete updtDevice.lat; delete updtDevice.lng; const updtPhoneDevice = await DriversModels.updtPhoneDevice(updtDevice, checkDeviceByDid[0].id); phone_device_id = checkDeviceByDid[0].id; } delete getDrivers[0].client_group_id; delete getDrivers[0].client_div_id; delete getDrivers[0].client_gp_id; // delete getDrivers[0].status; delete getDrivers[0].is_in_ord; delete getDrivers[0].ord_id; delete getDrivers[0].ord_code; delete getDrivers[0].ord_ids; delete getDrivers[0].ord_code; // success response const jwt = await LibJwt.createToken({ did: getDrivers[0].id, phone: getDrivers[0].phone, device_id: input.device_id, phone_device_id, uid: 0, }); const profileDriver = { drv_id: getDrivers[0].id, nik: getDrivers[0].nik, fullname: getDrivers[0].fullname, fullname2: getDrivers[0].fullname2 || '', phone: getDrivers[0].phone_code + '' +getDrivers[0].phone, phone2: getDrivers[0].phone2_code + '' +getDrivers[0].phone2, email: getDrivers[0].email || '', dob: moment(getDrivers[0].dob).format('DD-MM-YYYY'), age: getDrivers[0].age || 0, gender: (getDrivers[0].gender === 1) ? 'Laki-Laki' : 'Perempuan', blood: getDrivers[0].blood || '', fulladdress: getDrivers[0].fulladdress || '', vdr_id: getDrivers[0].vendor_id || 0, // status: getDrivers[0].status, // is_in_ord: getDrivers[0].is_in_ord, // ord_ids: (getDrivers[0].ord_ids === null) ? getDrivers[0].ord_ids : JSON.parse(getDrivers[0].ord_ids), token: jwt.token, }; apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.meta.message = 'success login'; apiRes.data = profileDriver; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async profile(req, res) { let apiRes = {} try { const now = moment().unix(); const { did } = req.auth; const getDrivers = await DriversModels.find(did); if (getDrivers.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_1'; apiRes.meta.message = 'account not found, please login again'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const profileDriver = { drv_id: getDrivers[0].id, nik: getDrivers[0].nik, fullname: getDrivers[0].fullname, fullname2: getDrivers[0].fullname2 || '', phone: getDrivers[0].phone_code + '' +getDrivers[0].phone, phone2: getDrivers[0].phone2_code + '' +getDrivers[0].phone2, email: getDrivers[0].email || '', dob: (getDrivers[0].dob) ? moment(getDrivers[0].dob).format('DD-MM-YYYY') : '', age: getDrivers[0].age || 0, gender: (getDrivers[0].gender === 1) ? 'Laki-Laki' : 'Perempuan', blood: getDrivers[0].blood || '', fulladdress: getDrivers[0].fulladdress || '', vdr_id: getDrivers[0].vendor_id || 0, // status: getDrivers[0].status, // is_in_ord: getDrivers[0].is_in_ord, // ord_ids: (getDrivers[0].ord_ids === null) ? getDrivers[0].ord_ids : JSON.parse(getDrivers[0].ord_ids), }; apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = profileDriver; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async logout(req, res) { let apiRes = {} try { const now = moment().unix(); const { phone_device_id } = req.auth; const updtDevice = { is_login: DriversModels.IS_LOGOUT, logout_at: now, }; const updtPhoneDevice = await DriversModels.updtPhoneDevice(updtDevice, phone_device_id); apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.meta.message = 'success logout'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } // JOBS async listActiveJobs(req, res) { let apiRes = {} try { const now = moment().unix(); // console.log(Buffer.from(`32_34_34_28`, 'ascii').toString('hex')); const { did, device_id, phone } = req.auth; const filter = { is_active: OrdersModels.IS_ACTIVE, // order_status: [OrdersModels.STTS_HAVE_GET_VHC,OrdersModels.STTS_PCK,OrdersModels.STTS_GO,OrdersModels.STTS_ARV,OrdersModels.STTS_DROP], delivery_statuses: [ OrdersPckDropsModels.STTS_DELIVERY_OTW_PICKUP,OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_PICKUP,OrdersPckDropsModels.STTS_DELIVERY_PROCESS_PICKUP ,OrdersPckDropsModels.STTS_DELIVERY_FINISH_PICKUP,OrdersPckDropsModels.STTS_DELIVERY_TRAVEL_DOC,OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP ,OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP,OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP,OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP // ,OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC ], get_prefer_type_truck: 1, couple_pck_drop: 1, get_pic_zone: 1, did, }; const activeOrders = await OrdersModels.listOrders(filter); if (activeOrders.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code = '200'; // += '_1'; apiRes.meta.message = 'there are no active orders'; apiRes.data = []; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const formattedActiveOrders = [], includedPckId = [], includedDropId = []; for (const row of activeOrders) { if (includedPckId.includes(row.ord_pck_id) || includedDropId.includes(row.ord_drop_id)) continue; includedPckId.push(row.ord_pck_id); includedDropId.push(row.ord_drop_id); const detail_id = Buffer.from(`${row.ord_id}_${row.ord_pck_id}_${row.ord_drop_id}_${row.ord_pck_drop_id}`, 'ascii').toString('hex'); // const decoded_detail_id = Buffer.from('34365f34305f3339', 'hex').toString('ascii'); let status_desc = ''; if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_OTW_PICKUP) { status_desc = 'Kendaraan Menuju Lokasi Jemput'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_PICKUP) { status_desc = 'Kendaraan Tiba dilokasi Jemput'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_PROCESS_PICKUP) { status_desc = 'Kendaraan Memuat Barang'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_FINISH_PICKUP) { status_desc = 'Kendaraan Selesai Memuat Barang'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_TRAVEL_DOC) { status_desc = 'Driver Sudah Mengupload Dokumen Perjalanan'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP) { status_desc = 'Kendaraan Menuju Lokasi Pengantaran'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP) { status_desc = 'Kendaraan Sampai Dilokasi Pengantaran'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP) { status_desc = 'Kendaraan Membongkar Barang'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP) { status_desc = 'Kendaraan Selesai Membongkar Barang'; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC) { status_desc = 'Driver Sudah Mengupload Dokumen Serah Terima'; } // validate is_going / ongoing by pickup date let is_ongoing = false; if (row.set_pck_at) { let cur_date = moment.unix(now).set({hour:0,minute:0,second:0,millisecond:0}); // as start let pck_date = moment.unix(row.set_pck_at).set({hour:0,minute:0,second:0,millisecond:0}); // as end let duration = moment.duration(pck_date.diff(cur_date)); if (duration.asDays() <= 0) { is_ongoing = true; } } // https://stackoverflow.com/questions/23299950/convert-date-to-utc-using-moment-js formattedActiveOrders.push({ detail_id, // ord_pck_drop_id: row.ord_pck_drop_id, ord_id: row.ord_id, ord_code: row.ord_code, // pck_total: row.pck_total, // drop_total: row.drop_total, is_ongoing, pck: { id: row.ord_pck_id, set_at_unix: row.set_pck_at, // set_at_raw: (row.set_pck_at != 0) ? moment.unix(row.set_pck_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // utc set_at_raw: (row.set_pck_at != 0) ? moment.unix(row.set_pck_at).format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // gmt +7 // stts: row.stts_pck, name: row.pck_name, addr: row.pck_addr, pic_name: row.pck_pic_name, pic_phone: row.pck_pic_phone_code + '' + row.pck_pic_phone_val, pic_mail: row.pck_pic_mail || '', client_name: row.c_name, client_phone: row.c_phone_code + '' + row.c_phone_val, client_mail: row.c_mail, client_prefer_truck_type: row.prefer_truck_type_name || '', }, // going_at_unix: row.going_at, // going_at_raw: (row.going_at != 0) ? moment.unix(row.going_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // going_note: row.going_note, drop: { id: row.ord_drop_id, // stts: row.ord_drop_stts, name: row.drop_name, addr: row.drop_addr, pic_name: row.drop_pic_name, pic_phone: row.drop_pic_phone_code + '' + row.drop_pic_phone_val, pic_mail: row.drop_pic_mail || '', client_name: row.c_name, client_phone: row.c_phone_code + '' + row.c_phone_val, client_mail: row.c_mail, client_prefer_truck_type: row.prefer_truck_type_name || '', }, // arrived_at_unix: row.arrived_at, // arrived_at_raw: (row.arrived_at != 0) ? moment.unix(row.arrived_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // arrived_note: row.arrived_note, lead_time: row.lead_time, lead_time_unit: row.lead_time_unit, // buy_price: row.buy_price, status: row.stts_delivery, status_desc, // c_name: row.c_name, // c_phone: row.c_phone_code + '' + row.c_phone_val, // c_mail: row.c_mail, // prefer_truck_type: row.prefer_truck_type, // prefer_truck_type_name: row.prefer_truck_type_name || '', vdr: { name: row.vdr_name, phone: row.vdr_phone_code + '' + row.vdr_phone_val, mail: row.vdr_mail, addr: row.vdr_addr, }, drv: { name: row.drv_name, kenek_name: row.drv_name2, phone: row.drv_phone_code + '' + row.drv_phone_val, kenek_phone: row.drv_phone2_code + '' + row.drv_phone2_val, mail: row.drv_mail, addr: row.drv_addr, }, vhc: { name_bukan_nopol: row.vhc_name, nopol1: row.vhc_nopol1, nopol2: row.vhc_nopol2, nopol3: row.vhc_nopol3, nopol_full: row.vhc_nopol1 + ' ' + row.vhc_nopol2 + ' ' + row.vhc_nopol3, }, }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedActiveOrders; // apiRes.raw = activeOrders; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async detailJobs(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const { detail_id } = req.params; // input validation const input = { detail_id, }; const rulesInput = { detail_id: 'required|string', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const decoded_detail_id = Buffer.from(detail_id, 'hex').toString('ascii'); const split_detail_id = decoded_detail_id.split('_'); if (split_detail_id.length !== 4) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.code += '_1'; apiRes.meta.message = 'detail id not valid'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const [ ord_id, pck_id, drop_id, ord_pck_drop_id ] = split_detail_id; const filter = { ord_id, pck_id, drop_id, ord_pck_drop_id, // is_active: OrdersModels.IS_ACTIVE, // order_status: [OrdersModels.STTS_HAVE_GET_VHC,OrdersModels.STTS_PCK,OrdersModels.STTS_GO,OrdersModels.STTS_ARV,OrdersModels.STTS_DROP], get_prefer_type_truck: 1, couple_pck_drop: 1, get_pic_zone: 1, did, }; const activeOrders = await OrdersModels.listOrders(filter); if (activeOrders.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_4'; apiRes.meta.message = 'job not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const drvUps = await OrdersDriversUploadsModels.list({ ord_id,pck_id,drop_id,ord_pck_drop_id }); /** * 0 => disable * 1 => enable * 2 => finish */ const menu = { menu_arrived_pickup: { content: '1. Foto sudah dilokasi muat', status: 0, photo: '', }, menu_process_pickup: { content: '2. Foto sedang memuat barang', status: 0, photo: '', }, menu_finish_pickup: { content: '3. Foto selesai memuat barang', status: 0, photo: '', }, menu_travel_document: { content: '4. Foto dokumen perjalanan', status: 0, photo: '', }, menu_otw_drop: { content: '5. Foto saat diperjalanan (/2jam)', status: 0, photo: [], }, menu_arrived_drop: { content: '6. Foto tiba dilokasi bongkar', status: 0, photo: '', }, menu_process_drop: { content: '7. Foto proses bongkar', status: 0, photo: '', }, menu_finish_drop: { content: '8. Foto selesai bongkar', status: 0, photo: '', }, menu_handover_document: { content: '9. Foto dokumen serah terima', status: 0, photo: '', }, menu_accident: { content: 'Menu Accident', status: 1, photo: [], }, }; for (const drvUp of drvUps) { if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_PICKUP) { menu.menu_arrived_pickup.status = 2; menu.menu_arrived_pickup.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_PICKUP) { menu.menu_process_pickup.status = 2; menu.menu_process_pickup.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_PICKUP) { menu.menu_finish_pickup.status = 2; menu.menu_finish_pickup.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_TRAVEL_DOCUMENT) { menu.menu_travel_document.status = 2; menu.menu_travel_document.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_OTW_DROP) { // if (row.drv_app_lock_menu_otw_drop_at !== 0) { menu.menu_otw_drop.status = 2; } else { menu.menu_otw_drop.status = 1; } menu.menu_otw_drop.status = 2; menu.menu_otw_drop.photo.push(drvUp.img); } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_DROP) { menu.menu_arrived_drop.status = 2; menu.menu_arrived_drop.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_DROP) { menu.menu_process_drop.status = 2; menu.menu_process_drop.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_DROP) { menu.menu_finish_drop.status = 2; menu.menu_finish_drop.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_HANDOVER_DOCUMENT) { menu.menu_handover_document.status = 2; menu.menu_handover_document.photo = drvUp.img; } else if (drvUp.stts === OrdersDriversUploadsModels.PHOTO_STATUS_ACCIDENT) { menu.menu_accident.status = 1; menu.menu_accident.photo.push(drvUp.img); } } const formattedActiveOrders = [], includedPckId = [], includedDropId = []; for (const row of activeOrders) { if (includedPckId.includes(row.ord_pck_id) || includedDropId.includes(row.ord_drop_id)) continue; includedPckId.push(row.ord_pck_id); includedDropId.push(row.ord_drop_id); const detail_id = Buffer.from(`${row.ord_id}_${row.ord_pck_id}_${row.ord_drop_id}_${row.ord_pck_drop_id}`, 'ascii').toString('hex'); // const decoded_detail_id = Buffer.from('34365f34305f3339', 'hex').toString('ascii'); let status_desc = ''; let is_wait_aprv_otw_drop = 0; // 0=>disable, 1=>enable let is_wait_aprv_otw_drop_desc = ''; // old way // if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_OTW_PICKUP) { status_desc = 'Kendaraan Menuju Lokasi Jemput'; menu.menu_arrived_pickup.status = 1; } // else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_PICKUP) { status_desc = 'Kendaraan Tiba dilokasi Jemput'; menu.menu_process_pickup.status = 1; } // else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_PROCESS_PICKUP) { status_desc = 'Kendaraan Memuat Barang'; menu.menu_finish_pickup.status = 1; } // else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_FINISH_PICKUP) { // status_desc = 'Kendaraan Selesai Memuat Barang'; // if (menu.menu_travel_document.status === 2) { menu.menu_otw_drop.status = 1; } else { menu.menu_travel_document.status = 1; } // } // else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP) { // status_desc = 'Kendaraan Menuju Lokasi Pengantaran'; // if (row.drv_app_lock_menu_otw_drop_at !== 0) { menu.menu_arrived_drop.status = 1; } else { menu.menu_otw_drop.status = 1; } // } // else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP) { status_desc = 'Kendaraan Sampai Dilokasi Pengantaran'; menu.menu_process_drop.status = 1; } // else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP) { status_desc = 'Kendaraan Membongkar Barang'; menu.menu_finish_drop.status = 1; } // else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP) { // status_desc = 'Kendaraan Selesai Membongkar Barang'; // if (menu.menu_handover_document.status === 2) { } else { menu.menu_handover_document.status = 1; } // } // new way if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_OTW_PICKUP) { status_desc = 'Kendaraan Menuju Lokasi Jemput'; menu.menu_arrived_pickup.status = 1; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_PICKUP) { status_desc = 'Kendaraan Tiba dilokasi Jemput'; menu.menu_process_pickup.status = 1; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_PROCESS_PICKUP) { status_desc = 'Kendaraan Memuat Barang'; menu.menu_finish_pickup.status = 1; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_FINISH_PICKUP) { status_desc = 'Kendaraan Selesai Memuat Barang'; menu.menu_travel_document.status = 1; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_TRAVEL_DOC) { status_desc = 'Driver Sudah Mengupload Dokumen Perjalanan'; if (row.is_aprv_pck == OrdersPckDropsModels.IS_APRV_YES) { menu.menu_otw_drop.status = 1; is_wait_aprv_otw_drop = 1; is_wait_aprv_otw_drop_desc = 'Menunggu persetujuan dari checker'; } else { menu.menu_otw_drop.status = 0; // disable sementara, seharusnya 0 } } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP) { status_desc = 'Kendaraan Menuju Lokasi Pengantaran'; if (row.drv_app_lock_menu_otw_drop_at !== 0) { menu.menu_arrived_drop.status = 1; } else { menu.menu_otw_drop.status = 1; } } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP) { status_desc = 'Kendaraan Sampai Dilokasi Pengantaran'; menu.menu_process_drop.status = 1; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP) { status_desc = 'Kendaraan Membongkar Barang'; menu.menu_finish_drop.status = 1; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP) { status_desc = 'Kendaraan Selesai Membongkar Barang'; menu.menu_handover_document.status = 1; } else if (row.stts_delivery === OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC) { status_desc = 'Driver Sudah Mengupload Dokumen Serah Terima'; } // validate is_going / ongoing by pickup date let is_ongoing = false; if (row.set_pck_at) { let cur_date = moment.unix(now).set({hour:0,minute:0,second:0,millisecond:0}); // as start let pck_date = moment.unix(row.set_pck_at).set({hour:0,minute:0,second:0,millisecond:0}); // as end let duration = moment.duration(pck_date.diff(cur_date)); if (duration.asDays() <= 0) { is_ongoing = true; } } formattedActiveOrders.push({ detail_id, // ord_pck_drop_id: row.ord_pck_drop_id, ord_id: row.ord_id, ord_code: row.ord_code, // pck_total: row.pck_total, // drop_total: row.drop_total, is_ongoing, pck: { id: row.ord_pck_id, set_at_unix: row.set_pck_at, // set_at_raw: (row.set_pck_at != 0) ? moment.unix(row.set_pck_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // utc set_at_raw: (row.set_pck_at != 0) ? moment.unix(row.set_pck_at).format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // gmt +7 // stts: row.stts_pck, name: row.pck_name, addr: row.pck_addr, pic_name: row.pck_pic_name, pic_phone: row.pck_pic_phone_code + '' + row.pck_pic_phone_val, pic_mail: row.pck_pic_mail || '', client_name: row.c_name, client_phone: row.c_phone_code + '' + row.c_phone_val, client_mail: row.c_mail, client_prefer_truck_type: row.prefer_truck_type_name || '', }, // going_at_unix: row.going_at, // going_at_raw: (row.going_at != 0) ? moment.unix(row.going_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // going_note: row.going_note, drop: { id: row.ord_drop_id, // stts: row.ord_drop_stts, name: row.drop_name, addr: row.drop_addr, pic_name: row.drop_pic_name, pic_phone: row.drop_pic_phone_code + '' + row.drop_pic_phone_val, pic_mail: row.drop_pic_mail || '', client_name: row.c_name, client_phone: row.c_phone_code + '' + row.c_phone_val, client_mail: row.c_mail, client_prefer_truck_type: row.prefer_truck_type_name || '', }, // arrived_at_unix: row.arrived_at, // arrived_at_raw: (row.arrived_at != 0) ? moment.unix(row.arrived_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // arrived_note: row.arrived_note, lead_time: row.lead_time, lead_time_unit: row.lead_time_unit, // buy_price: row.buy_price, status: row.stts_delivery, status_desc, is_wait_aprv_otw_drop, is_wait_aprv_otw_drop_desc, // c_name: row.c_name, // c_phone: row.c_phone_code + '' + row.c_phone_val, // c_mail: row.c_mail, // prefer_truck_type: row.prefer_truck_type, // prefer_truck_type_name: row.prefer_truck_type_name || '', vdr: { name: row.vdr_name, phone: row.vdr_phone_code + '' + row.vdr_phone_val, mail: row.vdr_mail, addr: row.vdr_addr, }, drv: { name: row.drv_name, kenek_name: row.drv_name2, phone: row.drv_phone_code + '' + row.drv_phone_val, kenek_phone: row.drv_phone2_code + '' + row.drv_phone2_val, mail: row.drv_mail, addr: row.drv_addr, }, vhc: { name_bukan_nopol: row.vhc_name, nopol1: row.vhc_nopol1, nopol2: row.vhc_nopol2, nopol3: row.vhc_nopol3, nopol_full: row.vhc_nopol1 + ' ' + row.vhc_nopol2 + ' ' + row.vhc_nopol3, }, menu, }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedActiveOrders[0]; // apiRes.raw = activeOrders; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async jobPhotoTransitions(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const { detail_id, type_up_photo, photo, lat, lng, photo_at } = req.body; // input validation const input = { detail_id, type_up_photo, photo, lat, lng, photo_at, }; const rulesInput = { detail_id: 'required|string', type_up_photo: 'required|numeric|max:12', photo: 'required|string', lat: 'required|string|max:125', lng: 'required|string|max:125', photo_at: 'required|numeric', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const decoded_detail_id = Buffer.from(detail_id, 'hex').toString('ascii'); const split_detail_id = decoded_detail_id.split('_'); if (split_detail_id.length !== 4) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.code += '_1'; apiRes.meta.message = 'detail id not valid'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const [ ord_id, pck_id, drop_id, ord_pck_drop_id ] = split_detail_id; const required_type_up_photo = [OrdersDriversUploadsModels.PHOTO_STATUS_OTW_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_TRAVEL_DOCUMENT,OrdersDriversUploadsModels.PHOTO_STATUS_OTW_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_HANDOVER_DOCUMENT,OrdersDriversUploadsModels.PHOTO_STATUS_ACCIDENT]; if (!required_type_up_photo.includes(type_up_photo)) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.code += '_2'; apiRes.meta.message = 'type up photo not valid'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const filter = { ord_id, pck_id, drop_id, ord_pck_drop_id, // is_active: OrdersModels.IS_ACTIVE, // order_status: [OrdersModels.STTS_HAVE_GET_VHC,OrdersModels.STTS_PCK,OrdersModels.STTS_GO,OrdersModels.STTS_ARV,OrdersModels.STTS_DROP], get_prefer_type_truck: 1, couple_pck_drop: 1, get_pic_zone: 1, did, }; const activeOrders = await OrdersModels.listOrders(filter); if (activeOrders.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_4'; apiRes.meta.message = 'job not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } // validate inside zone const check_pck_zone = [OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_TRAVEL_DOCUMENT]; const check_drop_zone = [OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_HANDOVER_DOCUMENT]; if (check_pck_zone.includes(type_up_photo)) { const inCircle = await ZoneModels.getOrdInCircle(lat, lng, { zid: activeOrders[0].pck_id }); const inShape = await ZoneModels.getOrdInShape(lat, lng, { zid: activeOrders[0].pck_id }); if (inCircle.length < 1 && inShape.length < 1) { apiRes = JSON.parse(JSON.stringify(response[400])); apiRes.meta.code += '_2'; apiRes.meta.message = 'driver should inside pickup zone'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } } // else if (check_drop_zone.includes(type_up_photo)) { // const inCircle = await ZoneModels.getOrdInCircle(lat, lng, { zid: activeOrders[0].drop_id }); // const inShape = await ZoneModels.getOrdInShape(lat, lng, { zid: activeOrders[0].drop_id }); // if (inCircle.length < 1 && inShape.length < 1) { // apiRes = JSON.parse(JSON.stringify(response[400])); // apiRes.meta.code += '_3'; // apiRes.meta.message = 'driver should inside drop zone'; LibLogReqResApi.log(req, apiRes); // return res.status(200).json(apiRes); // } // } // status transitions const insUploads = []; const updtOrd = {}, updtPck = {}, updtDrop = {}, updtPckDrop = {}; if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_PICKUP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_PCK; // t_orders_pickups updtPck.id = pck_id; updtPck.stts_pck = OrdersPickupsModels.STTS_PCK_WAIT; // updtPck.pck_enter_at = now; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_PICKUP; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_PICKUP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_PCK; // t_orders_pickups updtPck.id = pck_id; updtPck.stts_pck = OrdersPickupsModels.STTS_PCK_PICKING; updtPck.pck_at = now; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_PROCESS_PICKUP; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_PICKUP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_GO; updtOrd.going_at = now; // t_orders_pickups updtPck.id = pck_id; updtPck.stts_pck = OrdersPickupsModels.STTS_PCK_PICKED; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_FINISH_PICKUP; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_TRAVEL_DOCUMENT) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_GO; // t_orders_pickups updtPck.id = pck_id; updtPck.stts_pck = OrdersPickupsModels.STTS_PCK_PICKED; // updtPck.pck_leave_at = now; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_TRAVEL_DOC; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_OTW_DROP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_GO; // t_orders_pickups updtPck.id = pck_id; updtPck.stts_pck = OrdersPickupsModels.STTS_PCK_PICKED; // t_orders_drops updtDrop.id = drop_id; updtDrop.stts_drop = OrdersDropsModels.STTS_DROP_WAIT; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_DROP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_ARV; updtOrd.arrived_at = now; // t_orders_drops updtDrop.id = drop_id; updtDrop.stts_drop = OrdersDropsModels.STTS_DROP_WAIT; // updtDrop.drop_enter_at = now; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_DROP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_ARV; // t_orders_drops updtDrop.id = drop_id; updtDrop.stts_drop = OrdersDropsModels.STTS_DROP_DROPPING; updtDrop.drop_at = now; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_DROP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_DROP; // t_orders_drops updtDrop.id = drop_id; updtDrop.stts_drop = OrdersDropsModels.STTS_DROP_DROPED; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP; } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_HANDOVER_DOCUMENT) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_CLIENT_PAY; // t_orders_drops updtDrop.id = drop_id; updtDrop.stts_drop = OrdersDropsModels.STTS_DROP_DROPED; // updtDrop.drop_leave_at = now; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC; // kalau sudah selesai drop vhc & driver bisa order lagi let remaining_multi_drops = await OrdersPckDropsModels.listOrdPckDrop({ ord_id, not_stts: [OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP, OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC] }); let vehicle = await VhcModels.getVhcById(activeOrders[0].vhc_id); let updtVhc = {}; if (vehicle[0].ord_ids) { let vhc_in_ord_ids = JSON.parse(vehicle[0].ord_ids); for (let key in vhc_in_ord_ids) { if (vhc_in_ord_ids[key] == ord_id) { if (vhc_in_ord_ids.length <= 1) { if (remaining_multi_drops.length < 1) { updtVhc = { is_in_ord: VhcModels.IN_ORD_NO, ord_id: 0, ord_code: 0, ord_ids: null, }; } else { vhc_in_ord_ids.splice(key, 1); updtVhc['ord_ids'] = vhc_in_ord_ids; updtVhc['ord_ids'] = JSON.stringify(updtVhc['ord_ids']); if (vehicle[0].ord_id == ord_id) updtVhc['ord_id'] = 0; if (vehicle[0].ord_code == activeOrders[0].ord_code) updtVhc['ord_code'] = 0; } } else { vhc_in_ord_ids.splice(key, 1); updtVhc['ord_ids'] = vhc_in_ord_ids; updtVhc['ord_ids'] = JSON.stringify(updtVhc['ord_ids']); if (vehicle[0].ord_id == ord_id) updtVhc['ord_id'] = 0; if (vehicle[0].ord_code == activeOrders[0].ord_code) updtVhc['ord_code'] = 0; } } } } // if (updtVhc) VhcModles.update(activeOrders[0].vhc_id, updtVhc); let driver = await DriversModels.find(activeOrders[0].drv_id); let updtDrv = {}; if (driver[0].ord_ids) { let drv_in_ord_ids = JSON.parse(driver[0].ord_ids); for (let key in drv_in_ord_ids) { if (drv_in_ord_ids[key] == ord_id) { if (drv_in_ord_ids.length <= 1) { if (remaining_multi_drops.length < 1) { updtDrv = { is_in_ord: DriversModels.IN_ORD_NO, ord_id: 0, ord_code: 0, ord_ids: null, }; } else { drv_in_ord_ids.splice(key, 1); updtDrv['ord_ids'] = drv_in_ord_ids; updtDrv['ord_ids'] = JSON.stringify(updtDrv['ord_ids']); if (driver[0].ord_id == ord_id) updtDrv['ord_id'] = 0; if (driver[0].ord_code == activeOrders[0].ord_code) updtDrv['ord_code'] = 0; } } else { drv_in_ord_ids.splice(key, 1); updtDrv['ord_ids'] = drv_in_ord_ids; updtDrv['ord_ids'] = JSON.stringify(updtDrv['ord_ids']); if (driver[0].ord_id == ord_id) updtDrv['ord_id'] = 0; if (driver[0].ord_code == activeOrders[0].ord_code) updtDrv['ord_code'] = 0; } } } } // if (updtDrv) DriversModels.update(updtDrv, activeOrders[0].drv_id); } else if (type_up_photo == OrdersDriversUploadsModels.PHOTO_STATUS_ACCIDENT) { // t_orders updtOrd.id = ord_id; // updtOrd.is_accident = OrdersModels.IS_ACCIDENT; // updtOrd.acdnt_at = now; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.is_accident = OrdersModels.IS_ACCIDENT; updtPckDrop.acdnt_at = now; } let stts_reverse_geo = 0; const respRGeo = await LibCurl.reverseGeo(lat, lng); let { country_id, country_code, country_text, state_id, state_text, city_id, city_text, district_id, district_text, village_id, village_text, postcode, streets, fulladdress, log_reverse_geo } = respRGeo.addrData || {}; if (respRGeo.type === 'sc') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_SC; } else if (respRGeo.type === 'no_available') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_LOST; } else if (respRGeo.type === 'fail') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_ER; } else if (respRGeo.type === 'error') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_ER; } // start geotagging image const tempFileName = moment().valueOf() + '.jpeg'; const tempFileNamePath = path.resolve(__dirname, '../files/storage') + '/' + tempFileName; await LibImgGeotagging.create(tempFileName, tempFileNamePath, photo, {fulladdress, lat, lng, photo_at}); const apiSavePhotos = process.env.FMS_URL + 'api/v1/storage/save_photos'; const namePhoto = `ords_pcks_drops/${ord_pck_drop_id}/drvs_ups/${did}/${now}.jpeg`; const respSavePhotos = await LibCurl.formurlencoded(apiSavePhotos, { names: [namePhoto], photos: [fs.readFileSync(tempFileNamePath, {encoding: 'base64'})], }); LibFile.remove(tempFileName); if (respSavePhotos.type !== 'sc') { apiRes = JSON.parse(JSON.stringify(response[400])); apiRes.meta.code += '_1'; apiRes.meta.message = 'fail upload photo'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } // end geotagging image // get region_id if (!village_id) { const region = await LibCurl.getRegionId(postcode); if (typeof region.addrData !== 'undefined') { if (region.addrData.state_id) state_id = region.addrData.state_id; if (region.addrData.state_text) state_text = region.addrData.state_text.toUpperCase(); if (region.addrData.city_id) city_id = region.addrData.city_id; if (region.addrData.city_text) city_text = region.addrData.city_text.toUpperCase(); if (region.addrData.district_id) district_id = region.addrData.district_id; if (region.addrData.district_text) district_text = region.addrData.district_text.toUpperCase(); if (region.addrData.village_id) village_id = region.addrData.village_id; if (region.addrData.village_text) village_text = region.addrData.village_text.toUpperCase(); } } // t_orders_drivers_uploads insUploads.push({ did, ord_id, ord_code: activeOrders[0].ord_code, pck_id, drop_id, ord_pck_drop_id, stts: type_up_photo, is_active: OrdersDriversUploadsModels.IS_ACTIVE, img: process.env.FMS_URL + `storage/${namePhoto}`, // desc: undefined, lat, lng, country_id: country_id || null, country_code: country_code || null, country_text: country_text || null, state_id: state_id || null, state_text: state_text || null, city_id: city_id || null, city_text: city_text || null, district_id: district_id || null, district_text: district_text || null, village_id: village_id || null, village_text: village_text || null, postcode: postcode || null, streets: streets || null, fulladdress: fulladdress || null, stts_reverse_geo, log_reverse_geo, crt: now, crt_drv_at: photo_at, crt_by: did, updt: now, updt_by: did, }); OrdersModels.bundleInsDrvUploads(insUploads, updtOrd, updtPck, updtDrop, updtPckDrop); apiRes = JSON.parse(JSON.stringify(response[200])); // apiRes.data = activeOrders; // apiRes.raw = respRGeo.addrData || {}; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async lockMenu(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const { detail_id, type_lock_menu } = req.body; // input validation const input = { detail_id, type_lock_menu, }; const rulesInput = { detail_id: 'required|string', type_lock_menu: 'required|numeric|max:12', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const decoded_detail_id = Buffer.from(detail_id, 'hex').toString('ascii'); const split_detail_id = decoded_detail_id.split('_'); if (split_detail_id.length !== 4) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.code += '_1'; apiRes.meta.message = 'detail id not valid'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const [ ord_id, pck_id, drop_id, ord_pck_drop_id ] = split_detail_id; const required_type_lock_menu = [OrdersDriversUploadsModels.PHOTO_STATUS_OTW_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_PICKUP,OrdersDriversUploadsModels.PHOTO_STATUS_TRAVEL_DOCUMENT,OrdersDriversUploadsModels.PHOTO_STATUS_OTW_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_ARRIVED_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_PROCESS_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_FINISH_DROP,OrdersDriversUploadsModels.PHOTO_STATUS_HANDOVER_DOCUMENT,OrdersDriversUploadsModels.PHOTO_STATUS_ACCIDENT]; if (!required_type_lock_menu.includes(type_lock_menu)) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.code += '_2'; apiRes.meta.message = 'type lock menu not valid'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const filter = { ord_id, pck_id, drop_id, ord_pck_drop_id, // is_active: OrdersModels.IS_ACTIVE, // order_status: [OrdersModels.STTS_HAVE_GET_VHC,OrdersModels.STTS_PCK,OrdersModels.STTS_GO,OrdersModels.STTS_ARV,OrdersModels.STTS_DROP], get_prefer_type_truck: 1, couple_pck_drop: 1, get_pic_zone: 1, did, }; const activeOrders = await OrdersModels.listOrders(filter); if (activeOrders.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_4'; apiRes.meta.message = 'job not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const insUploads = []; const updtOrd = {}, updtPck = {}, updtDrop = {}, updtPckDrop = {}; if (type_lock_menu === OrdersDriversUploadsModels.PHOTO_STATUS_OTW_DROP) { // t_orders updtOrd.id = ord_id; updtOrd.status = OrdersModels.STTS_GO; // t_orders_pickups updtPck.id = pck_id; updtPck.stts_pck = OrdersPickupsModels.STTS_PCK_PICKED; // t_orders_drops updtDrop.id = drop_id; updtDrop.stts_drop = OrdersDropsModels.STTS_DROP_WAIT; // t_orders_pck_drops updtPckDrop.id = ord_pck_drop_id; updtPckDrop.drv_app_lock_menu_otw_drop_at = now; updtPckDrop.stts = OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP; } OrdersModels.bundleInsDrvUploads(insUploads, updtOrd, updtPck, updtDrop, updtPckDrop); apiRes = JSON.parse(JSON.stringify(response[200])); // apiRes.data = activeOrders; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } // FINANCE async listPockets(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; // input validation const input = {}; const rulesInput = {}; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const checkpoints = await OrdersCheckpointsModels.list({ did, is_paid: OrdersCheckpointsModels.IS_PAID_OFF, order_by: ' ORDER BY checkpoint.pocket_paid_at ASC', }); if (checkpoints.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_1'; apiRes.meta.message = 'there are no received pocket money'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const formattedCheckpoints = []; for (const checkpoint of checkpoints) { formattedCheckpoints.push({ checkpoint_id: checkpoint.checkpoint_id, ord_id: checkpoint.ord_id, ord_code: checkpoint.ord_code, ord_pocket_id: checkpoint.ord_pocket_id, // pocket_sort: checkpoint.pocket_sort, pocket_total: checkpoint.pocket_total, pocket_is_paid: checkpoint.pocket_is_paid, pocket_paid_at: checkpoint.pocket_paid_at, pocket_paid_at_raw: (checkpoint.pocket_paid_at != 0) ? moment.unix(checkpoint.pocket_paid_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // pocket_paid_by: checkpoint.pocket_paid_by, pocket_paid_by_name: checkpoint.pocket_paid_by_name || '', }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedCheckpoints; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async detailPocket(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const { checkpoint_id } = req.params; // input validation const input = { checkpoint_id, }; const rulesInput = { 'checkpoint_id': 'required|integer', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const checkpoints = await OrdersCheckpointsModels.list({ did, checkpoint_id, limit: 1, }); if (checkpoints.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_1'; apiRes.meta.message = 'pocket money not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const formattedCheckpoints = []; for (const checkpoint of checkpoints) { formattedCheckpoints.push({ checkpoint_id: checkpoint.checkpoint_id, ord_id: checkpoint.ord_id, ord_code: checkpoint.ord_code, ord_pocket_id: checkpoint.ord_pocket_id, // pocket_sort: checkpoint.pocket_sort, pocket_total: checkpoint.pocket_total, pocket_is_paid: checkpoint.pocket_is_paid, pocket_paid_at: checkpoint.pocket_paid_at, pocket_paid_at_raw: (checkpoint.pocket_paid_at != 0) ? moment.unix(checkpoint.pocket_paid_at).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', // pocket_paid_by: checkpoint.pocket_paid_by, pocket_paid_by_name: checkpoint.pocket_paid_by_name || '', }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedCheckpoints[0]; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } // LOGBOOK async listLogBooks(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const filter = { is_active: LogbookKeysModels.IS_ACTIVE, }; const listKeys = await LogbookKeysModels.list(filter); if (listKeys.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code = '200'; // += '_1'; apiRes.meta.message = 'there are no list logbook'; apiRes.data = []; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const formattedListKeys = [], includedLogbookId = []; for (const row of listKeys) { if (includedLogbookId.includes(row.lgb_id)) continue; includedLogbookId.push(row.lgb_id); // https://stackoverflow.com/questions/23299950/convert-date-to-utc-using-moment-js formattedListKeys.push({ lgb_id: row.lgb_id, lgb_name: row.name, updt_at: row.updt, updt_at_raw: (row.updt != 0) ? moment.unix(row.updt).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedListKeys; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async detailLogBook(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const { lgb_id } = req.params; // input validation const input = { lgb_id, }; const rulesInput = { 'lgb_id': 'required|integer', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const filter = { lgb_id, is_active: LogbookKeysModels.IS_ACTIVE, limit: 1, }; const listKeys = await LogbookKeysModels.list(filter); if (listKeys.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_4'; apiRes.meta.message = 'logbook not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const formattedListKeys = [], includedLogbookId = []; for (const row of listKeys) { if (includedLogbookId.includes(row.lgb_id)) continue; includedLogbookId.push(row.lgb_id); const params = []; row.keys = JSON.parse(row.keys); row.units = JSON.parse(row.units); row.dtypes = JSON.parse(row.dtypes); for (let i in row.keys) { params.push({ key: row.keys[i], unit: row.units[i], dtype: row.dtypes[i], value: "", }) } // https://stackoverflow.com/questions/23299950/convert-date-to-utc-using-moment-js formattedListKeys.push({ lgb_id: row.lgb_id, lgb_name: row.name, updt_at: row.updt, updt_at_raw: (row.updt != 0) ? moment.unix(row.updt).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', params, }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedListKeys[0]; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async insOrdLogBook(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const { lgb_id, detail_id, params } = req.body; // input validation const input = { lgb_id, detail_id, params, }; const rulesInput = { 'lgb_id': 'required|integer', 'detail_id': 'required|string', 'params': 'required|array', 'params.*.key': 'required|string|max:255', 'params.*.unit': 'required|string|max:255', 'params.*.dtype': 'required|string|max:255', 'params.*.value': 'required|string', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const decoded_detail_id = Buffer.from(detail_id, 'hex').toString('ascii'); const split_detail_id = decoded_detail_id.split('_'); if (split_detail_id.length !== 4) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.code += '_1'; apiRes.meta.message = 'detail id not valid'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const [ ord_id, pck_id, drop_id, ord_pck_drop_id ] = split_detail_id; const filter = { ord_id, pck_id, drop_id, ord_pck_drop_id, // is_active: OrdersModels.IS_ACTIVE, // order_status: [OrdersModels.STTS_HAVE_GET_VHC,OrdersModels.STTS_PCK,OrdersModels.STTS_GO,OrdersModels.STTS_ARV,OrdersModels.STTS_DROP], get_prefer_type_truck: 1, couple_pck_drop: 1, get_pic_zone: 1, did, }; const activeOrders = await OrdersModels.listOrders(filter); if (activeOrders.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_4'; apiRes.meta.message = 'job not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const filter1 = { lgb_id, is_active: LogbookKeysModels.IS_ACTIVE, join_type: 1, limit: 1, }; const listKeys = await LogbookKeysModels.list(filter1); if (listKeys.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_5'; apiRes.meta.message = 'logbook not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } let dy_cols = "(COLUMN_CREATE(", photo = ''; let values = []; for (const param of params) { // dy_cols += "'key', '" + param.key + "', 'unit', '" + param.unit + "', 'dtype', '" + param.dtype + "', 'value', '" + param.value + "',"; // as reference only if (param.dtype === LogbookKeysModels.IMG_BASE64) { // start reverse geo let stts_reverse_geo = 0; const respRGeo = await LibCurl.reverseGeo(param.lat, param.lng); let { country_id, country_code, country_text, state_id, state_text, city_id, city_text, district_id, district_text, village_id, village_text, postcode, streets, fulladdress, log_reverse_geo } = respRGeo.addrData || {}; if (respRGeo.type === 'sc') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_SC; } else if (respRGeo.type === 'no_available') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_LOST; } else if (respRGeo.type === 'fail') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_ER; } else if (respRGeo.type === 'error') { stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_ER; } // end reverse geo // start get region_id // if (!village_id) { // const region = await LibCurl.getRegionId(postcode); // if (typeof region.addrData !== 'undefined') { // if (region.addrData.state_id) state_id = region.addrData.state_id; // if (region.addrData.state_text) state_text = region.addrData.state_text.toUpperCase(); // if (region.addrData.city_id) city_id = region.addrData.city_id; // if (region.addrData.city_text) city_text = region.addrData.city_text.toUpperCase(); // if (region.addrData.district_id) district_id = region.addrData.district_id; // if (region.addrData.district_text) district_text = region.addrData.district_text.toUpperCase(); // if (region.addrData.village_id) village_id = region.addrData.village_id; // if (region.addrData.village_text) village_text = region.addrData.village_text.toUpperCase(); // } // } // // end get region_id // start geotagging image const tempFileName = moment().valueOf() + '.jpeg'; const tempFileNamePath = path.resolve(__dirname, '../files/storage') + '/' + tempFileName; await LibImgGeotagging.create(tempFileName, tempFileNamePath, param.value, {fulladdress: fulladdress || '', lat: param.lat || '', lng: param.lng || '', photo_at: param.photo_at || ''}); const apiSavePhotos = process.env.FMS_URL + 'api/v1/storage/save_photos'; const namePhoto = `ords_pcks_drops/${ord_pck_drop_id}/drvs_logbooks/${did}/${now}.jpeg`; const respSavePhotos = await LibCurl.formurlencoded(apiSavePhotos, { names: [namePhoto], photos: [fs.readFileSync(tempFileNamePath, {encoding: 'base64'})], }); LibFile.remove(tempFileName); if (respSavePhotos.type !== 'sc') { apiRes = JSON.parse(JSON.stringify(response[400])); apiRes.meta.code += '_1'; apiRes.meta.message = 'fail upload photo'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } // end geotagging image param.value = process.env.FMS_URL + `storage/${namePhoto}`; } // start dy_cols dy_cols += "'" + param.key + "', '" + param.value + "',"; values.push(param.value); // end dy_cols } dy_cols = dy_cols.slice(0, -1) + "))"; dy_cols = mysql.raw(dy_cols); const insOrdLogbook = { ord_id, ord_code: activeOrders[0].ord_code, lgb_key_id: lgb_id, lgb_key_name: listKeys[0].name, lgb_type_id: listKeys[0].type, lgb_type_name: listKeys[0].type_name, vhc_id: activeOrders[0].vhc_id, drv_id: did, is_active: LogbookOrdersModels.IS_ACTIVE, dy_cols, keys: listKeys[0].keys, units: listKeys[0].units, dtypes: listKeys[0].dtypes, values: JSON.stringify(values), crt: now, crt_by: did, updt: now, updt_by: did, }; await LogbookOrdersModels.ins(insOrdLogbook); apiRes = JSON.parse(JSON.stringify(response[200])); LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async listOrdLogBooks(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const filter = { is_active: LogbookOrdersModels.IS_ACTIVE, }; const listKeys = await LogbookOrdersModels.list(filter); if (listKeys.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code = '200'; // += '_1'; apiRes.meta.message = 'there are no list orders logbooks'; apiRes.data = []; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const formattedListKeys = [], includedLogbookId = []; for (const row of listKeys) { if (includedLogbookId.includes(row.ord_lgb_id)) continue; includedLogbookId.push(row.ord_lgb_id); // https://stackoverflow.com/questions/23299950/convert-date-to-utc-using-moment-js formattedListKeys.push({ ord_lgb_id: row.ord_lgb_id, lgb_key_id: row.lgb_key_id, lgb_key_name: row.lgb_key_name, lgb_type_id: row.lgb_type_id, lgb_type_name: row.lgb_type_name, dy_cols: (row.dy_cols_json == null) ? '' : JSON.parse(row.dy_cols_json), keys: JSON.parse(row.keys), units: JSON.parse(row.units), dtypes: JSON.parse(row.dtypes), values: JSON.parse(row.values), crt_at: row.crt, crt_at_raw: (row.crt != 0) ? moment.unix(row.crt).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', updt_at: row.updt, updt_at_raw: (row.updt != 0) ? moment.unix(row.updt).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedListKeys; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } async detailOrdLogBook(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone } = req.auth; const { ord_lgb_id } = req.params; // input validation const input = { ord_lgb_id, }; const rulesInput = { 'ord_lgb_id': 'required|integer', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const filter = { ord_lgb_id, is_active: LogbookOrdersModels.IS_ACTIVE, limit: 1, }; const listKeys = await LogbookOrdersModels.list(filter); if (listKeys.length < 1) { apiRes = JSON.parse(JSON.stringify(response[404])); apiRes.meta.code += '_4'; apiRes.meta.message = 'order logbook not found'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const formattedListKeys = [], includedLogbookId = []; for (const row of listKeys) { if (includedLogbookId.includes(row.ord_lgb_id)) continue; includedLogbookId.push(row.ord_lgb_id); // https://stackoverflow.com/questions/23299950/convert-date-to-utc-using-moment-js formattedListKeys.push({ ord_lgb_id: row.ord_lgb_id, lgb_key_id: row.lgb_key_id, lgb_key_name: row.lgb_key_name, lgb_type_id: row.lgb_type_id, lgb_type_name: row.lgb_type_name, dy_cols: (row.dy_cols_json == null) ? '' : JSON.parse(row.dy_cols_json), keys: JSON.parse(row.keys), units: JSON.parse(row.units), dtypes: JSON.parse(row.dtypes), values: JSON.parse(row.values), crt_at: row.crt, crt_at_raw: (row.crt != 0) ? moment.unix(row.crt).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', updt_at: row.updt, updt_at_raw: (row.updt != 0) ? moment.unix(row.updt).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z' : '', }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formattedListKeys[0]; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } // RUNNING BACKGROUD // up lat,long data per 1 minute async upLocation(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, device_id, phone, phone_device_id } = req.auth; const { lat, lng, req_at } = req.body; // input validation const input = { lat, lng, req_at, }; const rulesInput = { lat: 'required|string|max:125', lng: 'required|string|max:125', req_at: 'required|numeric', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const filter = { did, // order_status: [OrdersModels.STTS_HAVE_GET_VHC,OrdersModels.STTS_PCK,OrdersModels.STTS_GO,OrdersModels.STTS_ARV,OrdersModels.STTS_DROP], is_active: OrdersModels.IS_ACTIVE, delivery_statuses: [ OrdersPckDropsModels.STTS_DELIVERY_OTW_PICKUP,OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_PICKUP,OrdersPckDropsModels.STTS_DELIVERY_PROCESS_PICKUP ,OrdersPckDropsModels.STTS_DELIVERY_FINISH_PICKUP,OrdersPckDropsModels.STTS_DELIVERY_TRAVEL_DOC,OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP ,OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP,OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP,OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP // ,OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC ], couple_pck_drop: 1, }; // kalo mau track yang nonactive, gausah ngeget vhc, lansung insert aja dengan vhc_id: 0 // const updtToOrders = await OrdersModels.listActiveOrdVhc(filter); const updtToOrders = await OrdersModels.listOrders(filter); /** * get lat, lng to all checkpoints paid = 0, sort != 1, order.dlt is null, * kenakan ke checkpoints yang order stts_deliverynya lebih dari travel dokumen * jika ada case negative di inCircle ada dan di di inShape ada maka mana yang didahulukan ? langsung dikenain aja 2-2nya * pastikan dulu ada di activeOrder * kenakan yang ord_pck_id lebih awal */ // BISA BUAT DEBUGGING: where_not_pocket_is_paid: OrdersCheckpointsModels.IS_PAID_OFF // BUAT PRODUCTION: pocket_is_paid: OrdersCheckpointsModels.IS_UNPAID const inCircle = await ZoneModels.getCheckpointsInCircle(lat, lng, {drv_id: did, pocket_is_paid: OrdersCheckpointsModels.IS_UNPAID, pocket_sort: '!= 1', stts_delivery: [OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP,OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP,OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP,OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP,OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC]}); const inShape = await ZoneModels.getCheckpointsInShape(lat, lng, {drv_id: did, pocket_is_paid: OrdersCheckpointsModels.IS_UNPAID, pocket_sort: '!= 1', stts_delivery: [OrdersPckDropsModels.STTS_DELIVERY_OTW_DROP,OrdersPckDropsModels.STTS_DELIVERY_ARRIVED_DROP,OrdersPckDropsModels.STTS_DELIVERY_PROCESS_DROP,OrdersPckDropsModels.STTS_DELIVERY_FINISH_DROP,OrdersPckDropsModels.STTS_DELIVERY_HANDOVER_DOC]}); let insideZones = []; if (inCircle.length > 0 || inShape.length > 0) { for (const circle of inCircle) { // hit api disbure if (circle.drv_bank_id == 0 || circle.drv_bank_acc_number == null) continue; insideZones.push(circle); } for (const shape of inShape) { // hit api disbure if (shape.drv_bank_id == 0 || shape.drv_bank_acc_number == null) continue; insideZones.push(shape); } } // checkpoints if (insideZones.length > 0) { const UNIT_TYPE_UNIT = 3; let admins = await UsersModels.getUsersActiveByRole(UsersModels.ROLE_FINANCE); let admins_data = []; for (const admin of admins) { admins_data.push({ 'admin_id': admin.id, 'admin_name': admin.first_name, 'admin_phone': admin.phone_code + ' ' + admin.phone, 'admin_mail': admin.email, 'admin_addr': '' }); } for (const insideZone of insideZones) { const insOrdAItems = { 'ord_id': insideZone.ord_id, 'ord_code': insideZone.ord_code, 'flow_type': OrdersAItemsModels.FLOW_TYPE_PAYMENT, 'cash_type': OrdersAItemsModels.TYPE_CASH_OUT, 'a_item_type': OrdersAItemsModels.A_TYPE_SECONDARY, 'unit_id': 0, 'unit_type': UNIT_TYPE_UNIT, 'amt_base_flat': insideZone.pocket_total, 'unit_qty': 0, 'amt_tax_type': 0, 'amt_tax_ppn_percent': 0, 'amt_tax_ppn_flat': 0, 'amt_tax_pph_percent': 0, 'amt_tax_pph_flat': 0, 'amt_total_tax_flat': 0, 'amt_result_flat': insideZone.pocket_total, 'amt_total_flat': insideZone.pocket_total, 'only_vdr': OrdersAItemsModels.ONLY_VDR_YES, 'bank_id': insideZone.drv_bank_id, 'bank_name': insideZone.drv_bank_name, 'bank_code': insideZone.drv_bank_code, 'bank_short_name': insideZone.drv_bank_short_name, 'bank_branch_name': insideZone.drv_bank_branch_name || null, 'bank_acc_name': insideZone.drv_bank_acc_name, 'bank_acc_number': insideZone.drv_bank_acc_number, 'ref_ord_id': insideZone.ord_id, 'ref_ord_code': insideZone.ord_code, 'crt': now, 'crt_by': 0, 'updt': now, 'updt_by': 0, 'desc': 'Uang Saku Driver di ' + insideZone.name, }; insOrdAItems.calc_to_vdr = OrdersAItemsModels.CALC_TO_VDR_YES; let termins = []; termins = await OrdersTerminsModels.listWithFilter({ 'ord_id': insideZone.ord_id, 'termin_for': OrdersTerminsModels.TERMIN_FOR_VENDOR, 'termin_is_paid': OrdersTerminsModels.IS_PAID_NO, 'in_stts_merge': [ OrdersAItemsModels.STTS_MERGE_NO, OrdersAItemsModels.STTS_MERGE_RESULT, ], }); insOrdAItems.v_termin_id = 0; insOrdAItems.ref_v_termin_id = 0; if (termins.length > 0) { insOrdAItems.v_termin_id = termins[0].id; insOrdAItems.ref_v_termin_id = termins[0].id; } if (insOrdAItems.v_termin_id === 0) { termins = await OrdersTerminsModels.listWithFilter({ 'ord_id': insideZone.ord_id, 'termin_for': OrdersTerminsModels.TERMIN_FOR_VENDOR, 'in_stts_merge': [ OrdersAItemsModels.STTS_MERGE_NO, OrdersAItemsModels.STTS_MERGE_RESULT, ], }); insOrdAItems.v_termin_id = termins[termins.length - 1].id; insOrdAItems.ref_v_termin_id = termins[termins.length - 1].id; } const mailData = { trx_code: insideZone.ord_code, drv_name: insideZone.drv_name, // pickup_zone_title, // pickup_zone_addr, // drop_zone_title, // drop_zone_addr, pocket_total: 'Rp'+((new Intl.NumberFormat('id-ID')).format(insideZone.pocket_total)).split('.').join('-').split(',').join('.').split('-').join(','), // yang . => - || , => . || - => , bank_name: insideZone.drv_bank_name, bank_code: insideZone.drv_bank_code, bank_branch_name: insideZone.drv_bank_branch_name || '', bank_acc_name: insideZone.drv_bank_acc_name, bank_acc_no: insideZone.drv_bank_acc_number, }; for (const updtOrd of updtToOrders) { if (updtOrd.id === insideZone.ord_id) { mailData.pickup_zone_title = updtOrd.pck_name; mailData.pickup_zone_addr = updtOrd.pck_addr; mailData.drop_zone_title = updtOrd.drop_name; mailData.drop_zone_addr = updtOrd.drop_addr; } } const danas = await DanaModels.list({'id': DanaModels.PK_ID, 'limit': 1}); if (danas.length < 1) { apiRes = JSON.parse(JSON.stringify(response[400])); apiRes.meta.message = 'dana error'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const dana = danas[0]; const payloadDana = { 'ord_id': insideZone.ord_id, 'ord_code': insideZone.ord_code, 'checkpoint_id': insideZone.checkpoint_id, 'bank_code': insideZone.drv_bank_code, 'bank_name': insideZone.drv_bank_name, 'bank_branch': insideZone.drv_bank_branch_name || null, 'amt': insideZone.pocket_total, 'acc_name': insideZone.drv_bank_acc_name, 'acc_number': insideZone.drv_bank_acc_number, 'device_id': device_id, }; if (dana.amt > insideZone.pocket_total && dana.amt > DanaModels.MINIMUM_AMT) { const axInstance = axios.create(); axios.defaults.timeout = 3000; axios.defaults.crossDomain = true; const respDana = await axInstance.post(process.env.API_DANA_BINGCORP + 'order/create', payloadDana, { timeout: 3000, headers: { // 'Host': '127.0.0.1', 'Content-Type': 'application/json', } }); const respDataDana = respDana.data || {}; if (respDana.status != 200 || respDataDana.meta.code != 200) { const insLogsTf = { 'ord_id': insideZone.ord_id, 'ord_code': insideZone.ord_code, 'type': OrdersLogsTfModels.TYPE_TF_CHECKPOINT, 'checkpoint_id': insideZone.checkpoint_id, 'amount': insideZone.pocket_total, 'stts': OrdersLogsTfModels.STTS_FAIL, 'fail_at': moment().unix(), 'note': respDataDana.data.note || 0, 'bank_id': insideZone.drv_bank_id, 'bank_name': insideZone.drv_bank_name, 'bank_code': insideZone.drv_bank_code, 'bank_short_name': insideZone.drv_bank_short_name, 'bank_branch_name': insideZone.drv_bank_branch_name || null, 'bank_acc_name': insideZone.drv_bank_acc_name, 'bank_acc_number': insideZone.drv_bank_acc_number, 'method': OrdersLogsTfModels.METHOD_DANA, 'url': process.env.API_DANA_BINGCORP + 'order/create', 'ref_code': respDataDana.data.dana_reference_no || 0, 'req_data': JSON.stringify(payloadDana), 'resp_data': JSON.stringify(respDataDana), 'crt': moment().unix(), 'updt': moment().unix(), }; const updtOrdCheckpoint = { 'id': insideZone.checkpoint_id, 'pocket_is_paid': OrdersCheckpointsModels.IS_TF_FAIL, 'pocket_paid_note': respDataDana.data.note, 'pocket_paid_at': now, 'pocket_paid_by': 0, 'bank_id': insideZone.drv_bank_id, 'bank_name': insideZone.drv_bank_name, 'bank_code': insideZone.drv_bank_code, 'bank_short_name': insideZone.drv_bank_short_name, 'bank_branch_name': insideZone.drv_bank_branch_name || null, 'bank_acc_name': insideZone.drv_bank_acc_name, 'bank_acc_number': insideZone.drv_bank_acc_number, 'tf_method': OrdersCheckpointsModels.TF_METHOD_DANA, 'tf_at': now, 'tf_url': process.env.API_DANA_BINGCORP + 'order/create', 'tf_ref_code': respDataDana.data.dana_reference_no || 0, 'tf_note': respDataDana.data.note || 0, 'tf_req_data': JSON.stringify(payloadDana), 'tf_resp_data': JSON.stringify(respDataDana), }; await OrdersCheckpointsModels.bundleUpdt(updtOrdCheckpoint, {}, insLogsTf); continue; } const insLogsTf = { 'ord_id': insideZone.ord_id, 'ord_code': insideZone.ord_code, 'type': OrdersLogsTfModels.TYPE_TF_CHECKPOINT, 'checkpoint_id': insideZone.checkpoint_id, 'amount': insideZone.pocket_total, 'stts': OrdersLogsTfModels.STTS_PAID, 'paid_at': moment().unix(), 'note': respDataDana.data.note || 0, 'bank_id': insideZone.drv_bank_id, 'bank_name': insideZone.drv_bank_name, 'bank_code': insideZone.drv_bank_code, 'bank_short_name': insideZone.drv_bank_short_name, 'bank_branch_name': insideZone.drv_bank_branch_name || null, 'bank_acc_name': insideZone.drv_bank_acc_name, 'bank_acc_number': insideZone.drv_bank_acc_number, 'method': OrdersLogsTfModels.METHOD_DANA, 'url': process.env.API_DANA_BINGCORP + 'order/create', 'ref_code': respDataDana.data.dana_reference_no || 0, 'req_data': JSON.stringify(payloadDana), 'resp_data': JSON.stringify(respDataDana), 'crt': moment().unix(), 'updt': moment().unix(), }; const updtOrdCheckpoint = { 'id': insideZone.checkpoint_id, 'pocket_is_paid': OrdersCheckpointsModels.IS_PAID_OFF, 'pocket_paid_at': now, 'pocket_paid_by': 0, 'bank_id': insideZone.drv_bank_id, 'bank_name': insideZone.drv_bank_name, 'bank_code': insideZone.drv_bank_code, 'bank_short_name': insideZone.drv_bank_short_name, 'bank_branch_name': insideZone.drv_bank_branch_name || null, 'bank_acc_name': insideZone.drv_bank_acc_name, 'bank_acc_number': insideZone.drv_bank_acc_number, 'tf_method': OrdersCheckpointsModels.TF_METHOD_DANA, 'tf_at': now, 'tf_url': process.env.API_DANA_BINGCORP + 'order/create', 'tf_ref_code': respDataDana.data.dana_reference_no || 0, 'tf_note': respDataDana.data.note || 0, 'tf_req_data': JSON.stringify(payloadDana), 'tf_resp_data': JSON.stringify(respDataDana), }; const updtDana = { 'id': dana.id, 'amt': mysql.raw('amt-'+insideZone.pocket_total), }; await OrdersCheckpointsModels.bundleUpdt(updtOrdCheckpoint, insOrdAItems, insLogsTf, updtDana); } else { for (const admin of admins_data) { mailData.admin_name = admin.admin_name; mailData.dana_current = 'Rp'+((new Intl.NumberFormat('id-ID')).format(dana.amt)).split('.').join('-').split(',').join('.').split('-').join(','); // yang . => - || , => . || - => ,, mailData.dana_minimum = 'Rp'+((new Intl.NumberFormat('id-ID')).format(DanaModels.MINIMUM_AMT)).split('.').join('-').split(',').join('.').split('-').join(','); // yang . => - || , => . || - => ,, LibMail.sendFinanceInfoDanaAmount('Saldo Dana Tidak Mencukupi', admin.admin_mail, mailData); } } for (const admin of admins_data) { mailData.admin_name = admin.admin_name; LibMail.sendFinanceInfoTfPocket('Transfer uang saku order ' + mailData.trx_code, admin.admin_mail, mailData); } } } // spawn zone const inSpawnCircle = await ZoneModels.getCheckpointsInCircle(lat, lng, {drv_id: did}); const inSpawnShape = await ZoneModels.getCheckpointsInShape(lat, lng, {drv_id: did}); let insideSpawnZone = []; if (inSpawnCircle.length > 0 || inSpawnShape.length > 0) { for (const circle of inSpawnCircle) { // hit api disbure if (circle.drv_bank_id == 0 || circle.drv_bank_acc_number == null) continue; insideSpawnZone.push(circle); } for (const shape of inSpawnShape) { // hit api disbure if (shape.drv_bank_id == 0 || shape.drv_bank_acc_number == null) continue; insideSpawnZone.push(shape); } } let logDevices = []; if (updtToOrders.length > 0) { const includedVhcId = []; for (const updtOrd of updtToOrders) { if (includedVhcId.includes(updtOrd.vhc_id)) continue; includedVhcId.push(updtOrd.vhc_id); const logDevice = { original_hex: null, protocol: GpsTracksModels.PROTOCOL_SMARTPHONE, action: GpsTracksModels.ACT_LOCATION, device_id, latitude: lat, longitude: lng, speed: null, orientation: 0, stts_engine: GpsTracksModels.STTS_EN_MOVING, stts_gps: GpsTracksModels.STTS_GPS_ON, stts_gsm: GpsTracksModels.STTS_GSM_STRONG_SIGNAL, stts_reverse_geo: GpsTracksModels.STTS_REVERSE_GEO_NOT, pre_milleage: 0, // dari jarak sebelumnya ke sekarang sum_milleage: 0, // calculated on device_id (IMEI) vhc_milleage: 0, // calucalted on vhc_id drv_milleage: 0, // calucalted on drv_id vhc_id: updtOrd.vhc_id, drv_id: did, source: GpsTracksModels.SOURCE_SMARTPHONE, crt: now, crt_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), crt_d: req_at, crt_d_format: moment.unix(req_at).format('YYYY-MM-DD HH:mm:ss'), crt_s: now, crt_s_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), }; const lastTrack = await GpsTracksModels.get2LastLoc({vhc_id: updtOrd.vhc_id}); // const lastTrack = await GpsTracksModels.get2LastLoc({device_id}); // kurang cocok by device_id, karena ada case update lokasi tanpa orderan yang sedang aktif // count milleage if (logDevice.latitude != null && logDevice.longitude != null && lastTrack.length > 0) { const distance = LibHelper.haversineGreatCircleDistance(lastTrack[0].latitude, lastTrack[0].longitude, logDevice.latitude, logDevice.longitude, LibHelper.EARTH_RADIUS_KM); const distance_km = LibHelper.kmToKm(distance, 100000000); console.log(`DRIVER UP LOCATION => vhc_id:${updtOrd.vhc_id}, did:${did}, distance_km:${distance_km}`); if (distance_km < 3) { const vhc = await VhcModels.getVhcById(logDevice.vhc_id); if (vhc.length > 0) { logDevice.pre_milleage = distance_km; logDevice.sum_milleage = (lastTrack[0].sum_milleage + logDevice.pre_milleage).toFixed(8); if (lastTrack[0].vhc_id == logDevice.vhc_id) { logDevice.vhc_milleage = (lastTrack[0].vhc_milleage + logDevice.pre_milleage).toFixed(8); } else { logDevice.vhc_milleage = (vhc[0].sum_milleage + logDevice.pre_milleage).toFixed(8); } const drv = await DriversModels.find(did); if (drv.length > 0) { if (lastTrack[0].did == logDevice.did) { logDevice.drv_milleage = (lastTrack[0].drv_milleage + logDevice.pre_milleage).toFixed(8); } else { logDevice.drv_milleage = (drv[0].sum_milleage + logDevice.pre_milleage).toFixed(8); } DriversModels.update({ sum_milleage: logDevice.drv_milleage }, did); } // VhcModels.update(updtOrd.vhc_id, { sum_milleage: logDevice.vhc_milleage }); } } } logDevices.push(logDevice); const currTrack = await GpsTracksModels.bundleCreate2(logDevice, {}); /** * get lastSpawn desc index 0, where leave_at = 0 * kalo gaada create, dengan isi field enter_at aja, kalo ada didalam zona * kalo ada update, dengan isi field leave_at aja, kalo udah diluar zona */ const lastSpawn = await GpsTracksModels.listSpawnZone({ source: GpsTracksModels.SOURCE_SMARTPHONE, vhc_id: updtOrd.vhc_id, drv_id: did, ord_id: updtOrd.ord_id, // ord_code: updtOrd.ord_code, leave_at_d: 0, order_by: 'ORDER BY id DESC', limit: 1, }); if (lastSpawn.length > 0) { let is_leave_zone = 1, is_leave_zone_pck = 1, is_leave_zone_drop = 1; for (const insideZone of insideSpawnZone) { if (insideZone.ord_id === updtOrd.ord_id && insideZone.zid === lastSpawn[0].zone_id) is_leave_zone = 0; if (insideZone.zid === updtOrd.pck_id) is_leave_zone_pck = 0; if (insideZone.zid === updtOrd.drop_id) is_leave_zone_drop = 0; } if (is_leave_zone === 1 && lastSpawn[0].ord_id === updtOrd.ord_id) { GpsTracksModels.updt2SpawnZone({ leave_lat: lat, leave_lng: lng, leave_at_d: req_at, leave_at_d_format: moment.unix(req_at).format('YYYY-MM-DD HH:mm:ss'), leave_at_s: now, leave_at_s_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), updt: now, updt_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), }, lastSpawn[0].id); } if (is_leave_zone_pck === 1 && updtOrd.pck_leave_at === 0) { OrdersPickupsModels.updt({ id: updtOrd.ord_pck_id, pck_leave_at: req_at, }); } if (is_leave_zone_drop === 1 && is_leave_zone_pck === 0 && updtOrd.pck_leave_at !== 0 && updtOrd.drop_leave_at === 0) { OrdersDropsModels.updt({ id: updtOrd.ord_drop_id, drop_leave_at: req_at, }); } } else { for (const insideZone of insideSpawnZone) { if (insideZone.ord_id === updtOrd.ord_id) { GpsTracksModels.create2SpawnZone({ device_id: device_id, master_id: Number(currTrack.result.insertId), enter_lat: lat, enter_lng: lng, enter_at_d: req_at, enter_at_d_format: moment.unix(req_at).format('YYYY-MM-DD HH:mm:ss'), enter_at_s: now, enter_at_s_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), zone_id: insideZone.zid, zone_name: insideZone.name, vhc_id: updtOrd.vhc_id, drv_id: did, ord_id: insideZone.ord_id, ord_pck_drop_id: insideZone.ord_pck_drop_id, // ord_code: insideZone.ord_code, source: GpsTracksModels.SOURCE_SMARTPHONE, crt: now, crt_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), }); } if (insideZone.zid === updtOrd.pck_id && updtOrd.pck_enter_at === 0) { OrdersPickupsModels.updt({ id: updtOrd.ord_pck_id, pck_enter_at: req_at, }); } if (insideZone.zid === updtOrd.drop_id && updtOrd.drop_enter_at === 0) { OrdersDropsModels.updt({ id: updtOrd.ord_drop_id, drop_enter_at: req_at, }); } } } } } else { const logDevice = { original_hex: null, protocol: GpsTracksModels.PROTOCOL_SMARTPHONE, action: GpsTracksModels.ACT_LOCATION, device_id, latitude: lat, longitude: lng, speed: null, orientation: 0, stts_engine: GpsTracksModels.STTS_EN_MOVING, stts_gps: GpsTracksModels.STTS_GPS_ON, stts_gsm: GpsTracksModels.STTS_GSM_STRONG_SIGNAL, stts_reverse_geo: GpsTracksModels.STTS_REVERSE_GEO_NOT, pre_milleage: 0, // dari jarak sebelumnya ke sekarang sum_milleage: 0, // calculated on device_id (IMEI) vhc_milleage: 0, // calucalted on vhc_id drv_milleage: 0, // calucalted on drv_id vhc_id: 0, drv_id: did, source: GpsTracksModels.SOURCE_SMARTPHONE, crt: now, crt_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), crt_d: req_at, crt_d_format: moment.unix(req_at).format('YYYY-MM-DD HH:mm:ss'), crt_s: now, crt_s_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), }; const lastTrack = await GpsTracksModels.get2LastLoc({drv_id: did}); // count milleage if (logDevice.latitude != null && logDevice.longitude != null && lastTrack.length > 0) { const distance = LibHelper.haversineGreatCircleDistance(lastTrack[0].latitude, lastTrack[0].longitude, logDevice.latitude, logDevice.longitude, LibHelper.EARTH_RADIUS_KM); const distance_km = LibHelper.kmToKm(distance, 100000000); console.log(`DRIVER UP LOCATION => did:${did}, distance_km:${distance_km}`); if (distance_km < 3) { const drv = await DriversModels.find(did); if (drv.length > 0) { logDevice.pre_milleage = distance_km; logDevice.sum_milleage = (lastTrack[0].sum_milleage + logDevice.pre_milleage).toFixed(8); if (lastTrack[0].did == logDevice.did) { logDevice.drv_milleage = (lastTrack[0].drv_milleage + logDevice.pre_milleage).toFixed(8); } else { logDevice.drv_milleage = (drv[0].sum_milleage + logDevice.pre_milleage).toFixed(8); } DriversModels.update({ sum_milleage: logDevice.drv_milleage }, did); } } } logDevices.push(logDevice); const currTrack = await GpsTracksModels.bundleCreate2(logDevice, {}); /** * get lastSpawn desc index 0, where leave_at = 0 * kalo gaada create, dengan isi field enter_at aja, kalo ada didalam zona * kalo ada update, dengan isi field leave_at aja, kalo udah diluar zona */ const lastSpawn = await GpsTracksModels.listSpawnZone({ source: GpsTracksModels.SOURCE_SMARTPHONE, vhc_id: 0, drv_id: did, ord_id: 0, // ord_code: 0, leave_at_d: 0, order_by: 'ORDER BY id DESC', limit: 1, }); if (lastSpawn.length > 0) { let is_leave_zone = 1; for (const insideZone of insideSpawnZone) { if (insideZone.zid === lastSpawn[0].zone_id) is_leave_zone = 0; } if (is_leave_zone === 1) { GpsTracksModels.updt2SpawnZone({ leave_lat: lat, leave_lng: lng, leave_at_d: req_at, leave_at_d_format: moment.unix(req_at).format('YYYY-MM-DD HH:mm:ss'), leave_at_s: now, leave_at_s_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), updt: now, updt_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), }, lastSpawn[0].id); } } else { for (const insideZone of insideSpawnZone) { GpsTracksModels.create2SpawnZone({ device_id: device_id, master_id: Number(currTrack.result.insertId), enter_lat: lat, enter_lng: lng, enter_at_d: req_at, enter_at_d_format: moment.unix(req_at).format('YYYY-MM-DD HH:mm:ss'), enter_at_s: now, enter_at_s_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), zone_id: insideZone.zid, zone_name: insideZone.name, vhc_id: 0, drv_id: did, ord_id: 0, // ord_code: 0, source: GpsTracksModels.SOURCE_SMARTPHONE, crt: now, crt_format: moment.unix(now).format('YYYY-MM-DD HH:mm:ss'), }); } } } apiRes = JSON.parse(JSON.stringify(response[200])); // apiRes.raw = updtToOrders; // apiRes.logDevices = logDevices; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } // OTHERS async notif(req, res) { let apiRes = {} try { const now = moment().unix(); const { did, title, body, type, fcm_token, silent_notif } = req.body; // input validation const input = { did, fcm_token, title, body, type, silent_notif, }; const rulesInput = { did: 'required|integer', fcm_token: 'required|string', title: 'string|max:255', body: 'string', type: 'required|integer', silent_notif: 'boolean', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const optFirebase = { priority: 'normal', android: { priority: 'high', }, click_action: 'FLUTTER_NOTIFICATION_CLICK', }; let content_available = 'false'; if (silent_notif === 'true' || silent_notif === true) { optFirebase.content_available = true; // ios content_available = 'true'; } // const phoneDevice = await DriversModels.getDeviceByDid(did); // if (phoneDevice.length < 1) { // apiRes = JSON.parse(JSON.stringify(response[404])); // apiRes.meta.code += '_1'; // apiRes.meta.message = 'account not found, driver not login'; LibLogReqResApi.log(req, apiRes); // return res.status(200).json(apiRes); // } // if (phoneDevice[0].is_login !== DriversModels.IS_LOGIN) { // apiRes = JSON.parse(JSON.stringify(response[404])); // apiRes.meta.code += '_1'; // apiRes.meta.message = 'account not found, driver not login'; LibLogReqResApi.log(req, apiRes); // return res.status(200).json(apiRes); // } // phoneDevice[0].fcm_token const libFirebase = new LibFirebase(); let notifTitle = title || ''; let notifBody = body || ''; if (silent_notif === 'true' || silent_notif === true) { optFirebase.priority = 'high'; await libFirebase.sendToDevice(fcm_token, { notification: { title: 'Notifikasi Cek Lokasi', // notifTitle, body: 'Harap Dihiraukan', // notifBody, }, data: { title: notifTitle, body: notifBody, type: '' + type, detail_id: '', content_available, } }, optFirebase); } else { await libFirebase.sendToDevice(fcm_token, { notification: { title: notifTitle, body: notifBody, }, data: { title: notifTitle, body: notifBody, type: '' + type, detail_id: '', content_available, } }, optFirebase); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.meta.message = 'success send notification'; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } // DEV ONLY async lastLatLng(req, res) { let apiRes = {} try { const now = moment().unix(); const { did } = req.query; // input validation const input = { did, }; const rulesInput = { did: 'required|integer', }; const isInputValid = new Validator(input, rulesInput); if (isInputValid.fails()) { apiRes = JSON.parse(JSON.stringify(response[422])); apiRes.meta.message += Helper.setErrMsg(': ' + Object.values(isInputValid.errors.all())[0][0]); // get first message LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } const getLastLoc = await GpsTracksModels.getLastLocAndAddrByDid({did, limit: 100}); const formatLastLoc = []; for (const lst of getLastLoc) { formatLastLoc.push({ id: lst.id, did: lst.drv_id, device_id: lst.device_id, latitude: lst.latitude, longitude: lst.longitude, state_text: lst.state_text, city_text: lst.city_text, district_text: lst.district_text, village_text: lst.village_text, streets_text: lst.streets_text, fulladdress: lst.fulladdress, crt_device_unix: lst.crt_d, crt_dvice_raw: lst.crt_d_format, crt_server_unix: lst.crt_s, crt_server_raw: lst.crt_s_format, }); } apiRes = JSON.parse(JSON.stringify(response[200])); apiRes.data = formatLastLoc; LibLogReqResApi.log(req, apiRes); return res.status(200).json(apiRes); } catch (e) { apiRes = JSON.parse(JSON.stringify(response[500])); apiRes.meta.message += Helper.setErrMsg(': ' + e.message); LibLogReqResApi.log(req, apiRes); return res.status(500).json(apiRes); } } } const object = new ServiceDriverController(); module.exports = object;