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 ($check == null) $missing[] = $param; } else if ($req->getMethod() == 'POST') { $check = $req->request->get($param); if ($check == null) $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(); $promo = $inv->getPromo(); // 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, ]; } // promo if ($promo != null) { $promo_data = [ 'id' => $promo->getID(), 'name' => $promo->getName(), 'code' => $promo->getCode(), 'discount_rate' => $promo->getDiscountRate(), 'discount_apply' => $promo->getDiscountApply(), ]; } else { $promo_data = null; } $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(), 'promo' => $promo_data, ] ]; } $res->setData($data); return $res->getReturnResponse(); } protected function checkJO(Request $req, $required_params, &$jo = null) { // 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, $jo); 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, $jo); 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, $jo); 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 } public function getPromos(Request $req) { $em = $this->getDoctrine()->getManager(); $required_params = []; $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $promos = $em->getRepository(Promo::class)->findAll(); $promo_data = []; foreach ($promos as $promo) { $promo_data[] = [ 'id' => $promo->getID(), 'name' => $promo->getName(), 'code' => $promo->getCode(), ]; } $data = [ 'promos' => $promo_data, ]; $res->setData($data); return $res->getReturnResponse(); } public function getBatteries(Request $req) { // get batteries, models, and sizes $em = $this->getDoctrine()->getManager(); $required_params = []; $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $batts = $em->getRepository(Battery::class)->findAll(); $models = $em->getRepository(BatteryModel::class)->findAll(); $sizes = $em->getRepository(BatterySize::class)->findAll(); $batt_data = []; foreach ($batts as $batt) { $batt_data[] = [ 'id' => $batt->getID(), 'model_id' => $batt->getModel()->getID(), 'size_id' => $batt->getSize()->getID(), 'sell_price' => $batt->getSellingPrice(), ]; } $model_data = []; foreach ($models as $model) { $model_data[] = [ 'id' => $model->getID(), 'name' => $model->getName(), ]; } $size_data = []; foreach ($sizes as $size) { $size_data[] = [ 'id' => $size->getID(), 'name' => $size->getName(), ]; } $data = [ 'batteries' => $batt_data, 'models' => $model_data, 'sizes' => $size_data, ]; $res->setData($data); return $res->getReturnResponse(); } public function changeService(Request $req, InvoiceCreator $ic) { // allow rider to change service, promo, battery and trade-in options $em = $this->getDoctrine()->getManager(); $required_params = ['jo_id', 'stype_id', 'promo_id', 'batt_id', 'trade_in']; $res = $this->checkJO($req, $required_params, $jo); if ($res->isError()) return $res->getReturnResponse(); // check service type $stype_id = $req->request->get('stype_id'); if (!ServiceType::validate($stype_id)) { $res->setError(true) ->setErrorMessage('Invalid service type - ' . $stype_id); return $res->getReturnResponse(); } // check promo id $promo_id = $req->request->get('promo_id'); // no promo if ($promo_id == 0) $promo = null; else { $promo = $em->getRepository(Promo::class)->find($promo_id); if ($promo == null) { $res->setError(true) ->setErrorMessage('Invalid promo id - ' . $promo_id); return $res->getReturnResponse(); } } // check battery id $batt_id = $req->request->get('batt_id'); // no battery if ($batt_id == 0) $battery = null; else { $battery = $em->getRepository(Battery::class)->find($batt_id); if ($battery == null) { $res->setError(true) ->setErrorMessage('Invalid battery id - ' . $batt_id); return $res->getReturnResponse(); } } // check trade in $trade_in = $req->request->get('trade_in'); if (!TradeInType::validate($trade_in)) $trade_in = null; // generate new invoice $crit = new InvoiceCriteria(); $crit->setServiceType($stype_id); if ($promo != null) $crit->addPromo($promo); if ($battery != null) { $crit->addEntry($battery, $trade_in, 1); error_log('adding entry for battery - ' . $battery->getID()); } $invoice = $ic->processCriteria($crit); $invoice->setStatus(InvoiceStatus::DRAFT); // remove previous invoice $old_invoice = $jo->getInvoice(); $em->remove($old_invoice); $em->flush(); // save job order $jo->setServiceType($stype_id); // save invoice $jo->setInvoice($invoice); $em->persist($invoice); $em->flush(); // TODO: add event // TODO: send mqtt event (?) return $res->getReturnResponse(); } }