3442 lines
109 KiB
PHP
3442 lines
109 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use Doctrine\ORM\Query;
|
|
use Doctrine\ORM\QueryBuilder;
|
|
use Doctrine\DBAL\DBALException;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\Dotenv\Dotenv;
|
|
|
|
use CrEOF\Spatial\PHP\Types\Geometry\Point;
|
|
|
|
use App\Ramcar\APIResult;
|
|
use App\Ramcar\JOStatus;
|
|
use App\Ramcar\InvoiceCriteria;
|
|
use App\Ramcar\ServiceType;
|
|
use App\Ramcar\WarrantyClass;
|
|
use App\Ramcar\APIRiderStatus;
|
|
use App\Ramcar\TransactionOrigin;
|
|
use App\Ramcar\TradeInType;
|
|
use App\Ramcar\JOEventType;
|
|
use App\Ramcar\AdvanceOrderSlot;
|
|
|
|
use App\Service\InvoiceGeneratorInterface;
|
|
use App\Service\RisingTideGateway;
|
|
use App\Service\MQTTClient;
|
|
use App\Service\GeofenceTracker;
|
|
use App\Service\RiderTracker;
|
|
use App\Service\MapTools;
|
|
use App\Service\InventoryManager;
|
|
use App\Service\RiderAssignmentHandlerInterface;
|
|
|
|
use App\Entity\MobileSession;
|
|
use App\Entity\Customer;
|
|
use App\Entity\VehicleManufacturer;
|
|
use App\Entity\Vehicle;
|
|
use App\Entity\CustomerVehicle;
|
|
use App\Entity\JobOrder;
|
|
use App\Entity\Promo;
|
|
use App\Entity\Battery;
|
|
use App\Entity\RiderRating;
|
|
use App\Entity\JOEvent;
|
|
use App\Entity\Warranty;
|
|
use App\Entity\Service;
|
|
use App\Entity\Partner;
|
|
use App\Entity\Review;
|
|
use App\Entity\PrivacyPolicy;
|
|
use App\Entity\Hub;
|
|
|
|
use DateTime;
|
|
use DateInterval;
|
|
use Exception;
|
|
|
|
// mobile API
|
|
class APIController extends Controller implements LoggedController
|
|
{
|
|
const RIDER_SLOT_ARRAY_LENGTH = 8;
|
|
|
|
protected $session;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->session = null;
|
|
}
|
|
|
|
protected function debugRequest(Request $req)
|
|
{
|
|
$all = $req->request->all();
|
|
error_log(print_r($all, true));
|
|
}
|
|
|
|
protected function checkMissingParameters(Request $req, $params = [])
|
|
{
|
|
$missing = [];
|
|
|
|
// check if parameters are there
|
|
foreach ($params as $param)
|
|
{
|
|
if ($req->getMethod() == 'GET')
|
|
{
|
|
$check = $req->query->get($param);
|
|
if (empty($check))
|
|
$missing[] = $param;
|
|
}
|
|
else if ($req->getMethod() == 'POST')
|
|
{
|
|
$check = $req->request->get($param);
|
|
if (empty($check))
|
|
$missing[] = $param;
|
|
}
|
|
else
|
|
return $params;
|
|
}
|
|
|
|
return $missing;
|
|
}
|
|
|
|
// TODO: type hint entity manager
|
|
protected function checkAPIKey($em, $api_key)
|
|
{
|
|
// find the api key (session id)
|
|
$session = $em->getRepository(MobileSession::class)->find($api_key);
|
|
if ($session == null)
|
|
return null;
|
|
|
|
return $session;
|
|
}
|
|
|
|
protected function checkParamsAndKey(Request $req, $em, $params)
|
|
{
|
|
// returns APIResult object
|
|
$res = new APIResult();
|
|
|
|
// check for api_key in query string
|
|
$api_key = $req->query->get('api_key');
|
|
if (empty($api_key))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Missing API key');
|
|
return $res;
|
|
}
|
|
|
|
// check missing parameters
|
|
$missing = $this->checkMissingParameters($req, $params);
|
|
if (count($missing) > 0)
|
|
{
|
|
$miss_string = implode(', ', $missing);
|
|
$res->setError(true)
|
|
->setErrorMessage('Missing parameter(s): ' . $miss_string);
|
|
return $res;
|
|
}
|
|
|
|
// check api key
|
|
$sess = $this->checkAPIKey($em, $req->query->get('api_key'));
|
|
if ($sess == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid API Key');
|
|
return $res;
|
|
}
|
|
|
|
// store session
|
|
$this->session = $sess;
|
|
|
|
return $res;
|
|
}
|
|
|
|
public function register(Request $req)
|
|
{
|
|
$res = new APIResult();
|
|
|
|
// confirm parameters
|
|
$required_params = [
|
|
'phone_model',
|
|
'os_type',
|
|
'os_version',
|
|
'phone_id'
|
|
];
|
|
|
|
$missing = $this->checkMissingParameters($req, $required_params);
|
|
if (count($missing) > 0)
|
|
{
|
|
$params = implode(', ', $missing);
|
|
$res->setError(true)
|
|
->setErrorMessage('Missing parameter(s): ' . $params);
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$em = $this->getDoctrine()->getManager();
|
|
|
|
// retry until we get a unique id
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
// instantiate session
|
|
$sess = new MobileSession();
|
|
$sess->setPhoneModel($req->request->get('phone_model'))
|
|
->setOSType($req->request->get('os_type'))
|
|
->setOSVersion($req->request->get('os_version'))
|
|
->setPhoneID($req->request->get('phone_id'));
|
|
|
|
// reopen in case we get an exception
|
|
if (!$em->isOpen())
|
|
{
|
|
$em = $em->create(
|
|
$em->getConnection(),
|
|
$em->getConfiguration()
|
|
);
|
|
}
|
|
|
|
// save
|
|
$em->persist($sess);
|
|
$em->flush();
|
|
}
|
|
catch (DBALException $e)
|
|
{
|
|
error_log($e->getMessage());
|
|
// delay one second and try again
|
|
sleep(1);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// return data
|
|
$data = [
|
|
'session_id' => $sess->getID()
|
|
];
|
|
$res->setData($data);
|
|
|
|
|
|
// response
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
protected function generateConfirmCode()
|
|
{
|
|
return sprintf("%06d", mt_rand(100000, 999999));
|
|
}
|
|
|
|
protected function sendConfirmationCode(RisingTideGateway $rt, $phone_number, $code)
|
|
{
|
|
// send sms to number
|
|
$message = "Your Resq confirmation code is $code.";
|
|
$rt->sendSMS($phone_number, 'MOTOLITE', $message);
|
|
}
|
|
|
|
public function confirmNumber(RisingTideGateway $rt, Request $req)
|
|
{
|
|
// check parameters
|
|
$required_params = [
|
|
'phone_number',
|
|
];
|
|
|
|
// check required parameters and api key
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// phone number
|
|
$phone_number = $req->request->get('phone_number');
|
|
|
|
// get otp_mode from .env
|
|
$dotenv = new Dotenv();
|
|
$dotenv->loadEnv(__DIR__.'/../../.env');
|
|
|
|
$otp_mode = $_ENV['OTP_MODE'];
|
|
|
|
// check for hardcoded phone number for app store testing
|
|
if ($phone_number == '639991112233')
|
|
{
|
|
$code = '123456';
|
|
$this->session->setConfirmCode($code)
|
|
->setPhoneNumber($phone_number);
|
|
$em->flush();
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check if otp_mode is test
|
|
if ($otp_mode == 'test')
|
|
{
|
|
$code = '123456';
|
|
$this->session->setConfirmCode($code)
|
|
->setPhoneNumber($phone_number);
|
|
$em->flush();
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// TODO: spam protection
|
|
|
|
// TODO: validate phone number
|
|
|
|
// generate code and save
|
|
$code = $this->generateConfirmCode();
|
|
$this->session->setConfirmCode($code)
|
|
->setPhoneNumber($phone_number);
|
|
$em->flush();
|
|
|
|
if ($otp_mode != 'test')
|
|
{
|
|
// send sms to number
|
|
$this->sendConfirmationCode($rt, $phone_number, $code);
|
|
}
|
|
|
|
// response
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// TODO: find session customer by phone number
|
|
protected function findNumberSession($number)
|
|
{
|
|
$em = $this->getDoctrine()->getManager();
|
|
$query = $em->getRepository(MobileSession::class)->createQueryBuilder('s')
|
|
->where('s.phone_number = :number')
|
|
->andWhere('s.customer is not null')
|
|
->andWhere('s.confirm_flag = 1')
|
|
->setParameter('number', $number)
|
|
->setMaxResults(1)
|
|
->getQuery();
|
|
|
|
// we just need one
|
|
$res = $query->getOneOrNullResult();
|
|
|
|
return $res;
|
|
}
|
|
|
|
public function validateCode(Request $req)
|
|
{
|
|
// check parameters
|
|
$required_params = [
|
|
'code',
|
|
];
|
|
|
|
// check required parameters and api key
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// code is wrong
|
|
$code = $req->request->get('code');
|
|
if ($this->session->getConfirmCode() != $code)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Wrong confirm code');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// set confirm date
|
|
$date = new DateTime();
|
|
$this->session->setDateConfirmed($date)
|
|
->setConfirmed();
|
|
|
|
|
|
// TODO: check if we have the number registered before and merge
|
|
$dupe_sess = $this->findNumberSession($this->session->getPhoneNumber());
|
|
if ($dupe_sess != null)
|
|
{
|
|
$dupe_cust = $dupe_sess->getCustomer();
|
|
$this->session->setCustomer($dupe_cust);
|
|
}
|
|
|
|
// TODO: check if mobile matches mobile of customer
|
|
$customer = $this->findCustomerByNumber($this->session->getPhoneNumber());
|
|
if ($customer != null)
|
|
{
|
|
// TODO: if there is a dupe_sess, do we need to check if
|
|
// dupe_cust is the same as the customer we found?
|
|
$this->session->setCustomer($customer);
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
// response
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getInfo(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// if no customer found
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$data = [
|
|
'first_name' => '',
|
|
'last_name' => '',
|
|
'priv_third_party' => (bool) false,
|
|
'priv_promo' => (bool) false,
|
|
];
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// send back customer details
|
|
$data = [
|
|
'first_name' => $cust->getFirstName(),
|
|
'last_name' => $cust->getLastName(),
|
|
'priv_third_party' => (bool) $cust->getPrivacyThirdParty(),
|
|
'priv_promo' => (bool) $cust->getPrivacyPromo(),
|
|
];
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function updateInfo(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [
|
|
'first_name',
|
|
'last_name',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// create new customer if it's not there
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$cust = new Customer();
|
|
$em->persist($cust);
|
|
|
|
$this->session->setCustomer($cust);
|
|
}
|
|
|
|
$cust->setFirstName($req->request->get('first_name'))
|
|
->setLastName($req->request->get('last_name'))
|
|
->setConfirmed($this->session->isConfirmed());
|
|
|
|
// update mobile phone of customer
|
|
$cust->setPhoneMobile(substr($this->session->getPhoneNumber(), 2));
|
|
|
|
// get privacy policy for mobile
|
|
$dotenv = new Dotenv();
|
|
$dotenv->loadEnv(__DIR__.'/../../.env');
|
|
|
|
$policy_mobile_id = $_ENV['POLICY_MOBILE'];
|
|
|
|
$mobile_policy = $em->getRepository(PrivacyPolicy::class)->find($policy_mobile_id);
|
|
|
|
// set policy id
|
|
if ($mobile_policy != null)
|
|
{
|
|
$cust->setPrivacyPolicyMobile($mobile_policy);
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getStatus(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// set data
|
|
$data = [];
|
|
if ($this->session->isConfirmed())
|
|
$data['status'] = 'confirmed';
|
|
else
|
|
$data['status'] = 'unconfirmed';
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function listVehicleManufacturers(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get manufacturer list
|
|
$mfgs = $em->getRepository(VehicleManufacturer::class)->findBy(['flag_mobile' => true], ['name' => 'asc']);
|
|
$mfg_list = [];
|
|
foreach ($mfgs as $mfg)
|
|
{
|
|
$mfg_list[] = [
|
|
'id' => $mfg->getID(),
|
|
'name' => $mfg->getName(),
|
|
];
|
|
}
|
|
|
|
$data = [
|
|
'manufacturers' => $mfg_list
|
|
];
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function listVehicleMakes(Request $req, $mfg_id)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get manufacturer
|
|
$mfg = $em->getRepository(VehicleManufacturer::class)->find($mfg_id);
|
|
if ($mfg == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid vehicle manufacturer id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get makes
|
|
$vehicles = $em->getRepository(Vehicle::class)->findBy(
|
|
[
|
|
'flag_mobile' => true,
|
|
'manufacturer' => $mfg_id,
|
|
],
|
|
['make' => 'asc']
|
|
);
|
|
// $vehicles = $mfg->getVehicles();
|
|
$vlist = [];
|
|
foreach ($vehicles as $v)
|
|
{
|
|
$vlist[] = [
|
|
'id' => $v->getID(),
|
|
'make' => trim($v->getMake() . ' ' . $v->getModelYearFormatted(false)),
|
|
// 'make' => $v->getMake() . ' ' . $v->getModelYearFrom() . '-' . $v->getModelYearTo(),
|
|
];
|
|
}
|
|
|
|
$data = [
|
|
'manufacturer' => [
|
|
'id' => $mfg->getID(),
|
|
'name' => $mfg->getName(),
|
|
],
|
|
'makes' => $vlist,
|
|
];
|
|
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
protected function checkVehicleRequirements(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [
|
|
'make_id',
|
|
'name',
|
|
'plate_num',
|
|
'model_year',
|
|
'color',
|
|
'condition',
|
|
'fuel_type',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res;
|
|
|
|
// TODO: check valid plate number
|
|
// TODO: check valid fuel type (gas / diesel)
|
|
// TODO: check current battery id
|
|
// TODO: check condition (brand new / second-hand)
|
|
// TODO: check is_motolite and is_active (1 or 0)
|
|
// TODO: check warranty expiration date (YYYYMMDD)
|
|
// TODO: check model year coverage if it fits in between
|
|
|
|
return $res;
|
|
}
|
|
|
|
protected function setCustomerVehicleObject(Request $req, APIResult $res, CustomerVehicle $cv)
|
|
{
|
|
$em = $this->getDoctrine()->getManager();
|
|
|
|
// check customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res;
|
|
}
|
|
|
|
// get vehicle
|
|
$vehicle = $em->getRepository(Vehicle::class)->find($req->request->get('make_id'));
|
|
if ($vehicle == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid vehicle make id');
|
|
return $res;
|
|
}
|
|
|
|
$cv->setCustomer($cust)
|
|
->setVehicle($vehicle)
|
|
->setName($req->request->get('name'))
|
|
->setPlateNumber($req->request->get('plate_num'))
|
|
->setModelYear($req->request->get('model_year'))
|
|
->setColor($req->request->get('color'))
|
|
->setFuelType($req->request->get('fuel_type'))
|
|
->setStatusCondition($req->request->get('condition'));
|
|
|
|
// set warranty code and expiration
|
|
// TODO: check warranty requirements
|
|
if (!empty($req->request->get('wty_code')))
|
|
$cv->setWarrantyCode($req->request->get('wty_code'));
|
|
if (!empty($req->request->get('wty_expire')))
|
|
$cv->setWarrantyExpiration(new DateTime($req->request->get('wty_expire')));
|
|
|
|
// TODO: get current battery
|
|
|
|
// is motolite
|
|
if ($req->request->get('is_motolite') == 0)
|
|
$cv->setHasMotoliteBattery(false);
|
|
else
|
|
$cv->setHasMotoliteBattery(true);
|
|
|
|
// is active
|
|
if ($req->request->get('is_active') == 0)
|
|
$cv->setActive(false);
|
|
else
|
|
$cv->setActive(true);
|
|
|
|
// save
|
|
$em->persist($cv);
|
|
$em->flush();
|
|
|
|
// data
|
|
$data = [
|
|
'cv_id' => $cv->getID()
|
|
];
|
|
$res->setData($data);
|
|
|
|
return $res;
|
|
}
|
|
|
|
public function addVehicle(Request $req)
|
|
{
|
|
// check requirements
|
|
$res = $this->checkVehicleRequirements($req);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// customer vehicle
|
|
$cv = new CustomerVehicle();
|
|
|
|
$res = $this->setCustomerVehicleObject($req, $res, $cv);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function updateVehicle(Request $req, $id)
|
|
{
|
|
// check requirements
|
|
$res = $this->checkVehicleRequirements($req);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get customer vehicle
|
|
$em = $this->getDoctrine()->getManager();
|
|
$cv = $em->getRepository(CustomerVehicle::class)->find($id);
|
|
|
|
// check if it exists
|
|
if ($cv == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Vehicle does not exist');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check if it's owned by customer
|
|
if ($cv->getCustomer()->getID() != $this->session->getCustomer()->getID())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid vehicle');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$res = $this->setCustomerVehicleObject($req, $res, $cv);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
protected function getBatteryImageURL($req, $batt)
|
|
{
|
|
// TODO: workaround for now, we get static image of battery based on model name
|
|
$filename = trim(strtolower($batt->getModel()->getName())) . '_mobile.jpg';
|
|
$file_path = $req->getSchemeAndHttpHost() . $this->generateUrl('static_battery_image') . '/' . $filename;
|
|
|
|
return $file_path;
|
|
}
|
|
|
|
public function listVehicles(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// vehicles
|
|
$cv_list = [];
|
|
$cvs = $cust->getVehicles();
|
|
foreach ($cvs as $cv)
|
|
{
|
|
$battery_id = null;
|
|
if ($cv->getCurrentBattery() != null)
|
|
$battery_id = $cv->getCurrentBattery()->getID();
|
|
|
|
$wty_ex = null;
|
|
if ($cv->getWarrantyExpiration() != null)
|
|
$wty_ex = $cv->getWarrantyExpiration()->format('Y-m-d');
|
|
|
|
$warranty = $this->findWarranty($cv->getPlateNumber());
|
|
|
|
$cv_name = '';
|
|
if ($cv->getName() != null)
|
|
$cv_name = $cv->getName();
|
|
|
|
$cv_list[] = [
|
|
'cv_id' => $cv->getID(),
|
|
'mfg_id' => $cv->getVehicle()->getManufacturer()->getID(),
|
|
'make_id' => $cv->getVehicle()->getID(),
|
|
'name' => $cv_name,
|
|
'plate_num' => $cv->getPlateNumber(),
|
|
'model_year' => $cv->getModelYear(),
|
|
'color' => $cv->getColor(),
|
|
'condition' => $cv->getStatusCondition(),
|
|
'fuel_type' => $cv->getFuelType(),
|
|
'wty_code' => $cv->getWarrantyCode(),
|
|
'wty_expire' => $wty_ex,
|
|
'curr_batt_id' => $battery_id,
|
|
'is_motolite' => $cv->hasMotoliteBattery() ? 1 : 0,
|
|
'is_active' => $cv->isActive() ? 1 : 0,
|
|
'warranty' => $warranty,
|
|
];
|
|
}
|
|
|
|
// data
|
|
$data = [
|
|
'vehicles' => $cv_list
|
|
];
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function listPromos(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getCompatibleBatteries(Request $req, $vid)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get vehicle
|
|
$vehicle = $em->getRepository(Vehicle::class)->find($vid);
|
|
if ($vehicle == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid vehicle');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// batteries
|
|
$batt_list = [];
|
|
$batts = $vehicle->getBatteries();
|
|
foreach ($batts as $batt)
|
|
{
|
|
// TODO: Add warranty_tnv to battery information
|
|
$batt_list[] = [
|
|
'id' => $batt->getID(),
|
|
'mfg_id' => $batt->getManufacturer()->getID(),
|
|
'mfg_name' => $batt->getManufacturer()->getName(),
|
|
'model_id' => $batt->getModel()->getID(),
|
|
'model_name' => $batt->getModel()->getName(),
|
|
'size_id' => $batt->getSize()->getID(),
|
|
'size_name' => $batt->getSize()->getName(),
|
|
'price' => $batt->getSellingPrice(),
|
|
'wty_private' => $batt->getWarrantyPrivate(),
|
|
'wty_commercial' => $batt->getWarrantyCommercial(),
|
|
'image_url' => $this->getBatteryImageURL($req, $batt),
|
|
];
|
|
}
|
|
|
|
// data
|
|
$data = [
|
|
'vehicle' => [
|
|
'id' => $vehicle->getID(),
|
|
'mfg_id' => $vehicle->getManufacturer()->getID(),
|
|
'mfg_name' => $vehicle->getManufacturer()->getName(),
|
|
'make' => $vehicle->getMake(),
|
|
'model_year_from' => $vehicle->getModelYearFrom(),
|
|
'model_year_to' => $vehicle->getModelYearTo(),
|
|
],
|
|
'batteries' => $batt_list,
|
|
];
|
|
$res->setData($data);
|
|
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function requestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo,
|
|
MapTools $map_tools, InventoryManager $im, MQTTClient $mclient,
|
|
RiderAssignmentHandlerInterface $rah)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [
|
|
'service_type',
|
|
'cv_id',
|
|
// 'batt_id',
|
|
'trade_in',
|
|
'long',
|
|
'lat',
|
|
'warranty',
|
|
'mode_of_payment',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// trade in type
|
|
$trade_in = $req->request->get('trade_in');
|
|
|
|
// address
|
|
$address = $req->request->get('delivery_address', 'Set by mobile application');
|
|
|
|
// instructions
|
|
$instructions = $req->request->get('delivery_instructions', '');
|
|
|
|
// longitude and latitude
|
|
$long = $req->request->get('long');
|
|
$lat = $req->request->get('lat');
|
|
|
|
// geofence
|
|
$is_covered = $geo->isCovered($long, $lat);
|
|
if (!$is_covered)
|
|
{
|
|
// TODO: put geofence error message in config file somewhere
|
|
$res->setError(true)
|
|
->setErrorMessage('Oops! Our service is limited to some areas in Metro Manila, Laguna, and Baguio only. We will update you as soon as we are able to cover your area');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$jo = new JobOrder();
|
|
$jo->setSource(TransactionOrigin::MOBILE_APP)
|
|
->setStatus(JOStatus::PENDING)
|
|
->setDeliveryInstructions('')
|
|
->setTier1Notes('')
|
|
->setTier2Notes('')
|
|
->setDeliveryAddress($address)
|
|
->setTradeInType($trade_in)
|
|
->setDeliveryInstructions($instructions)
|
|
// TODO: error check for valid mode of payment
|
|
->setModeOfPayment($req->request->get('mode_of_payment'));
|
|
|
|
// customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$jo->setCustomer($cust);
|
|
|
|
// validate service type
|
|
$stype = $req->request->get('service_type');
|
|
if (!ServiceType::validate($stype))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid service type');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$jo->setServiceType($stype);
|
|
|
|
// validate warranty
|
|
$warr = $req->request->get('warranty');
|
|
if (!WarrantyClass::validate($warr))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid warranty class');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$jo->setWarrantyClass($warr);
|
|
|
|
// set coordinates
|
|
$point = new Point($long, $lat);
|
|
$jo->setCoordinates($point);
|
|
|
|
// make invoice criteria
|
|
$icrit = new InvoiceCriteria();
|
|
$icrit->setServiceType($stype);
|
|
|
|
// check promo
|
|
$promo_id = $req->request->get('promo_id');
|
|
if (!empty($promo_id))
|
|
{
|
|
$promo = $em->getRepository(Promo::class)->find($promo_id);
|
|
if ($promo == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid promo id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// put in criteria
|
|
$icrit->addPromo($promo);
|
|
}
|
|
|
|
// check customer vehicle
|
|
$cv = $em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id'));
|
|
if ($cv == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid customer vehicle id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$icrit->setCustomerVehicle($cv);
|
|
$jo->setCustomerVehicle($cv);
|
|
|
|
// check if customer owns vehicle
|
|
if ($cust->getID() != $cv->getCustomer()->getID())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Customer does not own vehicle');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check battery
|
|
$batt_id = $req->request->get('batt_id');
|
|
if ($batt_id != null)
|
|
{
|
|
$batt = $em->getRepository(Battery::class)->find($batt_id);
|
|
if ($batt == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid battery id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
}
|
|
else
|
|
$batt = null;
|
|
|
|
/*
|
|
// put battery in criteria
|
|
$icrit->addBattery($batt);
|
|
*/
|
|
|
|
// check trade-in
|
|
// only allow motolite, other, none
|
|
switch ($trade_in)
|
|
{
|
|
case TradeInType::MOTOLITE:
|
|
case TradeInType::OTHER:
|
|
break;
|
|
|
|
default:
|
|
$trade_in = '';
|
|
break;
|
|
}
|
|
|
|
$icrit->addEntry($batt, $trade_in, 1);
|
|
|
|
// send to invoice generator
|
|
$invoice = $ic->generateInvoice($icrit);
|
|
$jo->setInvoice($invoice);
|
|
|
|
// assign hub and rider
|
|
if (($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) ||
|
|
($jo->getServicetype() == ServiceType::BATTERY_REPLACEMENT_WARRANTY))
|
|
{
|
|
// get nearest hub
|
|
// $nearest_hub = $this->findNearestHubWithInventory($jo, $batt, $em, $map_tools, $im);
|
|
$nearest_hub = $this->findNearestHub($jo, $em, $map_tools);
|
|
}
|
|
else
|
|
{
|
|
$nearest_hub = $this->findNearestHub($jo, $em, $map_tools);
|
|
}
|
|
|
|
if (!empty($nearest_hub))
|
|
{
|
|
//error_log('found nearest hub ' . $nearest_hub->getID());
|
|
// assign rider
|
|
$available_riders = $nearest_hub->getAvailableRiders();
|
|
if (count($available_riders) > 0)
|
|
{
|
|
$assigned_rider = null;
|
|
if (count($available_riders) > 1)
|
|
{
|
|
// TODO: the setting of riders into an array
|
|
// will no longer be necessary when the contents
|
|
// of randomizeRider changes
|
|
$riders = [];
|
|
foreach ($available_riders as $rider)
|
|
{
|
|
$riders[] = $rider;
|
|
}
|
|
|
|
$assigned_rider = $this->randomizeRider($riders);
|
|
}
|
|
else
|
|
$assigned_rider = $available_riders[0];
|
|
|
|
//error_log('found rider ' . $assigned_rider->getID());
|
|
$jo->setHub($nearest_hub);
|
|
$jo->setRider($assigned_rider);
|
|
$jo->setStatus(JOStatus::ASSIGNED);
|
|
|
|
$assigned_rider->setAvailable(false);
|
|
}
|
|
}
|
|
|
|
$em->persist($jo);
|
|
$em->persist($invoice);
|
|
|
|
// add event log for JO
|
|
$event = new JOEvent();
|
|
$event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::CREATE)
|
|
->setJobOrder($jo);
|
|
$em->persist($event);
|
|
|
|
// check JO status
|
|
if ($jo->getStatus() == JOStatus::ASSIGNED)
|
|
{
|
|
// add event logs for hub and rider assignments
|
|
$hub_assign_event = new JOEvent();
|
|
$hub_assign_event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::HUB_ASSIGN)
|
|
->setJobOrder($jo);
|
|
|
|
$em->persist($hub_assign_event);
|
|
|
|
$rider_assign_event = new JOEvent();
|
|
$rider_assign_event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::RIDER_ASSIGN)
|
|
->setJobOrder($jo);
|
|
|
|
$em->persist($rider_assign_event);
|
|
|
|
// user mqtt event
|
|
$payload = [
|
|
'event' => 'outlet_assign'
|
|
];
|
|
$mclient->sendEvent($jo, $payload);
|
|
|
|
$rah->assignJobOrder($jo, $jo->getRider());
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
// make invoice json data
|
|
$invoice_data = [
|
|
'total_price' => $invoice->getTotalPrice(),
|
|
'vat_ex_price' => $invoice->getVATExclusivePrice(),
|
|
'vat' => $invoice->getVAT(),
|
|
'discount' => $invoice->getDiscount(),
|
|
'trade_in' => $invoice->getTradeIn(),
|
|
];
|
|
$items = $invoice->getItems();
|
|
$items_data = [];
|
|
foreach ($items as $item)
|
|
{
|
|
$items_data[] = [
|
|
'title' => $item->getTitle(),
|
|
'qty' => $item->getQuantity() + 0,
|
|
'price' => $item->getPrice() + 0.0,
|
|
];
|
|
}
|
|
$invoice_data['items'] = $items_data;
|
|
|
|
// make job order data
|
|
$data = [
|
|
'jo_id' => $jo->getID(),
|
|
'invoice' => $invoice_data
|
|
];
|
|
|
|
// set data
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getEstimate(Request $req, InvoiceGeneratorInterface $ic)
|
|
{
|
|
// $this->debugRequest($req);
|
|
|
|
// check required parameters and api key
|
|
$required_params = [
|
|
'service_type',
|
|
'cv_id',
|
|
// 'batt_id',
|
|
'trade_in',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// make invoice criteria
|
|
$icrit = new InvoiceCriteria();
|
|
$icrit->setServiceType($req->request->get('service_type'));
|
|
|
|
// check promo
|
|
$promo_id = $req->request->get('promo_id');
|
|
if (!empty($promo_id))
|
|
{
|
|
$promo = $em->getRepository(Promo::class)->find($promo_id);
|
|
if ($promo == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid promo id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// put in criteria
|
|
$icrit->addPromo($promo);
|
|
}
|
|
|
|
// check customer vehicle
|
|
$cv = $em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id'));
|
|
if ($cv == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid customer vehicle id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$icrit->setCustomerVehicle($cv);
|
|
|
|
// check if customer owns vehicle
|
|
if ($cust->getID() != $cv->getCustomer()->getID())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Customer does not own vehicle');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check battery
|
|
$batt_id = $req->request->get('batt_id');
|
|
if ($batt_id != null)
|
|
{
|
|
$batt = $em->getRepository(Battery::class)->find($batt_id);
|
|
if ($batt == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid battery id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
}
|
|
else
|
|
$batt = null;
|
|
|
|
/*
|
|
// put battery in criteria
|
|
$icrit->addBattery($batt);
|
|
*/
|
|
|
|
// check trade-in
|
|
// only allow motolite, other, none
|
|
$trade_in = $req->request->get('trade_in');
|
|
switch ($trade_in)
|
|
{
|
|
case TradeInType::MOTOLITE:
|
|
case TradeInType::OTHER:
|
|
break;
|
|
|
|
default:
|
|
$trade_in = '';
|
|
break;
|
|
}
|
|
|
|
$icrit->addEntry($batt, $trade_in, 1);
|
|
|
|
// send to invoice generator
|
|
$invoice = $ic->generateInvoice($icrit);
|
|
|
|
// make invoice json data
|
|
$data = [
|
|
'total_price' => (float) $invoice->getTotalPrice(),
|
|
'vat_ex_price' => (float) $invoice->getVATExclusivePrice(),
|
|
'vat' => (float) $invoice->getVAT(),
|
|
'discount' => (float) $invoice->getDiscount(),
|
|
'trade_in' => (float) $invoice->getTradeIn(),
|
|
];
|
|
$items = $invoice->getItems();
|
|
$items_data = [];
|
|
foreach ($items as $item)
|
|
{
|
|
$my_data = [
|
|
'title' => $item->getTitle(),
|
|
'qty' => (int) $item->getQuantity() + 0,
|
|
'price' => (float) $item->getPrice() + 0.0,
|
|
];
|
|
|
|
$item_batt = $item->getBattery();
|
|
if ($item_batt != null)
|
|
{
|
|
$my_data['image_url'] = $this->getBatteryImageURL($req, $item_batt);
|
|
}
|
|
|
|
$items_data[] = $my_data;
|
|
}
|
|
|
|
$data['items'] = $items_data;
|
|
|
|
// error_log(print_r($data, true));
|
|
|
|
// set data
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getRiderStatus(Request $req, RiderTracker $rt)
|
|
{
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check if we have an ongoing job order
|
|
/*
|
|
$ongoing_jos = $em->getRepository(JobOrder::class)->findBy([
|
|
'customer' => $cust,
|
|
'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS],
|
|
]);
|
|
*/
|
|
$ongoing_jos = $this->getOngoingJobOrders($cust);
|
|
|
|
// $res->setData(['here' => count($ongoing_jos)]);
|
|
// return $res->getReturnResponse();
|
|
if (count($ongoing_jos) <= 0)
|
|
{
|
|
try
|
|
{
|
|
// check if the latest fulfilled jo they have needs rider rating
|
|
$query = $em->createQuery('select jo from App\Entity\JobOrder jo where jo.customer = :cust and jo.status = :status order by jo.date_fulfill desc');
|
|
$fulfill_jo = $query->setParameters([
|
|
'cust' => $cust,
|
|
'status' => JOStatus::FULFILLED,
|
|
])
|
|
->setMaxResults(1)
|
|
->getSingleResult();
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
// no pending
|
|
$res->setData([
|
|
'status' => APIRiderStatus::NO_PENDING_JO
|
|
]);
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// we got a recently fulfilled job order
|
|
if ($fulfill_jo)
|
|
{
|
|
// check if the rider has been rated
|
|
if (!$fulfill_jo->hasRiderRating())
|
|
{
|
|
$dest = $fulfill_jo->getCoordinates();
|
|
|
|
$data = [
|
|
'jo_id' => $fulfill_jo->getID(),
|
|
'service_type' => $fulfill_jo->getServiceType(),
|
|
'destination' => [
|
|
'long' => $dest->getLongitude(),
|
|
'lat' => $dest->getLatitude(),
|
|
],
|
|
'delivery_address' => $fulfill_jo->getDeliveryAddress(),
|
|
'delivery_instructions' => $fulfill_jo->getDeliveryInstructions(),
|
|
];
|
|
|
|
$rider = $fulfill_jo->getRider();
|
|
|
|
// default image url
|
|
$url_prefix = $req->getSchemeAndHttpHost();
|
|
$image_url = $url_prefix . '/assets/images/user.gif';
|
|
if ($rider->getImageFile() != null)
|
|
$image_url = $url_prefix . '/uploads/' . $rider->getImageFile();
|
|
|
|
$data['status'] = APIRiderStatus::RIDER_RATING;
|
|
// default rider location to hub
|
|
$data['rider'] = [
|
|
'id' => $rider->getID(),
|
|
'name' => $rider->getFullName(),
|
|
'plate_num' => $rider->getPlateNumber(),
|
|
'contact_num' => $rider->getContactNumber(),
|
|
'image_url' => $image_url,
|
|
];
|
|
$res->setData($data);
|
|
return $res->getReturnResponse();
|
|
}
|
|
}
|
|
|
|
|
|
// no pending
|
|
$res->setData([
|
|
'status' => APIRiderStatus::NO_PENDING_JO
|
|
]);
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get first jo that's pending
|
|
$jo = $ongoing_jos[0];
|
|
$dest = $jo->getCoordinates();
|
|
|
|
$data = [
|
|
'jo_id' => $jo->getID(),
|
|
'service_type' => $jo->getServiceType(),
|
|
'destination' => [
|
|
'long' => $dest->getLongitude(),
|
|
'lat' => $dest->getLatitude(),
|
|
],
|
|
'delivery_address' => $jo->getDeliveryAddress(),
|
|
'delivery_instructions' => $jo->getDeliveryInstructions(),
|
|
];
|
|
|
|
switch ($jo->getStatus())
|
|
{
|
|
case JOStatus::PENDING:
|
|
$data['status'] = APIRiderStatus::OUTLET_ASSIGN;
|
|
$res->setData($data);
|
|
return $res->getReturnResponse();
|
|
case JOStatus::RIDER_ASSIGN:
|
|
$data['status'] = APIRiderStatus::RIDER_ASSIGN;
|
|
$res->setData($data);
|
|
return $res->getReturnResponse();
|
|
case JOStatus::ASSIGNED:
|
|
case JOStatus::IN_TRANSIT:
|
|
case JOStatus::IN_PROGRESS:
|
|
$rider = $jo->getRider();
|
|
// get rider coordinates from redis
|
|
$coord = $rt->getRiderLocation($rider->getID());
|
|
|
|
// default image url
|
|
$url_prefix = $req->getSchemeAndHttpHost();
|
|
$image_url = $url_prefix . '/assets/images/user.gif';
|
|
if ($rider->getImageFile() != null)
|
|
$image_url = $url_prefix . '/uploads/' . $rider->getImageFile();
|
|
|
|
$data['status'] = APIRiderStatus::RIDER_PICK_UP;
|
|
// TODO: fix this to actual location of rider
|
|
// default rider location to hub
|
|
$data['rider'] = [
|
|
'id' => $rider->getID(),
|
|
'name' => $rider->getFullName(),
|
|
'plate_num' => $rider->getPlateNumber(),
|
|
'contact_num' => $rider->getContactNumber(),
|
|
'image_url' => $image_url,
|
|
'location' => [
|
|
'long' => $coord->getLongitude(),
|
|
'lat' => $coord->getLatitude()
|
|
]
|
|
];
|
|
$res->setData($data);
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
|
|
|
|
$res->setData($data);
|
|
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
protected function getOngoingJobOrders($cust)
|
|
{
|
|
$em = $this->getDoctrine()->getManager();
|
|
$ongoing_jos = $em->getRepository(JobOrder::class)->findBy([
|
|
'customer' => $cust,
|
|
'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS],
|
|
]);
|
|
|
|
return $ongoing_jos;
|
|
}
|
|
|
|
public function getOngoing(Request $req)
|
|
{
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
/*
|
|
// check if we have an ongoing job order
|
|
$ongoing_jos = $em->getRepository(JobOrder::class)->findBy([
|
|
'customer' => $cust,
|
|
'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS],
|
|
]);
|
|
*/
|
|
$ongoing_jos = $this->getOngoingJobOrders($cust);
|
|
|
|
// initialize data
|
|
$data = [];
|
|
|
|
// no ongoing
|
|
if (count($ongoing_jos) <= 0)
|
|
{
|
|
$data = [
|
|
'has_ongoing' => false,
|
|
];
|
|
}
|
|
else
|
|
{
|
|
$data = [
|
|
'has_ongoing' => true,
|
|
];
|
|
}
|
|
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function addRiderRating(Request $req)
|
|
{
|
|
$required_params = [
|
|
'jo_id',
|
|
'rating',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get job order
|
|
$jo_id = $req->request->get('jo_id');
|
|
$jo = $em->getRepository(JobOrder::class)->find($jo_id);
|
|
if ($jo == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No job order found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get rider
|
|
$rider = $jo->getRider();
|
|
if ($rider == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No rider found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check that the customer owns the job order
|
|
$jo_cust = $jo->getCustomer();
|
|
if ($jo_cust->getID() != $cust->getID())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Job order was not initiated by customer');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// TODO: check job order status, if it's complete
|
|
|
|
// add rider rating
|
|
$rating_num = $req->request->get('rating', -1);
|
|
|
|
// if rating is -1
|
|
if ($rating_num == -1)
|
|
{
|
|
$jo->setHasRiderRating();
|
|
$em->flush();
|
|
|
|
$res->setData([]);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
|
|
$rating = new RiderRating();
|
|
$rating->setRider($rider)
|
|
->setCustomer($cust)
|
|
->setJobOrder($jo)
|
|
->setRating($rating_num);
|
|
|
|
// rider rating comment
|
|
$comment = $req->request->get('comment');
|
|
if (!empty($comment))
|
|
$rating->setComment($comment);
|
|
|
|
// mark jo as rider rated already
|
|
$jo->setHasRiderRating();
|
|
|
|
$em->persist($rating);
|
|
$em->flush();
|
|
|
|
// TODO: set average rating in rider entity
|
|
|
|
$res->setData([]);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getJOInvoice(Request $req)
|
|
{
|
|
$required_params = [
|
|
'jo_id',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get job order
|
|
$jo_id = $req->query->get('jo_id');
|
|
$jo = $em->getRepository(JobOrder::class)->find($jo_id);
|
|
if ($jo == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No job order found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check that the customer owns the job order
|
|
$jo_cust = $jo->getCustomer();
|
|
if ($jo_cust->getID() != $cust->getID())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Job order was not initiated by customer');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$invoice = $jo->getInvoice();
|
|
|
|
// make invoice json data
|
|
$data = [
|
|
'total_price' => (float) $invoice->getTotalPrice(),
|
|
'vat_ex_price' => (float) $invoice->getVATExclusivePrice(),
|
|
'vat' => (float) $invoice->getVAT(),
|
|
'discount' => (float) $invoice->getDiscount(),
|
|
'trade_in' => (float) $invoice->getTradeIn(),
|
|
];
|
|
$items = $invoice->getItems();
|
|
$items_data = [];
|
|
foreach ($items as $item)
|
|
{
|
|
$my_data = [
|
|
'title' => $item->getTitle(),
|
|
'qty' => (int) $item->getQuantity() + 0,
|
|
'price' => (float) $item->getPrice() + 0.0,
|
|
];
|
|
|
|
$item_batt = $item->getBattery();
|
|
if ($item_batt != null)
|
|
{
|
|
$my_data['image_url'] = $this->getBatteryImageURL($req, $item_batt);
|
|
}
|
|
|
|
$items_data[] = $my_data;
|
|
}
|
|
|
|
$data['items'] = $items_data;
|
|
|
|
// set data
|
|
$res->setData($data);
|
|
|
|
|
|
/*
|
|
// invoice items
|
|
$inv_items = [];
|
|
foreach ($inv->getItems() as $item)
|
|
{
|
|
$item_batt = $item->getBattery();
|
|
if ($item_batt == null)
|
|
$batt_id = null;
|
|
else
|
|
$batt_id = $item_batt->getID();
|
|
|
|
$inv_items[] = [
|
|
'id' => $item->getID(),
|
|
'title' => $item->getTitle(),
|
|
'qty' => $item->getQuantity(),
|
|
'price' => $item->getPrice(),
|
|
'batt_id' => $batt_id,
|
|
];
|
|
}
|
|
|
|
$data = [
|
|
'invoice' => [
|
|
'discount' => $inv->getDiscount(),
|
|
'trade_in' => $inv->getTradeIn(),
|
|
'total_price' => $inv->getTotalPrice(),
|
|
'vat' => $inv->getVat(),
|
|
'items' => $inv_items,
|
|
],
|
|
];
|
|
|
|
|
|
$res->setData($data);
|
|
*/
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function cancelJobOrder(Request $req, MQTTClient $mclient)
|
|
{
|
|
$required_params = [
|
|
'jo_id',
|
|
'reason'
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get job order
|
|
$jo_id = $req->request->get('jo_id');
|
|
$jo = $em->getRepository(JobOrder::class)->find($jo_id);
|
|
if ($jo == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No job order found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check that the customer owns the job order
|
|
$jo_cust = $jo->getCustomer();
|
|
if ($jo_cust->getID() != $cust->getID())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Job order was not initiated by customer');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// TODO: check job order status, if it's cancellable
|
|
$cancel_reason = $req->request->get('reason');
|
|
|
|
$jo->cancel($cancel_reason);
|
|
|
|
// add event log
|
|
$event = new JOEvent();
|
|
$event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::CANCEL)
|
|
->setJobOrder($jo);
|
|
$em->persist($event);
|
|
|
|
$em->flush();
|
|
|
|
// send mobile app event
|
|
$payload = [
|
|
'event' => 'cancelled',
|
|
'reason' => $cancel_reason,
|
|
'jo_id' => $jo->getID(),
|
|
];
|
|
// $mclient->sendEvent($jo, $payload);
|
|
$mclient->sendRiderEvent($jo, $payload);
|
|
|
|
|
|
$res->setData([]);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getJOHistory(Request $req)
|
|
{
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, []);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get job orders
|
|
$all_jo_data = [];
|
|
$jos = $cust->getJobOrders();
|
|
foreach ($jos as $jo)
|
|
{
|
|
$status = $jo->getStatus();
|
|
|
|
$jo_data = [
|
|
'id' => $jo->getID(),
|
|
'date_create' => $jo->getDateCreate()->format('M d, Y'),
|
|
'service_type' => $jo->getServiceType(),
|
|
'status' => $status,
|
|
];
|
|
|
|
// customer vehicle and warranty
|
|
$cv = $jo->getCustomerVehicle();
|
|
|
|
// get latest warranty using plate number
|
|
$warranty = $this->findWarranty($cv->getPlateNumber());
|
|
|
|
$jo_data['customer_vehicle'] = [
|
|
'id' => $cv->getID(),
|
|
'plate_number' => $cv->getPlateNumber(),
|
|
'warranty' => $warranty,
|
|
];
|
|
|
|
// rider
|
|
$rider = $jo->getRider();
|
|
if ($rider != null)
|
|
{
|
|
$jo_data['rider'] = $rider->getFullName();
|
|
}
|
|
|
|
// invoice items
|
|
$items = [];
|
|
$jo_items = $jo->getInvoice()->getItems();
|
|
foreach ($jo_items as $item)
|
|
{
|
|
$items[] = [
|
|
'id' => $item->getID(),
|
|
'title' => $item->getTitle(),
|
|
'qty' => $item->getQuantity(),
|
|
'price' => $item->getPrice(),
|
|
];
|
|
}
|
|
|
|
$jo_data['items'] = $items;
|
|
|
|
|
|
// dates depending on status
|
|
switch ($status)
|
|
{
|
|
case JOStatus::FULFILLED:
|
|
if ($jo->getDateFulfill() == null)
|
|
$jo_data['date_fulfilled'] = '';
|
|
else
|
|
$jo_data['date_fulfilled'] = $jo->getDateFulfill()->format('M d, Y');
|
|
break;
|
|
case JOStatus::CANCELLED:
|
|
$date_cancel = $jo->getDateCancel();
|
|
if ($date_cancel == null)
|
|
$date_cancel = new DateTime();
|
|
$jo_data['date_cancelled'] = $date_cancel->format('M d, Y');
|
|
break;
|
|
}
|
|
|
|
$all_jo_data[] = $jo_data;
|
|
}
|
|
|
|
// return data
|
|
$data = [
|
|
'job_orders' => $all_jo_data
|
|
];
|
|
$res->setData($data);
|
|
|
|
// response
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function updateDeviceID(Request $req)
|
|
{
|
|
$required_params = [
|
|
'device_id',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
$device_id = $req->request->get('device_id');
|
|
$this->session->setDevicePushID($device_id);
|
|
|
|
$em->flush();
|
|
|
|
// response
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function resendCode(Request $req, RisingTideGateway $rt)
|
|
{
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// already confirmed
|
|
if ($this->session->isConfirmed())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('User is already confirmed.');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// have sent code before
|
|
if ($this->session->getDateCodeSent() != null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Can only send confirm code every 5 mins.');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
|
|
// TODO: send via sms
|
|
$phone_number = $this->session->getPhoneNumber();
|
|
$code = $this->session->getConfirmCode();
|
|
$this->sendConfirmationCode($rt, $phone_number, $code);
|
|
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function privacySettings(Request $req)
|
|
{
|
|
$required_params = [
|
|
'priv_third_party',
|
|
// 'priv_promo',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// set privacy settings
|
|
$priv_promo = $req->request->get('priv_promo', false);
|
|
$priv_third_party = $req->request->get('priv_third_party');
|
|
$cust->setPrivacyThirdParty($priv_third_party)
|
|
->setPrivacyPromo($priv_promo);
|
|
|
|
// get the policy ids from .env
|
|
$dotenv = new Dotenv();
|
|
$dotenv->loadEnv(__DIR__.'/../../.env');
|
|
|
|
$policy_promo_id = $_ENV['POLICY_PROMO'];
|
|
$policy_third_party_id = $_ENV['POLICY_THIRD_PARTY'];
|
|
|
|
// check if privacy settings are true
|
|
// if true, set the private policy for the customer
|
|
if ($priv_promo)
|
|
{
|
|
// find the promo policy
|
|
$policy = $em->getRepository(PrivacyPolicy::class)->find($policy_promo_id);
|
|
|
|
// set policy id
|
|
if ($policy != null)
|
|
{
|
|
$cust->setPrivacyPolicyPromo($policy);
|
|
}
|
|
}
|
|
|
|
if ($priv_third_party)
|
|
{
|
|
// find the third party policy
|
|
$policy = $em->getRepository(PrivacyPolicy::class)->find($policy_third_party_id);
|
|
|
|
// set policy id
|
|
if ($policy != null)
|
|
{
|
|
$cust->setPrivacyPolicyThirdParty($policy);
|
|
}
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function locationSupport(Request $req)
|
|
{
|
|
$required_params = [
|
|
'longitude',
|
|
'latitude',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
|
|
$data = [
|
|
'longitude' => $req->query->get('longitude'),
|
|
'latitude' => $req->query->get('latitude'),
|
|
'supported' => true,
|
|
];
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function activateWarranty(Request $req)
|
|
{
|
|
$required_params = ['plate_number'];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
$plate_number = $req->request->get('plate_number');
|
|
|
|
// find warranty using plate number
|
|
$warranty_results = $em->getRepository(Warranty::class)->findBy(['plate_number' => $plate_number],
|
|
['date_create' => 'desc']);
|
|
|
|
// check if warranty_results is empty
|
|
if (empty($warranty_results))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No warranty found for plate number');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// activate all entries
|
|
foreach ($warranty_results as $warranty)
|
|
{
|
|
$warranty->setActivated();
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
protected function findWarranty($plate_number)
|
|
{
|
|
$em = $this->getDoctrine()->getManager();
|
|
// NOTE: Modify the search for the latest warranty. This seems hacky.
|
|
// get latest warranty using plate number
|
|
$warranty_results = $em->getRepository(Warranty::class)->findBy(['plate_number' => $plate_number],
|
|
['date_create' => 'desc']);
|
|
|
|
$warr = [];
|
|
|
|
// check if warranty_results is empty
|
|
if (empty($warranty_results))
|
|
{
|
|
/*
|
|
$res->setError(true)
|
|
->setErrorMessage('No warranty found for plate number');
|
|
return $res->getReturnResponse();
|
|
*/
|
|
|
|
return $warr;
|
|
}
|
|
|
|
// get first entry
|
|
$warranty = current($warranty_results);
|
|
|
|
// check for null values for battery and date claim and date expire
|
|
$batt_model = '';
|
|
$batt_size = '';
|
|
$sap_batt = '';
|
|
$claim_date = '';
|
|
$expiry_date = '';
|
|
|
|
if (!(is_null($warranty->getBatteryModel()))) {
|
|
$batt_model = $warranty->getBatteryModel()->getName();
|
|
}
|
|
if (!(is_null($warranty->getBatterySize()))) {
|
|
$batt_size = $warranty->getBatterySize()->getName();
|
|
}
|
|
if (!(is_null($warranty->getSAPBattery()))) {
|
|
$sap_batt = $warranty->getSAPBattery()->getID();
|
|
}
|
|
if (!(is_null($warranty->getDateClaim()))) {
|
|
$claim_date = $warranty->getDateClaim()->format("d M Y");
|
|
}
|
|
if (!(is_null($warranty->getDateExpire()))) {
|
|
$expiry_date = $warranty->getDateExpire()->format("d M Y");
|
|
}
|
|
|
|
$warr[] = [
|
|
'id' => $warranty->getID(),
|
|
'serial' => $warranty->getSerial(),
|
|
'warranty_class' => $warranty->getWarrantyClass(),
|
|
'plate_number' => $warranty->getPlateNumber(),
|
|
'first_name' => $warranty->getFirstName(),
|
|
'last_name' => $warranty->getLastName(),
|
|
'mobile_number' => $warranty->getMobileNumber(),
|
|
'battery_model' => $batt_model,
|
|
'battery_size' => $batt_size,
|
|
'sap_battery' => $sap_batt,
|
|
'status' => $warranty->getStatus(),
|
|
'date_create' => $warranty->getDateCreate()->format("d M Y g:i A"),
|
|
'date_purchase' => $warranty->getDatePurchase()->format("d M Y"),
|
|
'date_expire' => $expiry_date,
|
|
'date_claim' => $claim_date,
|
|
'claim_from' => $warranty->getClaimedFrom(),
|
|
'is_activated' => $warranty->isActivated() ? 1 : 0,
|
|
];
|
|
|
|
return $warr;
|
|
}
|
|
|
|
public function listServices(Request $req)
|
|
{
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// services
|
|
$results = $em->getRepository(Service::class)->findAll();
|
|
if (empty($results))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No services available.');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$services = [];
|
|
foreach ($results as $result)
|
|
{
|
|
/*
|
|
// get partners
|
|
$partners = [];
|
|
$service_partners = $result->getPartners();
|
|
foreach($service_partners as $sp)
|
|
{
|
|
$partners[] = [
|
|
'id' => $sp->getID(),
|
|
'name' => $sp->getName(),
|
|
'branch' => $sp->getBranch(),
|
|
'address' => $sp->getAddress(),
|
|
'contact_nums' => $sp->getContactNumbers(),
|
|
'time_open' => $sp->getTimeOpen()->format("g:i A"),
|
|
'time_close' => $sp->getTimeClose()->format("g:i A"),
|
|
];
|
|
}
|
|
*/
|
|
|
|
$services[] = [
|
|
'id' => $result->getID(),
|
|
'name' => $result->getName(),
|
|
// 'partners' => $partners,
|
|
];
|
|
}
|
|
|
|
$data['services'] = $services;
|
|
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getPartnerInformation(Request $req, $pid)
|
|
{
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// get partner
|
|
$partner = $em->getRepository(Partner::class)->findOneBy(['id' => $pid]);
|
|
if ($partner == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No partner found.');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// get reviews for partner
|
|
$reviews = $em->getRepository(Review::class)->findBy(['partner' => $partner]);
|
|
|
|
// get average rating for all reviews
|
|
$average_rating = 0;
|
|
if (!empty($reviews))
|
|
{
|
|
$rating = 0;
|
|
foreach($reviews as $review)
|
|
{
|
|
$rating = $rating + $review->getRating();
|
|
}
|
|
|
|
$average_rating = $rating / sizeof($reviews);
|
|
}
|
|
|
|
$data['partner'] = [
|
|
'id' => $partner->getID(),
|
|
'name' => $partner->getName(),
|
|
'branch' => $partner->getBranch(),
|
|
'address' => $partner->getAddress(),
|
|
'contact_nums' => $partner->getContactNumbers(),
|
|
'time_open' => $partner->getTimeOpen()->format("g:i A"),
|
|
'time_close' => $partner->getTimeClose()->format("g:i A"),
|
|
'longitude' => $partner->getCoordinates()->getLongitude(),
|
|
'latitude' => $partner->getCoordinates()->getLatitude(),
|
|
'average_rating' => $average_rating,
|
|
];
|
|
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getClosestPartners(Request $req)
|
|
{
|
|
$required_params = [
|
|
'longitude',
|
|
'latitude',
|
|
'service_id',
|
|
'limit',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
$long = $req->query->get('longitude');
|
|
$lat = $req->query->get('latitude');
|
|
$service_id = $req->query->get('service_id');
|
|
$limit = $req->query->get('limit');
|
|
|
|
// get partners within range
|
|
$query = $em->createQuery('SELECT p, st_distance(p.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Partner p
|
|
JOIN App\Entity\Service s where s.id = :service_id ORDER BY dist')
|
|
->setParameter('lat', $lat)
|
|
->setParameter('lng', $long)
|
|
->setParameter('service_id', $service_id);
|
|
|
|
$query->setMaxResults($limit);
|
|
$result = $query->getResult();
|
|
|
|
$data = [];
|
|
$partners = [];
|
|
foreach($result as $row)
|
|
{
|
|
$partners[] = [
|
|
'id' => $row[0]->getID(),
|
|
'name' => $row[0]->getName(),
|
|
'branch' => $row[0]->getBranch(),
|
|
'address' => $row[0]->getAddress(),
|
|
'contact_nums' => $row[0]->getContactNumbers(),
|
|
'time_open' => $row[0]->getTimeOpen()->format("g:i A"),
|
|
'time_close' => $row[0]->getTimeClose()->format("g:i A"),
|
|
'longitude' => $row[0]->getCoordinates()->getLongitude(),
|
|
'latitude' => $row[0]->getCoordinates()->getLatitude(),
|
|
'db_distance' => $row['dist'],
|
|
];
|
|
}
|
|
|
|
$data['partners'] = $partners;
|
|
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function reviewPartner($pid, Request $req, EntityManagerInterface $em)
|
|
{
|
|
$required_params = [
|
|
'rating',
|
|
'message',
|
|
];
|
|
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
$rating = $req->request->get('rating');
|
|
$msg = $req->request->get('message');
|
|
|
|
// TODO: check rating if 1 - 5
|
|
|
|
// check if partner exists
|
|
$partner = $em->getRepository(Partner::class)->find($pid);
|
|
if ($partner == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No partner found.');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$rev = new Review();
|
|
$rev->setRating($rating)
|
|
->setMessage($msg)
|
|
->setPartner($partner)
|
|
->setMobileSession($this->session);
|
|
|
|
// save to db
|
|
$em->persist($rev);
|
|
$em->flush();
|
|
|
|
$data = [];
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function getNearestHubAndSlots(Request $req, EntityManagerInterface $em,
|
|
MapTools $map_tools)
|
|
{
|
|
$required_params = [
|
|
'longitude',
|
|
'latitude',
|
|
];
|
|
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
$coordinates = new Point($req->query->get('longitude'), $req->query->get('latitude'));
|
|
$nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $em, $map_tools);
|
|
|
|
if (empty($nearest_hub_slots['hub']))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Thank you for reaching out to us. Due to the General Community Quarantine, our Operations are from 8AM to 6PM only. Please expect a call from us tomorrow and we will assist you with your request. Thank you and stay safe!');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// make hub data
|
|
$data = [
|
|
'hub_id' => $nearest_hub_slots['hub']->getID(),
|
|
'hub_slots' => $nearest_hub_slots['slots'],
|
|
];
|
|
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
|
|
}
|
|
|
|
public function newRequestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo,
|
|
MapTools $map_tools, InventoryManager $im, MQTTClient $mclient,
|
|
RiderAssignmentHandlerInterface $rah)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [
|
|
'service_type',
|
|
'cv_id',
|
|
'trade_in',
|
|
'long',
|
|
'lat',
|
|
'warranty',
|
|
'mode_of_payment',
|
|
];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
// trade in type
|
|
$trade_in = $req->request->get('trade_in');
|
|
|
|
// address
|
|
$address = $req->request->get('delivery_address', 'Set by mobile application');
|
|
|
|
// instructions
|
|
$instructions = $req->request->get('delivery_instructions', '');
|
|
|
|
// longitude and latitude
|
|
$long = $req->request->get('long');
|
|
$lat = $req->request->get('lat');
|
|
|
|
// geofence
|
|
$is_covered = $geo->isCovered($long, $lat);
|
|
if (!$is_covered)
|
|
{
|
|
// TODO: put geofence error message in config file somewhere
|
|
$res->setError(true)
|
|
->setErrorMessage('Oops! Our service is limited to some areas in Metro Manila, Laguna, and Baguio only. We will update you as soon as we are able to cover your area');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$hub = null;
|
|
$hub_id = $req->request->get('hub_id');
|
|
if (strlen($hub_id) > 0)
|
|
$hub = $em->getRepository(Hub::class)->find($hub_id);
|
|
|
|
$schedule_date = $req->request->get('date_schedule');
|
|
$slot_id = $req->request->get('slot_id');
|
|
$advance_order = $req->request->get('flag_advance_order');
|
|
if ($advance_order)
|
|
$flag_advance_order = true;
|
|
else
|
|
$flag_advance_order = false;
|
|
// $flag_advance_order = $advance_order ? true : false;
|
|
|
|
$jo = new JobOrder();
|
|
$jo->setSource(TransactionOrigin::MOBILE_APP)
|
|
->setStatus(JOStatus::PENDING)
|
|
->setDeliveryInstructions('')
|
|
->setTier1Notes('')
|
|
->setTier2Notes('')
|
|
->setDeliveryAddress($address)
|
|
->setTradeInType($trade_in)
|
|
->setDeliveryInstructions($instructions)
|
|
// TODO: error check for valid mode of payment
|
|
->setModeOfPayment($req->request->get('mode_of_payment'))
|
|
->setAdvanceOrder($flag_advance_order);
|
|
|
|
// customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('No customer information found');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$jo->setCustomer($cust);
|
|
|
|
// validate service type
|
|
$stype = $req->request->get('service_type');
|
|
if (!ServiceType::validate($stype))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid service type');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$jo->setServiceType($stype);
|
|
|
|
// validate warranty
|
|
$warr = $req->request->get('warranty');
|
|
if (!WarrantyClass::validate($warr))
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid warranty class');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$jo->setWarrantyClass($warr);
|
|
|
|
// set coordinates
|
|
$point = new Point($long, $lat);
|
|
$jo->setCoordinates($point);
|
|
|
|
// make invoice criteria
|
|
$icrit = new InvoiceCriteria();
|
|
$icrit->setServiceType($stype);
|
|
|
|
// check promo
|
|
$promo_id = $req->request->get('promo_id');
|
|
if (!empty($promo_id))
|
|
{
|
|
$promo = $em->getRepository(Promo::class)->find($promo_id);
|
|
if ($promo == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid promo id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// put in criteria
|
|
$icrit->addPromo($promo);
|
|
}
|
|
|
|
// check customer vehicle
|
|
$cv = $em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id'));
|
|
if ($cv == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid customer vehicle id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
$icrit->setCustomerVehicle($cv);
|
|
$jo->setCustomerVehicle($cv);
|
|
|
|
// check if customer owns vehicle
|
|
if ($cust->getID() != $cv->getCustomer()->getID())
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Customer does not own vehicle');
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
// check battery
|
|
$batt_id = $req->request->get('batt_id');
|
|
if ($batt_id != null)
|
|
{
|
|
$batt = $em->getRepository(Battery::class)->find($batt_id);
|
|
if ($batt == null)
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid battery id');
|
|
return $res->getReturnResponse();
|
|
}
|
|
}
|
|
else
|
|
$batt = null;
|
|
|
|
/*
|
|
// put battery in criteria
|
|
$icrit->addBattery($batt);
|
|
*/
|
|
|
|
// check trade-in
|
|
// only allow motolite, other, none
|
|
switch ($trade_in)
|
|
{
|
|
case TradeInType::MOTOLITE:
|
|
case TradeInType::OTHER:
|
|
break;
|
|
|
|
default:
|
|
$trade_in = '';
|
|
break;
|
|
}
|
|
|
|
$icrit->addEntry($batt, $trade_in, 1);
|
|
|
|
// send to invoice generator
|
|
$invoice = $ic->generateInvoice($icrit);
|
|
$jo->setInvoice($invoice);
|
|
|
|
// assign hub and rider
|
|
// check if hub is null
|
|
if ($hub == null)
|
|
{
|
|
// find nearest hub
|
|
if (($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) ||
|
|
($jo->getServicetype() == ServiceType::BATTERY_REPLACEMENT_WARRANTY))
|
|
{
|
|
// get nearest hub
|
|
// $nearest_hub = $this->findNearestHubWithInventory($jo, $batt, $em, $map_tools, $im);
|
|
$nearest_hub = $this->findNearestHub($jo, $em, $map_tools);
|
|
}
|
|
else
|
|
{
|
|
$nearest_hub = $this->findNearestHub($jo, $em, $map_tools);
|
|
}
|
|
|
|
if (!empty($nearest_hub))
|
|
{
|
|
//error_log('found nearest hub ' . $nearest_hub->getID());
|
|
// assign rider
|
|
$available_riders = $nearest_hub->getAvailableRiders();
|
|
if (count($available_riders) > 0)
|
|
{
|
|
$assigned_rider = null;
|
|
if (count($available_riders) > 1)
|
|
{
|
|
// TODO: the setting of riders into an array
|
|
// will no longer be necessary when the contents
|
|
// of randomizeRider changes
|
|
$riders = [];
|
|
foreach ($available_riders as $rider)
|
|
{
|
|
$riders[] = $rider;
|
|
}
|
|
|
|
$assigned_rider = $this->randomizeRider($riders);
|
|
}
|
|
else
|
|
$assigned_rider = $available_riders[0];
|
|
|
|
//error_log('found rider ' . $assigned_rider->getID());
|
|
$jo->setHub($nearest_hub);
|
|
$jo->setRider($assigned_rider);
|
|
$jo->setStatus(JOStatus::ASSIGNED);
|
|
|
|
$assigned_rider->setAvailable(false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$date_schedule = null;
|
|
if ((strlen($schedule_date) > 0) && (strlen($slot_id) > 0))
|
|
{
|
|
$time_schedule = $this->getTimeFromSlot($slot_id);
|
|
if (!empty($time_schedule))
|
|
{
|
|
$s_date = $schedule_date . ' ' . $time_schedule;
|
|
$date_schedule = DateTime::createFromFormat('Y-m-d H:i', $s_date);
|
|
//error_log($date_schedule->format('Y-m-d H:i'));
|
|
}
|
|
}
|
|
|
|
$jo->setHub($hub);
|
|
$jo->setStatus(JOStatus::RIDER_ASSIGN);
|
|
|
|
if ($date_schedule != null)
|
|
$jo->setDateSchedule($date_schedule);
|
|
}
|
|
|
|
$em->persist($jo);
|
|
$em->persist($invoice);
|
|
|
|
// add event log for JO
|
|
$event = new JOEvent();
|
|
$event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::CREATE)
|
|
->setJobOrder($jo);
|
|
$em->persist($event);
|
|
|
|
// check JO status
|
|
if ($jo->getStatus() == JOStatus::ASSIGNED)
|
|
{
|
|
// add event logs for hub and rider assignments
|
|
$hub_assign_event = new JOEvent();
|
|
$hub_assign_event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::HUB_ASSIGN)
|
|
->setJobOrder($jo);
|
|
|
|
$em->persist($hub_assign_event);
|
|
|
|
$rider_assign_event = new JOEvent();
|
|
$rider_assign_event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::RIDER_ASSIGN)
|
|
->setJobOrder($jo);
|
|
|
|
$em->persist($rider_assign_event);
|
|
|
|
// user mqtt event
|
|
$payload = [
|
|
'event' => 'outlet_assign'
|
|
];
|
|
$mclient->sendEvent($jo, $payload);
|
|
|
|
$rah->assignJobOrder($jo, $jo->getRider());
|
|
}
|
|
|
|
if ($jo->getStatus() == JOStatus::RIDER_ASSIGN)
|
|
{
|
|
// add event logs for hub assignments
|
|
$hub_assign_event = new JOEvent();
|
|
$hub_assign_event->setDateHappen(new DateTime())
|
|
->setTypeID(JOEventType::HUB_ASSIGN)
|
|
->setJobOrder($jo);
|
|
|
|
$em->persist($hub_assign_event);
|
|
|
|
// user mqtt event
|
|
$payload = [
|
|
'event' => 'outlet_assign'
|
|
];
|
|
$mclient->sendEvent($jo, $payload);
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
// make invoice json data
|
|
$invoice_data = [
|
|
'total_price' => $invoice->getTotalPrice(),
|
|
'vat_ex_price' => $invoice->getVATExclusivePrice(),
|
|
'vat' => $invoice->getVAT(),
|
|
'discount' => $invoice->getDiscount(),
|
|
'trade_in' => $invoice->getTradeIn(),
|
|
];
|
|
$items = $invoice->getItems();
|
|
$items_data = [];
|
|
foreach ($items as $item)
|
|
{
|
|
$items_data[] = [
|
|
'title' => $item->getTitle(),
|
|
'qty' => $item->getQuantity() + 0,
|
|
'price' => $item->getPrice() + 0.0,
|
|
];
|
|
}
|
|
$invoice_data['items'] = $items_data;
|
|
|
|
// make job order data
|
|
$data = [
|
|
'jo_id' => $jo->getID(),
|
|
'invoice' => $invoice_data
|
|
];
|
|
|
|
// set data
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function versionCheck(Request $req)
|
|
{
|
|
$res = new APIResult();
|
|
|
|
$required_params = [
|
|
'version',
|
|
];
|
|
|
|
$missing = $this->checkMissingParameters($req, $required_params);
|
|
if (count($missing) > 0)
|
|
{
|
|
$params = implode(', ', $missing);
|
|
$res->setError(true)
|
|
->setErrorMessage('Missing parameter(s): ' . $params);
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
$need_update = false;
|
|
$msg = 'Version is up to date.';
|
|
|
|
$api_version = $this->getParameter('api_version');
|
|
|
|
$app_version = $req->query->get('version');
|
|
|
|
$api_v = explode('.', $api_version);
|
|
$app_v = explode('.', $app_version);
|
|
|
|
if ($api_v[0] < $app_v[0])
|
|
{
|
|
$res->setError(true)
|
|
->setErrorMessage('Invalid application version: ' . $app_version);
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
if ($api_v[0] > $app_v[0])
|
|
{
|
|
$need_update = true;
|
|
$msg = 'Your version is outdated and needs an update to use the latest features RES-Q has to offer.';
|
|
}
|
|
|
|
|
|
$data = [
|
|
'need_update' => $need_update,
|
|
'message' => $msg,
|
|
];
|
|
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
public function scheduleOptionStatus(Request $req)
|
|
{
|
|
// check required parameters and api key
|
|
$required_params = [];
|
|
$em = $this->getDoctrine()->getManager();
|
|
$res = $this->checkParamsAndKey($req, $em, $required_params);
|
|
if ($res->isError())
|
|
return $res->getReturnResponse();
|
|
|
|
$schedule_choice = true;
|
|
|
|
// TODO: remove the time check after ECQ. This will then always return true
|
|
// get current time
|
|
$current_datetime = new DateTime();
|
|
//$current_datetime = DateTime::createFromFormat('Y-m-d H:i', '2020-04-30 17:01');
|
|
|
|
// get the hour
|
|
$hour = $current_datetime->format('G');
|
|
|
|
if (($hour < 8) || ($hour > 16))
|
|
$schedule_choice = false;
|
|
|
|
$data = [
|
|
'display_schedule_choice' => $schedule_choice,
|
|
];
|
|
$res->setData($data);
|
|
|
|
return $res->getReturnResponse();
|
|
}
|
|
|
|
protected function findCustomerByNumber($number)
|
|
{
|
|
$em = $this->getDoctrine()->getManager();
|
|
$customers = $em->getRepository(Customer::class)->findBy(['phone_mobile' => $number]);
|
|
|
|
// find the customer with the most number of cars
|
|
$car_count = 0;
|
|
$cust = null;
|
|
|
|
foreach($customers as $customer)
|
|
{
|
|
$vehicles = $customer->getVehicles();
|
|
if (count($vehicles) > $car_count)
|
|
{
|
|
$car_count = count($vehicles);
|
|
|
|
// "save" customer object
|
|
$cust = $customer;
|
|
}
|
|
}
|
|
|
|
return $cust;
|
|
}
|
|
|
|
protected function findNearestHub($jo, EntityManagerInterface $em, MapTools $map_tools)
|
|
{
|
|
// get the nearest 10 hubs
|
|
$selected_hub = null;
|
|
$hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s"));
|
|
|
|
$nearest_hubs_with_distance = [];
|
|
$nearest_branch_codes = [];
|
|
foreach ($hubs as $hub)
|
|
{
|
|
$nearest_hubs_with_distance[] = $hub;
|
|
//if (!empty($hub['hub']->getBranchCode()))
|
|
// $nearest_branch_codes[] = $hub['hub']->getBranchCode();
|
|
}
|
|
|
|
// check if nearest hubs have branch codes
|
|
//if (count($nearest_branch_codes) == 0)
|
|
// return $selected_hub;
|
|
|
|
// assume all 10 have stock
|
|
// find the nearest hub with available riders
|
|
$nearest = null;
|
|
foreach ($nearest_hubs_with_distance as $nhd)
|
|
{
|
|
// get number of available riders
|
|
$count_riders = count($nhd['hub']->getAvailableRiders());
|
|
|
|
// get number of advance orders in the next 3 hours
|
|
$time_now = new DateTime();
|
|
$date_end = new DateTime();
|
|
$date_end->add(new DateInterval('PT2H'));
|
|
|
|
// NOTE: get advance orders via query
|
|
// get JOs assigned to hub that are advance orders and scheduled within X hours with
|
|
// for rider assignment status
|
|
$query = $em->createQuery('select count(jo) from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and jo.date_schedule <= :date_end and jo.status = :status');
|
|
$count_advance_orders = $query->setParameters([
|
|
'hub' => $nhd['hub'],
|
|
'date_end' => $date_end,
|
|
'status' => JOStatus::RIDER_ASSIGN,
|
|
])
|
|
->setMaxResults(1)
|
|
->getSingleScalarResult();
|
|
|
|
error_log('HUB - ' . $nhd['hub']->getID());
|
|
error_log('RIDER COUNT - ' . $count_riders);
|
|
error_log('ADVANCE ORDER COUNT - ' . $count_advance_orders);
|
|
|
|
// if (count($nhd['hub']->getAvailableRiders()) > 0)
|
|
// if we have more riders than we have advance orders
|
|
if ($count_riders - $count_advance_orders > 0)
|
|
{
|
|
if (empty($nearest))
|
|
$nearest = $nhd;
|
|
else
|
|
{
|
|
if ($nhd['distance'] < $nearest['distance'])
|
|
$nearest = $nhd;
|
|
}
|
|
}
|
|
}
|
|
|
|
$selected_hub = $nearest['hub'];
|
|
|
|
return $selected_hub;
|
|
}
|
|
|
|
protected function findNearestHubWithInventory($jo, Battery $batt, EntityManagerInterface $em,
|
|
MapTools $map_tools, InventoryManager $im)
|
|
{
|
|
// get the nearest 10 hubs
|
|
$selected_hub = null;
|
|
$hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s"));
|
|
|
|
$nearest_hubs_with_distance = [];
|
|
$nearest_branch_codes = [];
|
|
foreach ($hubs as $hub)
|
|
{
|
|
$nearest_hubs_with_distance[] = $hub;
|
|
//if (!empty($hub['hub']->getBranchCode()))
|
|
// $nearest_branch_codes[] = $hub['hub']->getBranchCode();
|
|
}
|
|
|
|
// check if nearest hubs have branch codes
|
|
//if (count($nearest_branch_codes) == 0)
|
|
// return $selected_hub;
|
|
|
|
// assume all 10 have stock
|
|
// find the nearest hub with available riders
|
|
$nearest = null;
|
|
foreach ($nearest_hubs_with_distance as $nhd)
|
|
{
|
|
if (count($nhd['hub']->getAvailableRiders()) > 0)
|
|
{
|
|
if (empty($nearest))
|
|
$nearest = $nhd;
|
|
else
|
|
{
|
|
if ($nhd['distance'] < $nearest['distance'])
|
|
$nearest = $nhd;
|
|
}
|
|
}
|
|
}
|
|
|
|
$selected_hub = $nearest['hub'];
|
|
|
|
// uncomment this snippet when inventory check becomes active
|
|
// get battery sku
|
|
/*
|
|
if ($batt != null)
|
|
{
|
|
$skus[] = $batt->getSAPCode();
|
|
|
|
// api call to check inventory
|
|
// pass the list of branch codes of nearest hubs and the skus
|
|
// go through returned list of branch codes
|
|
// bypass inventory check for now
|
|
// $hubs_with_inventory = $im->getBranchesInventory($nearest_branch_codes, $skus);
|
|
if (!empty($hubs_with_inventory))
|
|
{
|
|
$nearest = [];
|
|
$flag_hub_found = false;
|
|
foreach ($hubs_with_inventory as $hub_with_inventory)
|
|
{
|
|
// find hub according to branch code
|
|
$found_hub = $em->getRepository(Hub::class)->findOneBy(['branch_code' => $hub_with_inventory['BranchCode']]);
|
|
if ($found_hub != null)
|
|
{
|
|
// check rider availability
|
|
if (count($found_hub->getAvailableRiders()) > 0)
|
|
{
|
|
// check against nearest hubs with distance
|
|
foreach ($nearest_hubs_with_distance as $nhd)
|
|
{
|
|
// get distance of hub from location, compare with $nearest. if less, replace nearest
|
|
if ($found_hub->getID() == $nhd['hub']->getID())
|
|
{
|
|
if (empty($nearest))
|
|
{
|
|
$nearest = $nhd;
|
|
$flag_hub_found = true;
|
|
}
|
|
else
|
|
{
|
|
if ($nhd['distance'] < $nearest['distance'])
|
|
{
|
|
$nearest = $nhd;
|
|
$flag_hub_found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$selected_hub = $nearest['hub'];
|
|
}
|
|
} */
|
|
return $selected_hub;
|
|
}
|
|
|
|
protected function randomizeRider($riders)
|
|
{
|
|
// TODO: get redis to track the sales per rider per day
|
|
// check the time they came in
|
|
// for now, randomize the rider
|
|
$selected_index = array_rand($riders);
|
|
|
|
$selected_rider = $riders[$selected_index];
|
|
|
|
return $selected_rider;
|
|
}
|
|
|
|
protected function findAdvanceNearestHubAndSlots(Point $coordinates, EntityManagerInterface $em, MapTools $map_tools)
|
|
{
|
|
// get the nearest 10 hubs
|
|
$hub_data = [];
|
|
$nearest_hubs_with_distance = [];
|
|
$hubs = $map_tools->getClosestOpenHubs($coordinates, 10);
|
|
|
|
foreach ($hubs as $hub)
|
|
{
|
|
$nearest_hubs_with_distance[] = $hub;
|
|
// TODO: insert checking for branch code here when inventory manager is up
|
|
}
|
|
|
|
$nearest = null;
|
|
$slot_found = false;
|
|
// find the nearest hub
|
|
if (!empty($nearest_hubs_with_distance))
|
|
{
|
|
foreach ($nearest_hubs_with_distance as $nhd)
|
|
{
|
|
if (empty($nearest))
|
|
$nearest = $nhd;
|
|
else
|
|
{
|
|
if ($nhd['distance'] < $nearest['distance'])
|
|
$nearest = $nhd;
|
|
}
|
|
}
|
|
|
|
// get slots of nearest hub
|
|
if ($nearest != null)
|
|
{
|
|
$hub_slots = $this->getHubRiderSlots($nearest['hub'], $em);
|
|
|
|
$hub_data = [
|
|
'hub' => $nearest['hub'],
|
|
'slots' => $hub_slots,
|
|
];
|
|
}
|
|
}
|
|
|
|
return $hub_data;
|
|
|
|
}
|
|
|
|
protected function getHubRiderSlots(Hub $hub, EntityManagerInterface $em)
|
|
{
|
|
// check hub's advance orders for the day
|
|
// get number of advance orders for the next day if request came in before midnight
|
|
// or for current day if request came in after midnight
|
|
// check request_time
|
|
$request_time = time();
|
|
$midnight = strtotime('00:00');
|
|
$start_date = new DateTime();
|
|
$end_date = new DateTime();
|
|
|
|
if ($request_time < $midnight)
|
|
{
|
|
// add +1 to start date to get the next day
|
|
// add +3 to date to end date to get the advance orders for the next three days
|
|
$start_date->add(new DateInterval('P1D'));
|
|
}
|
|
$end_date->add(new DateInterval('P2D'));
|
|
|
|
// set time bounds for the start and end date
|
|
$start_date->setTime(0, 1);
|
|
$end_date->setTime(23, 59);
|
|
|
|
$str_request_time = date('Y-m-d H:i:s', $request_time);
|
|
$time_of_request = DateTime::createFromFormat('Y-m-d H:i:s', $str_request_time);
|
|
|
|
// NOTE: get advance orders via query
|
|
// get JOs assigned to hub that are advance orders and scheduled for the next three days with
|
|
// for hub assignment status
|
|
$query = $em->createQuery('select jo from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and
|
|
jo.date_schedule >= :date_start and jo.date_schedule <= :date_end and jo.status != :status_cancelled
|
|
and jo.status != :status_fulfilled');
|
|
$jos_advance_orders = $query->setParameters([
|
|
'hub' => $hub,
|
|
'date_start' => $start_date,
|
|
'date_end' => $end_date,
|
|
'status_cancelled' => JOStatus::CANCELLED,
|
|
'status_fulfilled' => JOStatus::FULFILLED,
|
|
])
|
|
->getResult();
|
|
|
|
// check each JO's date_schedule, decrement rider_slots if date schedule falls in that slot
|
|
// index - equivalent time
|
|
// 0 - 8-9
|
|
// 1 - 9-10
|
|
// 2 - 10-11
|
|
// 3 - 11-12
|
|
// 4 - 12 -13
|
|
// 5 - 13-14
|
|
// 6 - 14-15
|
|
// 7 - 15-16
|
|
// 8 - 16-17
|
|
$hub_slots = [];
|
|
|
|
// get the dates for the next three days
|
|
$first_date = $start_date->format('Y-m-d');
|
|
|
|
// 2nd day
|
|
$second_date = $start_date->add(new DateInterval('P1D'));
|
|
$sec_date = $second_date->format('Y-m-d');
|
|
|
|
// third day
|
|
$third_date = $end_date->format('Y-m-d');
|
|
|
|
// array of # of riders that can handle JOs in a timeslot
|
|
$hub_rider_slots = [];
|
|
|
|
// populate the array with the hub's rider slots per day
|
|
for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++)
|
|
{
|
|
$hub_rider_slots[$first_date][$i] = $hub->getRiderSlots();
|
|
}
|
|
for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++)
|
|
{
|
|
$hub_rider_slots[$sec_date][$i] = $hub->getRiderSlots();
|
|
}
|
|
for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++)
|
|
{
|
|
$hub_rider_slots[$third_date][$i] = $hub->getRiderSlots();
|
|
}
|
|
|
|
// no advance orders
|
|
if (empty($jos_advance_orders))
|
|
{
|
|
// set hub slots for the next three days
|
|
$slot_status = true;
|
|
|
|
$hub_slots[$first_date] = $this->setAllHubSlots($slot_status);
|
|
$hub_slots[$sec_date] = $this->setAllHubSlots($slot_status);
|
|
$hub_slots[$third_date] = $this->setAllHubSlots($slot_status);
|
|
}
|
|
else
|
|
{
|
|
foreach ($jos_advance_orders as $advance_jo)
|
|
{
|
|
// get date schedules of JO
|
|
$jo_date_schedule = $advance_jo->getDateSchedule();
|
|
|
|
$date_schedule = $jo_date_schedule->format('Y-m-d');
|
|
$time_schedule = $jo_date_schedule->format('H:i');
|
|
|
|
$hour_schedule = $jo_date_schedule->format('H');
|
|
$minute_schedule = $jo_date_schedule->format('i');
|
|
|
|
// check which of the three dates does date schedule fall
|
|
// and if date is already in hub slots
|
|
if (($first_date == $date_schedule) &&
|
|
(!isset($hub_slots[$first_date])))
|
|
$hub_slots[$first_date] = [];
|
|
|
|
if (($sec_date == $date_schedule) &&
|
|
(!isset($hub_slots[$sec_date])))
|
|
$hub_slots[$sec_date] = [];
|
|
|
|
if (($third_date == $date_schedule) &&
|
|
(!isset($hub_slots[$third_date])))
|
|
$hub_slots[$third_date] = [];
|
|
|
|
// decrement number of hub rider slots then
|
|
// set hub slots
|
|
if ($date_schedule == $first_date)
|
|
$this->decrementRiderSlots($hub_rider_slots, $first_date, $hour_schedule, $minute_schedule);
|
|
|
|
if ($date_schedule == $sec_date)
|
|
$this->decrementRiderSlots($hub_rider_slots, $sec_date, $hour_schedule, $minute_schedule);
|
|
|
|
if ($date_schedule == $third_date)
|
|
$this->decrementRiderSlots($hub_rider_slots, $third_date, $hour_schedule, $minute_schedule);
|
|
|
|
$hub_slots[$first_date] = $this->setHubSlots($hub_rider_slots, $first_date);
|
|
$hub_slots[$sec_date] = $this->setHubSlots($hub_rider_slots, $sec_date);
|
|
$hub_slots[$third_date] = $this->setHubSlots($hub_rider_slots, $third_date);
|
|
}
|
|
}
|
|
|
|
return $hub_slots;
|
|
}
|
|
|
|
protected function setAllHubSlots($status)
|
|
{
|
|
$data = [];
|
|
|
|
// set all slots to true
|
|
$data = [
|
|
[
|
|
'id' => '08_09',
|
|
'label' => '8:00 AM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' => '09_10',
|
|
'label' => '9:00 AM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' => '10_11',
|
|
'label' => '10:00 AM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' =>'11_12',
|
|
'label' => '11:00 AM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' => '12_13',
|
|
'label' => '12:00 PM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' => '13_14',
|
|
'label' => '1:00 PM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' => '14_15',
|
|
'label' => '2:00 PM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' => '15_16',
|
|
'label' => '3:00 PM',
|
|
'available' => $status,
|
|
],
|
|
[
|
|
'id' => '16_17',
|
|
'label' => '4:00 PM',
|
|
'available' => $status,
|
|
],
|
|
];
|
|
|
|
return $data;
|
|
}
|
|
|
|
protected function setHubSlots($hub_rider_slots, $schedule_date)
|
|
{
|
|
$data = [];
|
|
// check values of hub_rider_slot to mark if available or not
|
|
for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++)
|
|
{
|
|
if ($hub_rider_slots[$schedule_date][$i] > 0)
|
|
{
|
|
// set the time label
|
|
switch($i) {
|
|
case '0':
|
|
$data[] = [
|
|
'id' => '08_09',
|
|
'label' => '8:00 AM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '1':
|
|
$data[] = [
|
|
'id' => '09_10',
|
|
'label' => '9:00 AM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '2':
|
|
$data[] = [
|
|
'id' => '10_11',
|
|
'label' => '10:00 AM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '3':
|
|
$data[] = [
|
|
'id' => '11_12',
|
|
'label' => '11:00 AM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '4':
|
|
$data[] = [
|
|
'id' => '12_13',
|
|
'label' => '12:00 PM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '5':
|
|
$data[] = [
|
|
'id' => '13_14',
|
|
'label' => '1:00 PM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '6':
|
|
$data[] = [
|
|
'id' => '14_15',
|
|
'label' => '2:00 PM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '7':
|
|
$data[] = [
|
|
'id' => '15_16',
|
|
'label' => '3:00 PM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
case '8':
|
|
$data[] = [
|
|
'id' => '16_17',
|
|
'label' => '4:00 PM',
|
|
'available' => true,
|
|
];
|
|
break;
|
|
default:
|
|
error_log('Invalid rider slot index.');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// set the time label
|
|
switch($i) {
|
|
case '0':
|
|
$data[] = [
|
|
'id' => '08_09',
|
|
'label' => '8:00 AM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '1':
|
|
$data[] = [
|
|
'id' => '09_10',
|
|
'label' => '9:00 AM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '2':
|
|
$data[] = [
|
|
'id' => '10_11',
|
|
'label' => '10:00 AM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '3':
|
|
$data[] = [
|
|
'id' => '11_12',
|
|
'label' => '11:00 AM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '4':
|
|
$data[] = [
|
|
'id' => '12_13',
|
|
'label' => '12:00 PM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '5':
|
|
$data[] = [
|
|
'id' => '13_14',
|
|
'label' => '1:00 PM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '6':
|
|
$data[] = [
|
|
'id' => '14_15',
|
|
'label' => '2:00 PM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '7':
|
|
$data[] = [
|
|
'id' => '15_16',
|
|
'label' => '3:00 PM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
case '8':
|
|
$data[] = [
|
|
'id' => '16_17',
|
|
'label' => '4:00 PM',
|
|
'available' => false,
|
|
];
|
|
break;
|
|
default:
|
|
error_log('Invalid rider slot index.');
|
|
}
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
protected function decrementRiderSlots(&$hub_rider_slots, $date_schedule, $hour_schedule, $minute_schedule)
|
|
{
|
|
if ($minute_schedule != '00')
|
|
{
|
|
switch($hour_schedule) {
|
|
case '8':
|
|
$hub_rider_slots[$date_schedule][0]--;
|
|
$hub_rider_slots[$date_schedule][1]--;
|
|
break;
|
|
case '9':
|
|
$hub_rider_slots[$date_schedule][1]--;
|
|
$hub_rider_slots[$date_schedule][2]--;
|
|
break;
|
|
case '10':
|
|
$hub_rider_slots[$date_schedule][2]--;
|
|
$hub_rider_slots[$date_schedule][3]--;
|
|
break;
|
|
case '11':
|
|
$hub_rider_slots[$date_schedule][3]--;
|
|
$hub_rider_slots[$date_schedule][4]--;
|
|
break;
|
|
case '12':
|
|
$hub_rider_slots[$date_schedule][4]--;
|
|
$hub_rider_slots[$date_schedule][5]--;
|
|
break;
|
|
case '13':
|
|
$hub_rider_slots[$date_schedule][5]--;
|
|
$hub_rider_slots[$date_schedule][6]--;
|
|
break;
|
|
case '14':
|
|
$hub_rider_slots[$date_schedule][6]--;
|
|
$hub_rider_slots[$date_schedule][7]--;
|
|
break;
|
|
case '15':
|
|
$hub_rider_slots[$date_schedule][7]--;
|
|
$hub_rider_slots[$date_schedule][8]--;
|
|
break;
|
|
case '16':
|
|
error_log('No slots for the day');
|
|
break;
|
|
default:
|
|
error_log('Does not fit in any time slot. ' . $time_schedule);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ($hour_schedule) {
|
|
case '8':
|
|
$hub_rider_slots[$date_schedule][0]--;
|
|
break;
|
|
case '9':
|
|
$hub_rider_slots[$date_schedule][1]--;
|
|
break;
|
|
case '10':
|
|
$hub_rider_slots[$date_schedule][2]--;
|
|
break;
|
|
case '11':
|
|
$hub_rider_slots[$date_schedule][3]--;
|
|
break;
|
|
case '12':
|
|
$hub_rider_slots[$date_schedule][4]--;
|
|
break;
|
|
case '13':
|
|
$hub_rider_slots[$date_schedule][5]--;
|
|
break;
|
|
case '14':
|
|
$hub_rider_slots[$date_schedule][6]--;
|
|
break;
|
|
case '15':
|
|
$hub_rider_slots[$date_schedule][7]--;
|
|
break;
|
|
case '16':
|
|
$hub_rider_slots[$date_schedule][8]--;
|
|
break;
|
|
default:
|
|
error_log('Does not fit in any time slot.' . $time_schedule);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function getTimeFromSlot($slot_id)
|
|
{
|
|
$time_selected = '';
|
|
|
|
switch($slot_id) {
|
|
case '08_09':
|
|
$time_selected = AdvanceOrderSlot::_08_09;
|
|
break;
|
|
case '09_10':
|
|
$time_selected = AdvanceOrderSlot::_09_10;
|
|
break;
|
|
case '10_11':
|
|
$time_selected = AdvanceOrderSlot::_10_11;
|
|
break;
|
|
case '11_12':
|
|
$time_selected = AdvanceOrderSlot::_11_12;
|
|
break;
|
|
case '12_13':
|
|
$time_selected = AdvanceOrderSlot::_12_13;
|
|
break;
|
|
case '13_14':
|
|
$time_selected = AdvanceOrderSlot::_13_14;
|
|
break;
|
|
case '14_15':
|
|
$time_selected = AdvanceOrderSlot::_14_15;
|
|
break;
|
|
case '15_16':
|
|
$time_selected = AdvanceOrderSlot::_15_16;
|
|
break;
|
|
case '16_17':
|
|
$time_selected = AdvanceOrderSlot::_16_17;
|
|
break;
|
|
default:
|
|
error_log('Invalid slot id ' . $slot_id);
|
|
}
|
|
|
|
return $time_selected;
|
|
}
|
|
}
|