diff --git a/config/packages/security.yaml b/config/packages/security.yaml
index b39a2272..6de188ee 100644
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -23,6 +23,10 @@ security:
pattern: ^\/api\/
security: false
+ rider_api:
+ pattern: ^\/rapi\/
+ security: false
+
main:
form_login:
login_path: login
diff --git a/config/routes/api.yaml b/config/routes/api.yaml
index 190f6edb..7e601657 100644
--- a/config/routes/api.yaml
+++ b/config/routes/api.yaml
@@ -104,3 +104,13 @@ api_device_id:
path: /api/device_id
controller: App\Controller\APIController:updateDeviceID
methods: [POST]
+
+api_privacy:
+ path: /api/privacy
+ controller: App\Controller\APIController:privacySettings
+ methods: [POST]
+
+api_resend_code:
+ path: /api/resend_code
+ controller: App\Controller\APIController:resendCode
+ methods: [POST]
diff --git a/config/routes/rider_api.yaml b/config/routes/rider_api.yaml
new file mode 100644
index 00000000..4cf98d93
--- /dev/null
+++ b/config/routes/rider_api.yaml
@@ -0,0 +1,41 @@
+# rider app api
+
+rapi_register:
+ path: /rapi/register
+ controller: App\Controller\RAPIController::register
+ methods: [POST]
+
+rapi_login:
+ path: /rapi/login
+ controller: App\Controller\RAPIController::login
+ methods: [POST]
+
+rapi_logout:
+ path: /rapi/logout
+ controller: App\Controller\RAPIController::logout
+ methods: [POST]
+
+rapi_jo_get:
+ path: /rapi/joborder
+ controller: App\Controller\RAPIController::getJobOrder
+ methods: [GET]
+
+rapi_jo_accept:
+ path: /rapi/accept
+ controller: App\Controller\RAPIController::acceptJobOrder
+ methods: [POST]
+
+rapi_jo_cancel:
+ path: /rapi/cancel
+ controller: App\Controller\RAPIController::cancelJobOrder
+ methods: [POST]
+
+rapi_arrive:
+ path: /rapi/arrive
+ controller: App\Controller\RAPIController::arrive
+ methods: [POST]
+
+rapi_payment:
+ path: /rapi/payment
+ controller: App\Controller\RAPIController::payment
+ methods: [POST]
diff --git a/public/static/privacy.html b/public/static/privacy.html
new file mode 100644
index 00000000..bcecf84e
--- /dev/null
+++ b/public/static/privacy.html
@@ -0,0 +1,117 @@
+
Privacy Policy
+
+Oriental and Motolite Marketing Corporation ("Company," "We," "Us," "Our"), is committed to protecting the privacy and security of all personal and/or sensitive information ("Personal Data") related to its employees, customers, business partners, suppliers, contractors, and other parties that the Company will and is engaged into. For this reason, uniform practice and procedure for collecting, recording, consolidating, updating, disclosing, storing, accessing, transferring, retaining, destroying and disposing of Personal Data by the Company is hereby adopted in order to process Personal Data fairly, appropriately, and lawfully.
+
+
+
+This Privacy Policy sets out how the Company uses, protects and controls any Personal Data that you will provide in our website and mobile application (the "Site"). The Company may change this policy from time to time by updating it in accordance with subsequent Laws and Implementing Rules and Regulations. The terms of this Privacy Policy apply to all users of this Site.
+
+
+Collection of Personal Data
+
+The Company collects Personal Data you voluntarily submitted in our Site and our mobile apps, as well as non-personal information provided therein.
+
+
+User Registration
+
+We collect information about you (i) when you register in order to receive a product/service; (ii) when you subscribe to marketing emails; (iii) when you apply for a job post; (iv) when you contact us for inquiries, support or feedback. Information may include Personal Data such as but not limited to your first name, last name, email address, birthdate, gender, mobile number, address and password.
+
+
+Log information
+
+When you visit our Site, we may also collect non-personal information such as but not limited to web page from which you came to our Site, your web page request, Internet Protocol (IP) address, geolocation, browser type, browser language, the date and time of your request and your registration data. Please be noted that when you purchase a product or use a web-based service in our Site, we may also log the specific path, actions, and navigation choices you make.
+
+
+Cookies
+
+We may use cookies in analyzing and evaluating performance to provide you better experience when using our Site. Cookies are small files that a Site or its service provider transfers to your computer's hard drive through your web browser (you can disable this) that allows the Site's or service provider's systems to recognize your browser and capture and remember certain information.
+
+
+Location Services
+
+Your current location is only determined if you permit our services to do so. If you allow your location to be obtained using our RESQ app, or with a browser, we will use this information to return your estimated location. We use this information solely to distinguish your current location and not to identify you. Motolite does not automatically track your location.
+
+
+Use of Personal Data
+
+
+- The Company uses your Personal Data for the following purposes, without limitation:
+- to deliver or improve our products and services
+- to administer a content, promotion, survey or other Site feature
+- to send periodic emails regarding your account or other products and services
+- to process your application for any of our job posts
+- to process your application for a franchise
+- to effectively respond to your customer service requests and support needs.
+
+
+
+
+The Company may share the Personal Data gathered from you within the Ramcar Group of Companies and will use it consistent with the purpose of this Privacy Policy. We may also use the information in the aggregate to understand how our users as a group, use the services and resources provided on our Site.
+
+
+Further Disclosure
+
+We take reasonable precautions to be sure that nonaffiliated third parties and affiliates, to whom we disclose your Personal Data are aware of our Privacy Policy and will treat the information in a similarly responsible manner. Our contracts and written agreements with nonaffiliated third parties that receive information from us about you prohibit those parties from transferring the information other than to provide the service that you obtain from us.
+
+
+Agents and contractors
+
+Our contractors sometimes have access to your Personal Data in the course of assisting in operating our business and providing products or services to you. These contractors may include vendors and suppliers that provide us with technology, services, and/or content for the operation and maintenance of our Site. Access to your Personal Data by these contractors is limited to the information reasonably necessary for the contractor to perform its limited function. Contractors have an obligation under their contracts with us to keep your information confidential and to comply with our privacy and security policies.
+
+
+Disclosure for legal reasons
+
+We may release Personal Data to third parties: (1) to comply with valid legal requirements such as a law, regulation, search warrant, subpoena or court order; or (2) in special cases, such as a threat to security, a threat to our system or network, or cases in which we believe it is reasonably necessary to investigate or prevent harm, fraud, abuse, or illegal conduct.
+
+
+Changes in our corporate structure
+
+If all or part of the Company is sold, merged or otherwise transferred to another entity, the Personal Data you have provided to us may be transferred as part of that transaction. We will take steps to ensure that, without your consent, any Personal Data that is transferred will not be used or shared in a manner inconsistent with this Privacy Policy.
+
+
+Accuracy and Access
+
+The Company will keep your Personal Data as accurate, complete and up-to-date as is necessary for the purpose for which it is processed. In case you need to access, correct, amend, delete inaccurate Personal Data, or withdraw your consent, please immediately inform us. (see Contact Us section below)
+
+
+Security
+
+The Company employs adequate organizational, physical and technical security measures such as password protection, encryption, firewalls and other controls, to ensure and preserve the confidentiality, integrity and availability of your Personal Data. We work to protect the security of your information during transmission by using Secure Sockets Layer (SSL) software, which encrypts information you input online. Only authorized persons from the Company will have access to your Personal Data. Employees who misuse any information are subject to disciplinary action, including termination.
+
+
+Third Party Links
+
+The Company may offer links to sites that is run by third parties. If you visit other sites, you should read the site’s privacy policy, terms and conditions, and their other policies. The Company is not responsible for the policies and practices of third parties. Any information you give to those organizations is dealt with under their privacy statement, terms and conditions, and other policies.
+
+
Retention and Disposal
+
+The Company will retain your Personal Data only as long as necessary for the fulfillment of the stated purposes. In case that the Site is no longer functional or decommissioned, all of your Personal Data in digital or hard copies will be disposed or erased according to the Company's guidelines, where it is irretrievable, unreadable, or unidentifiable. In case you subscribed to email/SMS marketing, you will still receive email/SMS notifications. If you want to cancel your subscription, you may click the unsubscribe link at the bottom of any email message from the Company, or follow the SMS cancellation instructions.
+
+
+Minors' Privacy
+
+The Company takes minors' privacy seriously. If you are under 18 years of age, please do not submit any Personal Data through our Site without the express consent and participation of a parent or guardian. If you are a parent or guardian, please make every effort to guard your children's privacy.
+
+
+Applicable Law and Rights
+
+The Company upholds compliance with Republic Act No. 10173 or the Data Privacy Act of 2012 (DPA), its Implementing Rules and Regulations, and other relevant policies, including issuances of the National Privacy Commission. The Company acknowledges your right to be informed, object processing, access and rectify, suspend or withdraw Personal Data, and be indemnified in case of damages pursuant to the provisions of DPA.
+
+
+Changes to Privacy Policy
+
+The Company reserves the right to amend this Privacy Policy at any time, consistent with applicable law. In such case, an updated version will be posted in our Site. If we are going to process your Personal Data in a manner different from that stated at the time of collection, we will notify you, and you will have a choice as to whether or not we can use your Personal Data in such a way.
+
+
+Contact Us
+
+For any inquiry regarding this Privacy Policy, please contact our Data Privacy Officer, Mr. Luis Quiogue at:
+
+
+
+OMMC
+Ramcar Center
+80-82 Roces Ave.
+Diliman, Quezon City PH
+(+632) 370-1100
+
diff --git a/src/Controller/APIController.php b/src/Controller/APIController.php
index 85016459..03216a17 100644
--- a/src/Controller/APIController.php
+++ b/src/Controller/APIController.php
@@ -227,6 +227,24 @@ class APIController extends Controller
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
@@ -253,6 +271,16 @@ class APIController extends Controller
$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);
+ }
+
$em->flush();
// response
@@ -317,6 +345,9 @@ class APIController extends Controller
->setLastName($req->request->get('last_name'))
->setConfirmed($this->session->isConfirmed());
+ // update mobile phone of customer
+ $cust->setPhoneMobile(substr(2, $this->session->getPhoneNumber()));
+
$em->flush();
return $res->getReturnResponse();
@@ -395,7 +426,8 @@ class APIController extends Controller
{
$vlist[] = [
'id' => $v->getID(),
- 'make' => $v->getMake() . ' ' . $v->getModelYearFrom() . '-' . $v->getModelYearTo(),
+ 'make' => trim($v->getMake() . ' ' . $v->getModelYearFormatted(false)),
+ // 'make' => $v->getMake() . ' ' . $v->getModelYearFrom() . '-' . $v->getModelYearTo(),
];
}
@@ -1197,6 +1229,11 @@ class APIController extends Controller
->setJobOrder($jo)
->setRating($rating_num);
+ // rider rating comment
+ $comment = $req->request->get('comment');
+ if (!empty($comment))
+ $rating->setComment($comment);
+
$em->persist($rating);
$em->flush();
@@ -1364,4 +1401,63 @@ class APIController extends Controller
// response
return $res->getReturnResponse();
}
+
+ public function resendCode(Request $req)
+ {
+ $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
+
+ 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
+ $cust->setPrivacyThirdParty($req->request->get('priv_third_party'))
+ ->setPrivacyPromo($req->request->get('priv_promo'));
+
+ $em->flush();
+
+ return $res->getReturnResponse();
+ }
}
diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php
index c515c345..e25be13c 100644
--- a/src/Controller/JobOrderController.php
+++ b/src/Controller/JobOrderController.php
@@ -1364,15 +1364,20 @@ class JobOrderController extends BaseController
protected function sendEvent(JobOrder $job_order, $payload)
{
- $session = $job_order->getCustomer()->getMobileSessions();
- if (count($session) == 0)
+ $sessions = $job_order->getCustomer()->getMobileSessions();
+ if (count($sessions) == 0)
return;
- $phone_num = $session[0]->getPhoneNumber();
- $channel = 'motolite.control.' . $phone_num;
$client = new MosquittoClient();
$client->connect('localhost', 1883);
- $client->publish($channel, json_encode($payload));
+
+ foreach ($sessions as $sess)
+ {
+ $phone_num = $sess->getPhoneNumber();
+ $channel = 'motolite.control.' . $phone_num;
+ $client->publish($channel, json_encode($payload));
+ }
+
$client->disconnect();
}
@@ -1783,6 +1788,14 @@ class JobOrderController extends BaseController
// save
$em->flush();
+ // send mobile app event
+ $payload = [
+ 'event' => 'cancelled',
+ 'reason' => $cancel_reason,
+ 'jo_id' => $obj->getID(),
+ ];
+ $this->sendEvent($obj, $payload);
+
// return successful response
return $this->json([
'success' => 'Job order has been cancelled!'
diff --git a/src/Controller/RAPIController.php b/src/Controller/RAPIController.php
new file mode 100644
index 00000000..4dab01ff
--- /dev/null
+++ b/src/Controller/RAPIController.php
@@ -0,0 +1,453 @@
+session = null;
+ }
+
+ 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(RiderSession::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_number',
+ 'device_push_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 RiderSession();
+ $sess->setPhoneNumber($req->request->get('phone_number'))
+ ->setDevicePushID($req->request->get('device_push_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();
+ }
+
+ public function login(Request $req, EncoderFactoryInterface $ef)
+ {
+ $required_params = [
+ 'user',
+ 'pass',
+ ];
+ $em = $this->getDoctrine()->getManager();
+ $res = $this->checkParamsAndKey($req, $em, $required_params);
+ if ($res->isError())
+ return $res->getReturnResponse();
+
+ // check if session has a rider already
+ if ($this->session->hasRider())
+ {
+ $res->setError(true)
+ ->setErrorMessage('Another rider is already logged in. Please logout first.');
+ return $res->getReturnResponse();
+ }
+
+ // look for rider with username
+ $rider = $em->getRepository(Rider::class)->findOneBy(['username' => $req->request->get('user')]);
+ if ($rider == null)
+ {
+ $res->setError(true)
+ ->setErrorMessage('Invalid username or password.');
+ return $res->getReturnResponse();
+ }
+
+ // check if rider password is correct
+ $encoder = $ef->getEncoder(new User());
+ if (!$encoder->isPasswordValid($rider->getPassword(), $req->request->get('pass'), ''))
+ {
+ $res->setError(true)
+ ->setErrorMessage('Invalid username or password.');
+ return $res->getReturnResponse();
+ }
+
+ // assign rider to session
+ $this->session->setRider($rider);
+
+ // TODO: log rider logging in
+
+ $em->flush();
+
+ return $res->getReturnResponse();
+ }
+
+ public function logout(Request $req)
+ {
+ $required_params = [];
+ $em = $this->getDoctrine()->getManager();
+ $res = $this->checkParamsAndKey($req, $em, $required_params);
+ if ($res->isError())
+ return $res->getReturnResponse();
+
+ // remove rider from session
+ $this->session->setRider(null);
+
+ // TODO: log rider logging out
+
+ $em->flush();
+
+ return $res->getReturnResponse();
+ }
+
+ public function getJobOrder(Request $req)
+ {
+ // get the job order of the rider assigned to this session
+ $required_params = [];
+ $em = $this->getDoctrine()->getManager();
+ $res = $this->checkParamsAndKey($req, $em, $required_params);
+ if ($res->isError())
+ return $res->getReturnResponse();
+
+ // are we logged in?
+ if (!$this->session->hasRider())
+ {
+ $res->setError(true)
+ ->setErrorMessage('No logged in rider.');
+ return $res->getReturnResponse();
+ }
+
+ $rider = $this->session->getRider();
+
+ // do we have a job order?
+ $jo = $rider->getActiveJobOrder();
+ if ($jo == null)
+ {
+ $data = [
+ 'job_order' => null
+ ];
+ }
+ else
+ {
+ $coord = $jo->getCoordinates();
+ $cust = $jo->getCustomer();
+ $cv = $jo->getCustomerVehicle();
+ $v = $cv->getVehicle();
+ $inv = $jo->getInvoice();
+
+ // invoice items
+ $inv_items = [];
+ foreach ($inv->getItems() as $item)
+ {
+ $inv_items[] = [
+ 'id' => $item->getID(),
+ 'title' => $item->getTitle(),
+ 'qty' => $item->getQuantity(),
+ 'price' => $item->getPrice(),
+ ];
+ }
+
+ $data = [
+ 'job_order' => [
+ 'id' => $jo->getID(),
+ 'service_type' => $jo->getServiceType(),
+ 'date_schedule' => $jo->getDateSchedule()->format('Ymd'),
+ 'longitude' => $coord->getLongitude(),
+ 'latitude' => $coord->getLatitude(),
+ 'status' => $jo->getStatus(),
+ 'customer' => [
+ 'title' => $cust->getTitle(),
+ 'first_name' => $cust->getFirstName(),
+ 'last_name' => $cust->getLastName(),
+ 'phone_mobile' => $cust->getPhoneMobile(),
+ ],
+ 'vehicle' => [
+ 'manufacturer' => $v->getManufacturer()->getName(),
+ 'make' => $v->getMake(),
+ 'model' => $cv->getModelYear(),
+ 'plate_number' => $cv->getPlateNumber(),
+ 'color' => $cv->getColor(),
+ ],
+ 'delivery_instructions' => $jo->getDeliveryInstructions(),
+ 'delivery_address' => $jo->getDeliveryAddress(),
+ 'landmark' => $jo->getLandmark(),
+ 'invoice' => [
+ 'discount' => $inv->getDiscount(),
+ 'trade_in' => $inv->getTradeIn(),
+ 'total_price' => $inv->getTotalPrice(),
+ 'vat' => $inv->getVat(),
+ 'items' => $inv_items,
+ ],
+ 'mode_of_payment' => $jo->getModeOfPayment(),
+
+
+ ]
+ ];
+ }
+
+ $res->setData($data);
+
+ return $res->getReturnResponse();
+ }
+
+ protected function checkJO(Request $req, $required_params)
+ {
+ // set jo status to in transit
+ $em = $this->getDoctrine()->getManager();
+ $res = $this->checkParamsAndKey($req, $em, $required_params);
+ if ($res->isError())
+ return $res;
+
+ // are we logged in?
+ if (!$this->session->hasRider())
+ {
+ $res->setError(true)
+ ->setErrorMessage('No logged in rider.');
+ return $res;
+ }
+
+ $rider = $this->session->getRider();
+
+ // check if we have an active JO
+ $jo = $rider->getActiveJobOrder();
+ if ($jo == null)
+ {
+ $res->setError(true)
+ ->setErrorMessage('No active job order.');
+ return $res;
+ }
+
+ // check if the jo_id sent is the same as our active jo
+ if ($req->request->get('jo_id') != $jo->getID())
+ {
+ $res->setError(true)
+ ->setErrorMessage('Job order selected is not active job order.');
+ return $res;
+ }
+
+ return $res;
+ }
+
+ public function acceptJobOrder(Request $req)
+ {
+ $required_params = ['jo_id'];
+ $res = $this->checkJO($req, $required_params);
+ if ($res->isError())
+ return $res->getReturnResponse();
+
+ // TODO: refactor this into a jo handler class, so we don't have to repeat for control center
+
+ // set jo status to in transit
+ $jo->setStatus(JOStatus::IN_TRANSIT);
+
+ // TODO: send mqtt event
+
+ // TODO: add event
+
+ return $res->getReturnResponse();
+ }
+
+ public function cancelJobOrder(Request $req)
+ {
+ $required_params = ['jo_id'];
+ $res = $this->checkJO($req, $required_params);
+ if ($res->isError())
+ return $res->getReturnResponse();
+
+ // TODO: refactor this into a jo handler class, so we don't have to repeat for control center
+
+ // set jo status to cancelled
+ $jo->setStatus(JOStatus::CANCELLED);
+
+ // TODO: send mqtt event
+
+ // TODO: add event
+
+ return $res->getReturnResponse();
+ }
+
+ public function arrive(Request $req)
+ {
+ $required_params = ['jo_id'];
+ $res = $this->checkJO($req, $required_params);
+ if ($res->isError())
+ return $res->getReturnResponse();
+
+ // TODO: refactor this into a jo handler class, so we don't have to repeat for control center
+
+ // set jo status to in progress
+ $jo->setStatus(JOStatus::IN_PROGRESS);
+
+ // TODO: send mqtt event
+
+ // TODO: add event
+
+ return $res->getReturnResponse();
+ }
+
+ public function payment(Request $req)
+ {
+ // set invoice to paid
+
+ // set jo status to fulfilled
+ }
+}
diff --git a/src/Controller/RiderController.php b/src/Controller/RiderController.php
index 84e2f21d..16b9bb8c 100644
--- a/src/Controller/RiderController.php
+++ b/src/Controller/RiderController.php
@@ -7,6 +7,7 @@ use App\Ramcar\DayOfWeek;
use App\Entity\Rider;
use App\Entity\RiderSchedule;
use App\Entity\Hub;
+use App\Entity\User;
use App\Service\FileUploader;
use Doctrine\ORM\Query;
@@ -14,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
+use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use DateTime;
@@ -154,7 +156,8 @@ class RiderController extends BaseController
->setContactNumber($req->request->get('contact_no'))
->setPlateNumber($req->request->get('plate_number'))
->setImageFile($req->request->get('image_file'))
- ->setActive($req->request->get('flag_active') ? true : false);
+ ->setActive($req->request->get('flag_active') ? true : false)
+ ->setUsername($req->request->get('username'));
}
public function addSubmit(Request $req, EncoderFactoryInterface $ef, ValidatorInterface $validator)
@@ -176,6 +179,24 @@ class RiderController extends BaseController
// initialize error list
$error_array = [];
+ // get password inputs
+ $password = $req->request->get('password');
+ $confirm_password = $req->request->get('confirm_password');
+
+ // custom validation for password fields
+ if (!$password) {
+ $error_array['password'] = 'This value should not be blank.';
+ } else if ($password != $confirm_password) {
+ $error_array['confirm_password'] = 'Passwords do not match.';
+ } else {
+ // encode password
+ $enc = $ef->getEncoder(new User());
+ $encoded_password = $enc->encodePassword($req->request->get('password'), '');
+
+ // set password
+ $obj->setPassword($encoded_password);
+ }
+
// custom validation for associations
$hub_id = $req->request->get('hub');
@@ -303,6 +324,24 @@ class RiderController extends BaseController
// initialize error list
$error_array = [];
+ // get password inputs
+ $password = $req->request->get('password');
+ $confirm_password = $req->request->get('confirm_password');
+
+ // custom validation for password fields
+ if ($password || $confirm_password) {
+ if ($password != $confirm_password) {
+ $error_array['confirm_password'] = 'Passwords do not match.';
+ } else {
+ // encode password
+ $enc = $ef->getEncoder(new User());
+ $encoded_password = $enc->encodePassword($req->request->get('password'), '');
+
+ // set password
+ $obj->setPassword($encoded_password);
+ }
+ }
+
// custom validation for associations
$hub_id = $req->request->get('hub');
diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php
index 641b188c..3390064d 100644
--- a/src/Entity/Customer.php
+++ b/src/Entity/Customer.php
@@ -131,6 +131,16 @@ class Customer
*/
protected $email;
+ /**
+ * @ORM\Column(type="boolean")
+ */
+ protected $priv_third_party;
+
+ /**
+ * @ORM\Column(type="boolean")
+ */
+ protected $priv_promo;
+
public function __construct()
{
$this->numbers = new ArrayCollection();
@@ -152,6 +162,9 @@ class Customer
$this->phone_fax = '';
$this->email = '';
+
+ $this->priv_third_party = 0;
+ $this->priv_promo = 0;
}
public function getID()
@@ -379,4 +392,26 @@ class Customer
{
return $this->email;
}
+
+ public function setPrivacyThirdParty($bool = true)
+ {
+ $this->priv_third_party = $bool;
+ return $this;
+ }
+
+ public function getPrivacyThirdParty()
+ {
+ return $this->priv_third_party;
+ }
+
+ public function setPrivacyPromo($bool = true)
+ {
+ $this->priv_promo = $bool;
+ return $this;
+ }
+
+ public function getPrivacyPromo()
+ {
+ return $this->priv_promo;
+ }
}
diff --git a/src/Entity/CustomerVehicle.php b/src/Entity/CustomerVehicle.php
index aec185b1..01aff142 100644
--- a/src/Entity/CustomerVehicle.php
+++ b/src/Entity/CustomerVehicle.php
@@ -167,13 +167,16 @@ class CustomerVehicle
// remove spaces
$plate_number = str_replace(' ', '', $plate_number);
+ // upper case
+ $plate_number = strtoupper($plate_number);
+
$this->plate_number = $plate_number;
return $this;
}
public function getPlateNumber()
{
- return $this->plate_number;
+ return strtoupper($this->plate_number);
}
public function setModelYear($model_year)
diff --git a/src/Entity/MobileSession.php b/src/Entity/MobileSession.php
index 460ef562..c11986f4 100644
--- a/src/Entity/MobileSession.php
+++ b/src/Entity/MobileSession.php
@@ -79,6 +79,12 @@ class MobileSession
*/
protected $date_confirmed;
+ // date and time that the confirmation code was last sent
+ /**
+ * @ORM\Column(type="datetime", nullable=true)
+ */
+ protected $date_code_sent;
+
public function __construct()
{
@@ -88,6 +94,7 @@ class MobileSession
$this->customer = null;
$this->confirm_flag = false;
$this->date_confirmed = null;
+ $this->date_code_sent = null;
}
public function generateKeyID()
@@ -204,4 +211,15 @@ class MobileSession
{
return $this->date_confirmed;
}
+
+ public function setDateCodeSent(DateTime $date)
+ {
+ $this->date_code_sent = $date;
+ return $this;
+ }
+
+ public function getDateCodeSent()
+ {
+ return $this->date_code_sent;
+ }
}
diff --git a/src/Entity/Rider.php b/src/Entity/Rider.php
index d637bc99..02bca560 100644
--- a/src/Entity/Rider.php
+++ b/src/Entity/Rider.php
@@ -8,6 +8,8 @@ use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
+use App\Ramcar\JOStatus;
+
/**
* @ORM\Entity
* @ORM\Table(name="rider")
@@ -92,6 +94,18 @@ class Rider
*/
protected $flag_active;
+ // username for rider api
+ /**
+ * @ORM\Column(type="string", length=80, unique=true, nullable=true)
+ */
+ protected $username;
+
+ // password for rider api
+ /**
+ * @ORM\Column(type="string", length=64)
+ */
+ protected $password;
+
public function __construct()
{
$this->job_orders = new ArrayCollection();
@@ -99,6 +113,8 @@ class Rider
$this->curr_rating = 0;
$this->flag_available = true;
$this->flag_active = true;
+ $this->username = null;
+ $this->password = '';
}
public function getID()
@@ -253,4 +269,42 @@ class Rider
{
return $this->flag_active;
}
+
+ public function setUsername($username)
+ {
+ $this->username = $username;
+ return $this;
+ }
+
+ public function getUsername()
+ {
+ return $this->username;
+ }
+
+ public function setPassword($pass)
+ {
+ // they have to pass the encoded password
+ $this->password = $pass;
+ return $this;
+ }
+
+ public function getPassword()
+ {
+ return $this->password;
+ }
+
+ public function getActiveJobOrder()
+ {
+ $active_status = [
+ JOStatus::ASSIGNED,
+ JOStatus::IN_TRANSIT,
+ JOStatus::IN_PROGRESS,
+ ];
+
+ $criteria = Criteria::create();
+ $criteria->where(Criteria::expr()->in('status', $active_status))
+ ->getFirstResult(1);
+
+ return $this->job_orders->matching($criteria)[0];
+ }
}
diff --git a/src/Entity/RiderRating.php b/src/Entity/RiderRating.php
index e0a03ddc..f5875c27 100644
--- a/src/Entity/RiderRating.php
+++ b/src/Entity/RiderRating.php
@@ -52,10 +52,17 @@ class RiderRating
*/
protected $rating;
+ // customer's comment that goes along with the rating
+ /**
+ * @ORM\Column(type="text")
+ */
+ protected $comment;
+
public function __construct()
{
$this->date_create = new DateTime();
$this->rating = 0;
+ $this->comment = '';
}
public function getID()
@@ -111,4 +118,15 @@ class RiderRating
{
return $this->rating;
}
+
+ public function setComment($comment)
+ {
+ $this->comment = $comment;
+ return $this;
+ }
+
+ public function getComment()
+ {
+ return $this->comment;
+ }
}
diff --git a/src/Entity/RiderSession.php b/src/Entity/RiderSession.php
new file mode 100644
index 00000000..3e6fc0a7
--- /dev/null
+++ b/src/Entity/RiderSession.php
@@ -0,0 +1,117 @@
+id = $this->generateKeyID();
+ $this->rider = null;
+ $this->is_active = true;
+ }
+
+ public function generateKeyID()
+ {
+ // use uniqid for now, since primary key dupes will trigger exceptions
+ return uniqid();
+ }
+
+ public function getID()
+ {
+ return $this->id;
+ }
+
+ public function setDevicePushID($id)
+ {
+ $this->device_push_id = $id;
+ return $this;
+ }
+
+ public function getDevicePushID()
+ {
+ return $this->device_push_id;
+ }
+
+ public function setRider(Rider $rider = null)
+ {
+ $this->rider = $rider;
+ return $this;
+ }
+
+ public function getRider()
+ {
+ return $this->rider;
+ }
+
+ public function setPhoneNumber($num)
+ {
+ $this->phone_number = $num;
+ return $this;
+ }
+
+ public function getPhoneNumber()
+ {
+ return $this->phone_number;
+ }
+
+ public function setActive($flag = true)
+ {
+ $this->is_active = $flag;
+ return $this;
+ }
+
+ public function isActive()
+ {
+ return $this->is_active;
+ }
+
+ public function hasRider()
+ {
+ if ($this->rider == null)
+ return false;
+
+ return true;
+ }
+}
diff --git a/src/Entity/Vehicle.php b/src/Entity/Vehicle.php
index 05e05897..f751b473 100644
--- a/src/Entity/Vehicle.php
+++ b/src/Entity/Vehicle.php
@@ -114,12 +114,17 @@ class Vehicle
return $this->model_year_to;
}
- public function getModelYearFormatted()
+ public function getModelYearFormatted($has_dash = true)
{
if ($this->model_year_from == 0)
{
if ($this->model_year_to == 0)
- return '-';
+ {
+ if ($has_dash)
+ return '-';
+
+ return '';
+ }
return $this->model_year_to;
}
diff --git a/src/Entity/VehicleManufacturer.php b/src/Entity/VehicleManufacturer.php
index 48de14b0..00026b05 100644
--- a/src/Entity/VehicleManufacturer.php
+++ b/src/Entity/VehicleManufacturer.php
@@ -30,6 +30,7 @@ class VehicleManufacturer
// vehicles
/**
* @ORM\OneToMany(targetEntity="Vehicle", mappedBy="manufacturer")
+ * @ORM\OrderBy({"make" = "ASC"})
*/
protected $vehicles;
diff --git a/src/Ramcar/FuelType.php b/src/Ramcar/FuelType.php
index c85a4d81..62f2c13a 100644
--- a/src/Ramcar/FuelType.php
+++ b/src/Ramcar/FuelType.php
@@ -4,11 +4,13 @@ namespace App\Ramcar;
class FuelType extends NameValue
{
- const GAS = 'gas';
- const DIESEL = 'diesel';
+ const GAS = 'gas';
+ const DIESEL = 'diesel';
+ const LPG = 'lpg';
const COLLECTION = [
'gas' => 'Gas',
'diesel' => 'Diesel',
+ 'lpg' => 'LPG',
];
}
diff --git a/src/Service/MapTools.php b/src/Service/MapTools.php
index 693b3c21..aa38cd6d 100644
--- a/src/Service/MapTools.php
+++ b/src/Service/MapTools.php
@@ -52,7 +52,12 @@ class MapTools
// query google maps api
- $res = $client->request('GET', $maps_url, ['query' => $gmaps_params]);
+ $res = $client->request('GET', $maps_url, [
+ 'query' => $gmaps_params,
+ 'curl' => [
+ CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4
+ ],
+ ]);
return $res->getBody();
}
diff --git a/templates/rider/form.html.twig b/templates/rider/form.html.twig
index c6501ca9..c08c250c 100644
--- a/templates/rider/form.html.twig
+++ b/templates/rider/form.html.twig
@@ -35,6 +35,36 @@