resq/src/Service/JobOrderHandler/ResqJobOrderHandler.php
2024-07-09 16:55:58 +08:00

4385 lines
160 KiB
PHP

<?php
namespace App\Service\JobOrderHandler;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\PessimisticLockException;
use App\Entity\JobOrder;
use App\Entity\BatteryManufacturer;
use App\Entity\JOEvent;
use App\Entity\CustomerVehicle;
use App\Entity\Hub;
use App\Entity\Promo;
use App\Entity\Rider;
use App\Entity\JORejection;
use App\Entity\Warranty;
use App\Entity\Customer;
use App\Entity\CustomerTag;
use App\Entity\EmergencyType;
use App\Entity\OwnershipType;
use App\Entity\CustomerLocation;
use App\Entity\Battery;
use App\Ramcar\ServiceType;
use App\Ramcar\TradeInType;
use App\Ramcar\JOEventType;
use App\Ramcar\JOStatus;
use App\Ramcar\InvoiceCriteria;
use App\Ramcar\WarrantyClass;
use App\Ramcar\DiscountApply;
use App\Ramcar\ModeOfPayment;
use App\Ramcar\TransactionOrigin;
use App\Ramcar\FacilitatedType;
use App\Ramcar\JORejectionReason;
use App\Ramcar\CustomerNotWaitReason;
use App\Ramcar\NoTradeInReason;
use App\Ramcar\WillingToWaitContent;
use App\Ramcar\WarrantySource;
use App\Ramcar\HubCriteria;
use App\Ramcar\DeliveryStatus;
use App\Ramcar\SourceOfAwareness;
use App\Ramcar\InitialConcern;
use App\Ramcar\CustomerClassification;
use App\Ramcar\Gender;
use App\Ramcar\CallerClassification;
use App\Service\InvoiceGeneratorInterface;
use App\Service\JobOrderHandlerInterface;
use App\Service\RiderAssignmentHandlerInterface;
use App\Service\WarrantyHandler;
use App\Service\MQTTClient;
use App\Service\MQTTClientApiv2;
use App\Service\FCMSender;
use App\Service\APNSClient;
use App\Service\MapTools;
use App\Service\RisingTideGateway;
use App\Service\PromoLogger;
use App\Service\HubSelector;
use App\Service\HubDistributor;
use App\Service\HubFilteringGeoChecker;
use App\Service\JobOrderManager;
use App\Service\PriceTierManager;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
use Mosquitto\Client as MosquittoClient;
use DateTime;
use DateInterval;
use FPDF;
class ResqJobOrderHandler implements JobOrderHandlerInterface
{
protected $em;
protected $ic;
protected $security;
protected $validator;
protected $translator;
protected $rah;
protected $country_code;
protected $wh;
protected $rt;
protected $promo_logger;
protected $hub_dist;
protected $hub_geofence;
protected $cust_distance_limit;
protected $hub_filter_enabled;
protected $jo_manager;
protected $pt_manager;
protected $template_hash;
public function __construct(Security $security, EntityManagerInterface $em,
InvoiceGeneratorInterface $ic, ValidatorInterface $validator,
TranslatorInterface $translator, RiderAssignmentHandlerInterface $rah,
string $country_code, WarrantyHandler $wh, RisingTideGateway $rt,
PromoLogger $promo_logger, HubDistributor $hub_dist, HubFilteringGeoChecker $hub_geofence,
string $cust_distance_limit, string $hub_filter_enabled, JobOrderManager $jo_manager, PriceTierManager $pt_manager)
{
$this->em = $em;
$this->ic = $ic;
$this->security = $security;
$this->validator = $validator;
$this->translator = $translator;
$this->rah = $rah;
$this->country_code = $country_code;
$this->wh = $wh;
$this->rt = $rt;
$this->promo_logger = $promo_logger;
$this->hub_dist = $hub_dist;
$this->hub_geofence = $hub_geofence;
$this->cust_distance_limit = $cust_distance_limit;
$this->hub_filter_enabled = $hub_filter_enabled;
$this->jo_manager = $jo_manager;
$this->pt_manager = $pt_manager;
$this->loadTemplates();
}
// get job order rows
public function getRows(Request $req, $tier)
{
// check which job order tier is being called for and confirm access
$tier_params = $this->checkTier($tier);
// get current user
$user = $this->security->getUser();
if ($user == null)
throw new AccessDeniedHttpException('No access.');
$hubs = $user->getHubs();
// get query builder
$qb = $this->em->getRepository(JobOrder::class)
->createQueryBuilder('q');
// get datatable params
$datatable = $req->request->get('datatable');
// count total records
$tquery = $qb->select('COUNT(q)');
$this->setQueryFilters($datatable, $tquery, $qb, $hubs, $tier, $tier_params['jo_status']);
$total = $tquery->getQuery()
->getSingleScalarResult();
// get current page number
$page = $datatable['pagination']['page'] ?? 1;
$perpage = $datatable['pagination']['perpage'];
$offset = ($page - 1) * $perpage;
// add metadata
$meta = [
'page' => $page,
'perpage' => $perpage,
'pages' => ceil($total / $perpage),
'total' => $total,
'sort' => 'asc',
'field' => 'id'
];
// build query
$qb = $this->em->getRepository(JobOrder::class)
->createQueryBuilder('q');
$query = $qb->select('q');
$this->setQueryFilters($datatable, $query, $qb, $hubs, $tier, $tier_params['jo_status']);
// check if sorting is present, otherwise use default
if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) {
$order = $datatable['sort']['sort'] ?? 'asc';
$query->orderBy('q.' . $datatable['sort']['field'], $order);
} else {
$query->orderBy('q.date_schedule', 'asc');
}
// get rows for this page
$query_obj = $query->setFirstResult($offset)
->setMaxResults($perpage)
->getQuery();
// error_log($query_obj->getSQL());
$obj_rows = $query_obj->getResult();
$statuses = JOStatus::getCollection();
$service_types = ServiceType::getCollection();
// process rows
$rows = [];
foreach ($obj_rows as $orow) {
$is_vip = false;
$is_emergency = false;
// check if customer is vip
$cust_class = $orow->getCustomer()->getCustomerClassification();
if ($cust_class == CustomerClassification::VIP)
$is_vip = true;
// check if customer is not willing to wait
$will_not_wait = $orow->getWillWait();
if ($will_not_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// check if reason is emergency or rush
$reason = $orow->getReasonNotWait();
if (($reason == CustomerNotWaitReason::EMERGENCY) ||
($reason == CustomerNotWaitReason::RUSH_REQUEST))
$is_emergency = true;
}
// add row data
$row['id'] = $orow->getID();
$row['customer_name'] = $orow->getCustomer()->getFirstName() . ' ' . $orow->getCustomer()->getLastName();
$row['delivery_address'] = $orow->getDeliveryAddress();
$row['date_schedule'] = $orow->getDateSchedule()->format("d M Y g:i A");
$row['type'] = $orow->isAdvanceOrder() ? 'Advanced Order' : 'Immediate';
$row['service_type'] = $service_types[$orow->getServiceType()];
$row['status'] = $statuses[$orow->getStatus()];
$row['flag_advance'] = $orow->isAdvanceOrder();
$row['plate_number'] = $orow->getCustomerVehicle()->getPlateNumber();
$row['is_mobile'] = $orow->getSource() == TransactionOrigin::MOBILE_APP;
$row['is_vip'] = $is_vip;
$row['is_emergency'] = $is_emergency;
$row['flag_cust_new'] = $orow->isCustNew();
$row['date_assign'] = !empty($orow->getDateAssign()) ? $orow->getDateAssign()->format("c") : null;
$row['date_fulfill'] = !empty($orow->getDateFulfill()) ? $orow->getDateFulfill()->format("c") : null;
$processor = $orow->getProcessedBy();
if ($processor == null)
$row['processor'] = '';
else
$row['processor'] = $orow->getProcessedBy()->getFullName();
$assignor = $orow->getAssignedBy();
if ($assignor == null)
$row['assignor'] = '';
else
$row['assignor'] = $orow->getAssignedBy()->getFullName();
// not removing this since this might be used by other pages since getRows is also used by other pages
$hub_facilitated = $orow->getFacilitatedBy();
if ($hub_facilitated == null)
$row['hub_facilitated'] = '';
else
$row['hub_facilitated'] = $orow->getFacilitatedBy()->getName();
// get the assigned hub, if any
$assigned_hub = $orow->getHub();
if ($assigned_hub == null)
$row['assigned_hub'] = '';
else
$row['assigned_hub'] = $orow->getHub()->getName();
$rows[] = $row;
}
$params['meta'] = $meta;
$params['rows'] = $rows;
$params['tier_params'] = $tier_params;
return $params;
}
// get job orders
public function getJobOrders(Request $req)
{
// get search term
$term = $req->query->get('search');
// get querybuilder
$qb = $this->em->getRepository(JobOrder::class)
->createQueryBuilder('q');
// build expression now since we're reusing it
$jo_label = $qb->expr()->concat($qb->expr()->literal('#'), 'q.id', $qb->expr()->literal(' - '), 'c.first_name', $qb->expr()->literal(' '), 'c.last_name', $qb->expr()->literal(' (Plate No: '), 'v.plate_number', $qb->expr()->literal(')'));
// count total records
$tquery = $qb->select('COUNT(q)')
->join('q.customer', 'c')
->join('q.cus_vehicle', 'v');
// add filters to count query
if (!empty($term)) {
$tquery->where($jo_label . ' LIKE :filter')
->setParameter('filter', '%' . $term . '%');
}
$total = $tquery->getQuery()
->getSingleScalarResult();
// pagination vars
$page = $req->query->get('page') ?? 1;
$perpage = 20;
$offset = ($page - 1) * $perpage;
$pages = ceil($total / $perpage);
$has_more_pages = $page < $pages ? true : false;
// build main query
$query = $qb->select('q')
->addSelect($jo_label . ' as jo_label')
->addSelect('c.first_name as cust_first_name')
->addSelect('c.last_name as cust_last_name')
->addSelect('v.plate_number as vehicle_plate_number');
// add filters if needed
if (!empty($term)) {
$query->where($jo_label . ' LIKE :filter')
->setParameter('filter', '%' . $term . '%');
}
// get rows
$obj_rows = $query->orderBy('q.id', 'asc')
->setFirstResult($offset)
->setMaxResults($perpage)
->getQuery()
->getResult();
// build job order array
$job_orders = [];
foreach ($obj_rows as $jo) {
$service_type = ServiceType::getName($jo[0]->getServiceType());
$job_orders[] = [
'id' => $jo[0]->getID(),
'text' => $jo['jo_label'] . ' - ' . $service_type
];
}
$params['job_orders'] = $job_orders;
$params['has_more_pages'] = $has_more_pages;
return $params;
}
// creates job order
public function generateJobOrder(Request $req, $id)
{
// initialize error list
$error_array = [];
$em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id);
// flag for new job order for the customer tags
$flag_new_jo = false;
if (empty($jo))
{
// new job order
$jo = new JobOrder();
$flag_new_jo = true;
}
// find customer
$cust_id = $req->request->get('cid');
$customer = $em->getRepository(Customer::class)->find($cust_id);
$flag_cust_new = false;
if (empty($customer))
{
$error_array['customer_vehicle'] = 'Invalid customer specified.';
}
else
{
// get email, dpa_consent, promo_sms, and promo_email, if set
// check for dpa access
$is_dpa_checked = true;
if ($this->security->isGranted('customer.dpa'))
$is_dpa_checked = $req->request->get('flag_dpa_consent', false);
// check if email marketing promo is checked
$is_email_promo_checked = $req->request->get('flag_promo_email');
if ($is_email_promo_checked)
{
// check email field
if (empty($req->request->get('customer_email')))
$error_array['customer_email'] = 'Email address required.';
}
$customer->setEmail($req->request->get('customer_email'))
->setPromoSms($req->request->get('flag_promo_sms', false))
->setPromoEmail($req->request->get('flag_promo_email', false))
->setResearchSms($req->request->get('flag_research_sms', false))
->setResearchEmail($req->request->get('flag_research_email', false))
->setDpaConsent($is_dpa_checked);
}
// check if customer has more than one job order already
$cust_jo_count = $this->jo_manager->getCustomerJobOrderCount($customer->getID());
if ($cust_jo_count <= 1)
$flag_cust_new = true;
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) {
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if customer vehicle is set
if (empty($req->request->get('customer_vehicle'))) {
$error_array['customer_vehicle'] = 'No vehicle selected.';
} else {
// get customer vehicle
$cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle'));
if (empty($cust_vehicle)) {
$error_array['customer_vehicle'] = 'Invalid vehicle specified.';
}
}
// check if landmark is set
//if (empty($req->request->get('landmark')))
// $error_array['landmark'] = 'Landmark is required.';
// check if customer is not willing to wait
$will_wait = $req->request->get('flag_willing_to_wait');
$reason = '';
$more_reason = '';
if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// get the reason and text
$reason = $req->request->get('no_wait_reason');
$more_reason = $req->request->get('not_wait_notes');
}
// check if service is battery sales
$stype = $req->request->get('service_type');
$no_trade_in_reason = '';
if ($stype == ServiceType::BATTERY_REPLACEMENT_NEW)
{
// check if trade in
$is_trade_in = $req->request->get('invoice_trade_in_type');
if (empty($is_trade_in))
{
$no_trade_in_reason = $req->request->get('no_trade_in_reason');
if (empty($no_trade_in_reason))
$error_array['no_trade_in_reason'] = 'No trade in reason required.';
}
}
// check if customer location is set
$cust_location_id = $req->request->get('cust_location', 0);
if ($cust_location_id == 0)
$error_array['cust_location'] = 'Customer location is required.';
else
{
// get customer location
$cust_location = $em->getRepository(CustomerLocation::class)->find($cust_location_id);
if ($cust_location == null)
$error_array['cust_location'] = 'Invalid customer location';
}
// get source of awareness if any
$soa_type = $req->request->get('source_of_awareness', '');
// get remarks
$remarks = $req->request->get('remarks', '');
// get initial concern if any
$initial_concern = $req->request->get('initial_concern', '');
if (empty($initial_concern))
$error_array['initial_concern'] = 'Initial concern of customer is required.';
// get initial concern notes if any
$initial_concern_notes = $req->request->get('initial_concern_notes', '');
if (empty($initial_concern_notes))
$error_array['initial_concern_notes'] = 'Notes on initial concern of customer is required.';
// get gender if any
$gender = $req->request->get('gender', '');
// get caller classification if any
$caller_class = $req->request->get('caller_class','');
// get emergency type if any
$etype_id = $req->request->get('emergency_type', 0);
$etype = $em->getRepository(EmergencyType::class)->find($etype_id);
// get ownership type if any
$ownertype_id = $req->request->get('ownership_type', 0);
$owner_type = $em->getRepository(OwnershipType::class)->find($ownertype_id);
// TODO: check status before saving since JO might already
// have a status that needs to be retained
if (empty($error_array)) {
// get current user
$user = $this->security->getUser();
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
// set and save values
$jo->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($stype)
->setWarrantyClass($req->request->get('warranty_class'))
->setCustomer($cust_vehicle->getCustomer())
->setCustomerVehicle($cust_vehicle)
->setSource($req->request->get('source'))
->setStatus(JOStatus::PENDING)
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setORName($req->request->get('or_name'))
->setPromoDetail($req->request->get('promo_detail'))
->setModeOfPayment($req->request->get('mode_of_payment'))
->setLandmark($req->request->get('landmark'))
->setWillWait($req->request->get('flag_willing_to_wait'))
->setReasonNotWait($reason)
->setNotWaitingNotes($more_reason)
->setNoTradeInReason($no_trade_in_reason)
->setSourceOfAwareness($soa_type)
->setRemarks($remarks)
->setInitialConcern($initial_concern)
->setInitialConcernNotes($initial_concern_notes)
->setCallerClassification($caller_class)
->setGender($gender)
->setEmergencyType($etype)
->setOwnershipType($owner_type)
->setCustomerLocation($cust_location)
->setCustNew($flag_cust_new);
// check if user is null, meaning call to create came from API
if ($user != null)
{
$jo->setCreatedBy($user);
}
// check if reference JO is set and validate
if (!empty($req->request->get('ref_jo'))) {
// get reference JO
$ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo'));
if (empty($ref_jo)) {
$error_array['ref_jo'] = 'Invalid reference job order specified.';
} else {
$jo->setReferenceJO($ref_jo);
}
}
// check service type if new battery
// check if new JO
if (($stype == ServiceType::BATTERY_REPLACEMENT_NEW) &&
($flag_new_jo))
{
// check if customer has customer tag promo
if (($customer->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) ||
($customer->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO')))
{
// if has customer tag, customer has not availed of promo, get the hub where customer is pre-registered
$car_club_hub = $customer->getCarClubCustomerHub();
if ($car_club_hub != null)
{
// assign hub, change the jo status
$hub = $car_club_hub->getHub();
$jo->setHub($hub)
->setStatus(JOStatus::RIDER_ASSIGN);
}
}
}
// call service to generate job order and invoice
$invoice_items = $req->request->get('invoice_items', []);
$promo_id = $req->request->get('invoice_promo');
$invoice_change = $req->request->get('invoice_change', 0);
// check if invoice changed
if ($invoice_change)
{
$source = $jo->getSource();
// get the price tier according to location.
$price_tier = $this->pt_manager->getPriceTier($jo->getCoordinates());
$this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $price_tier, $error_array);
}
// validate
$errors = $this->validator->validate($jo);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if errors are found
if (empty($error_array))
{
// validated, no error. save the job order and customer
$em->persist($jo);
$em->persist($customer);
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::CREATE)
->setJobOrder($jo);
if ($user != null)
{
$event->setUser($user);
}
$em->persist($event);
// check if JOStatus is rider assign
if ($jo->getStatus() == JOStatus::RIDER_ASSIGN)
{
$rider_assign_event = new JOEvent();
$rider_assign_event->setDateHappen(new DateTime())
->setTypeID(JOEventType::HUB_ASSIGN)
->setJobOrder($jo);
if ($user != null)
{
$rider_assign_event->setUser($user);
}
$em->persist($rider_assign_event);
}
$em->flush();
}
}
$data['error_array'] = $error_array;
if ($jo != null)
{
$data['job_order'] = $jo;
// need to get the customer tags but only if new JO
if ($flag_new_jo)
{
// check service type
if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW)
{
$customer = $cust_vehicle->getCustomer();
$customer_tags = $customer->getCustomerTagObjects();
if (!empty($customer_tags))
{
foreach ($customer_tags as $customer_tag)
{
if ($customer_tag->getID() == $jo->getInvoice()->getUsedCustomerTagId())
{
// remove associated entity
$customer->removeCustomerTag($customer_tag);
// log the availment of promo from customer
$created_by = $jo->getCreatedBy()->getUsername();
$cust_id = $jo->getCustomer()->getID();
$cust_fname = $jo->getCustomer()->getFirstName();
$cust_lname = $jo->getCustomer()->getLastName();
$jo_id = $jo->getID();
$invoice_id = $jo->getInvoice()->getID();
// TODO: check if we store total price of invoice or just the discounted amount
$amount = $jo->getInvoice()->getTotalPrice();
$this->promo_logger->logPromoInfo($created_by, $cust_id, $cust_fname, $cust_lname, $jo_id,
$invoice_id, $amount);
}
}
}
}
}
}
return $data;
}
// updates job order
public function openEditJobOrder(Request $req, $id)
{
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
// initialize error list
$error_array = [];
// make sure this object exists
if (empty($obj))
throw $this->createNotFoundException('The item does not exist');
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) {
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if landmark is set
//if (empty($req->request->get('landmark')))
// $error_array['landmark'] = 'Landmark is required.';
// check if customer is not willing to wait
$will_wait = $req->request->get('flag_willing_to_wait');
$reason = '';
$more_reason = '';
if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// get the reason and text
$reason = $req->request->get('no_wait_reason');
$more_reason = $req->request->get('not_wait_notes');
}
// check if service type is battery sales
$stype = $req->request->get('service_type');
$no_trade_in_reason = '';
if ($stype == ServiceType::BATTERY_REPLACEMENT_NEW)
{
// check if trade in
$is_trade_in = $req->request->get('invoice_trade_in_type');
if (empty($is_trade_in))
{
$no_trade_in_reason = $req->request->get('no_trade_in_reason');
if (empty($no_trade_in_reason))
$error_array['no_trade_in_reason'] = 'No trade in reason required.';
}
}
// get source of awareness if any
$soa_type = $req->request->get('source_of_awareness', '');
// get remarks
$remarks = $req->request->get('remarks', '');
// get initial concern if any
$initial_concern = $req->request->get('initial_concern', '');
if (empty($initial_concern))
$error_array['initial_concern'] = 'Initial concern of customer is required.';
// get initial concern notes if any
$initial_concern_notes = $req->request->get('initial_concern_notes', '');
if (empty($initial_concern_notes))
$error_array['initial_concern_notes'] = 'Notes on initial concern of customer is required.';
// get gender if any
$gender = $req->request->get('gender', '');
// get caller classification if any
$caller_class = $req->request->get('caller_class', '');
// get emergency type if any
$etype_id = $req->request->get('emergency_type', 0);
$etype = $em->getRepository(EmergencyType::class)->find($etype_id);
// get ownership type if any
$ownertype_id = $req->request->get('ownership_type', 0);
$owner_type = $em->getRepository(OwnershipType::class)->find($ownertype_id);
// check if customer location is set
$cust_location_id = $req->request->get('cust_location', 0);
if ($cust_location_id == 0)
$error_array['cust_location'] = 'Customer location is required.';
else
{
// get customer location
$cust_location = $em->getRepository(CustomerLocation::class)->find($cust_location_id);
if ($cust_location == null)
$error_array['cust_location'] = 'Invalid customer location';
}
if (empty($error_array))
{
// get current user
$user = $this->security->getUser();
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
// set and save values
$obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($stype)
->setWarrantyClass($req->request->get('warranty_class'))
->setSource($req->request->get('source'))
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setORName($req->request->get('or_name'))
->setPromoDetail($req->request->get('promo_detail'))
->setModeOfPayment($req->request->get('mode_of_payment'))
->setLandmark($req->request->get('landmark'))
->setWillWait($req->request->get('flag_willing_to_wait'))
->setReasonNotWait($reason)
->setNotWaitingNotes($more_reason)
->setNoTradeInReason($no_trade_in_reason)
->setSourceOfAwareness($soa_type)
->setRemarks($remarks)
->setInitialConcern($initial_concern)
->setInitialConcernNotes($initial_concern_notes)
->setCallerClassification($caller_class)
->setGender($gender)
->setEmergencyType($etype)
->setOwnershipType($owner_type)
->setCustomerLocation($cust_location);
// did they change invoice?
$invoice_items = $req->request->get('invoice_items', []);
$promo_id = $req->request->get('invoice_promo');
$invoice_change = $req->request->get('invoice_change', 0);
if ($invoice_change)
{
$source = $obj->getSource();
// get the price tier according to location.
$price_tier = $this->pt_manager->getPriceTier($obj->getCoordinates());
$this->ic->generateInvoiceCriteria($obj, $promo_id, $invoice_items, $source, $price_tier, $error_array);
}
// validate
$errors = $this->validator->validate($obj);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if any errors were found
if (!empty($error_array)) {
// return validation failure response
return $this->json([
'success' => false,
'errors' => $error_array
], 422);
}
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::OPEN_EDIT)
->setJobOrder($obj);
// error_log('open edit?');
if ($user != null)
{
$event->setUser($user);
}
$em->persist($event);
// validated! save the entity
$em->flush();
}
$data['error_array'] = $error_array;
if ($obj != null)
{
$data['job_order'] = $obj;
}
return $data;
}
// dispatch job order
public function dispatchJobOrder(Request $req, int $id, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient)
{
// get object data
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
$processor = $obj->getProcessedBy();
$user = $this->security->getUser();;
// check if we're the one processing, return error otherwise
if ($processor == null)
throw new AccessDeniedHttpException('Not the processor');
if ($processor != null && $processor->getID() != $user->getID())
throw new AccessDeniedHttpException('Not the processor');
// initialize error list
$error_array = [];
// make sure this object exists
if (empty($obj))
throw new NotFoundHttpException('The item does not exist');
// check if cancelled already
if (!$obj->canDispatch())
{
throw new NotFoundHttpException('Could not dispatch. Job Order is not pending.');
// TODO: have this handled better, so UI shows the error
// $error_array['dispatch'] = 'Could not dispatch. Job Order is not pending.';
}
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat')))
{
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if hub is set
if (empty($req->request->get('hub')))
{
$error_array['hub'] = 'No hub selected.';
}
else
{
// get hub
$hub = $em->getRepository(Hub::class)->find($req->request->get('hub'));
if (empty($hub))
{
$error_array['hub'] = 'Invalid hub specified.';
}
}
// check facilitated type
$fac_type = $req->request->get('facilitated_type');
if (!empty($fac_type))
{
if (!FacilitatedType::validate($fac_type))
$fac_type = null;
}
else
$fac_type = null;
// check facilitated by
$fac_by_id = $req->request->get('facilitated_by');
$fac_by = null;
if (!empty($fac_by_id))
{
$fac_by = $em->getRepository(Hub::class)->find($fac_by_id);
if (empty($fac_by))
$fac_by = null;
}
// check if landmark is set
//if (empty($req->request->get('landmark')))
// $error_array['landmark'] = 'Landmark is required.';
// check if customer is not willing to wait
$will_wait = $req->request->get('flag_willing_to_wait');
$reason = '';
$more_reason = '';
if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// get the reason and text
$reason = $req->request->get('no_wait_reason');
$more_reason = $req->request->get('not_wait_notes');
}
// get source of awareness if any
$soa_type = $req->request->get('source_of_awareness', '');
// get remarks
$remarks = $req->request->get('remarks', '');
// get initial concern if any
$initial_concern = $req->request->get('initial_concern', '');
if (empty($initial_concern))
$error_array['initial_concern'] = 'Initial concern of customer is required.';
// get initial concern notes if any
$initial_concern_notes = $req->request->get('initial_concern_notes', '');
if (empty($initial_concern_notes))
$error_array['initial_concern_notes'] = 'Notes on initial concern of customer is required.';
// get gender if any
$gender = $req->request->get('gender', '');
// get caller classification if any
$caller_class = $req->request->get('caller_class', '');
// get emergency type if any
$etype_id = $req->request->get('emergency_type', 0);
$etype = $em->getRepository(EmergencyType::class)->find($etype_id);
// get ownership type if any
$ownertype_id = $req->request->get('ownership_type', 0);
$owner_type = $em->getRepository(OwnershipType::class)->find($ownertype_id);
// check if customer location is set
$cust_location_id = $req->request->get('cust_location', 0);
if ($cust_location_id == 0)
$error_array['cust_location'] = 'Customer location is required.';
else
{
// get customer location
$cust_location = $em->getRepository(CustomerLocation::class)->find($cust_location_id);
if ($cust_location == null)
$error_array['cust_location'] = 'Invalid customer location';
}
if (empty($error_array))
{
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
// set and save values
$obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($req->request->get('service_type'))
->setWarrantyClass($req->request->get('warranty_class'))
->setSource($req->request->get('source'))
->setStatus(JOStatus::RIDER_ASSIGN)
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setFacilitatedType($fac_type)
->setFacilitatedBy($fac_by)
->setHub($hub)
->setLandmark($req->request->get('landmark'))
->setWillWait($req->request->get('flag_willing_to_wait'))
->setReasonNotWait($reason)
->setNotWaitingNotes($more_reason)
->setSourceOfAwareness($soa_type)
->setRemarks($remarks)
->setInitialConcern($initial_concern)
->setInitialConcernNotes($initial_concern_notes)
->setGender($gender)
->setCallerClassification($caller_class)
->setEmergencyType($etype)
->setOwnershipType($owner_type)
->setCustomerLocation($cust_location)
->setInventoryCount($req->request->get('hub_inv_count', 0));
// validate
$errors = $this->validator->validate($obj);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
}
if (empty($error_array))
{
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::HUB_ASSIGN)
->setJobOrder($obj);
if ($user != null)
{
$event->setUser($user);
}
$em->persist($event);
// validated! save the entity
$em->flush();
// send event to mobile app
$payload = [
'event' => 'outlet_assign'
];
$mclient->sendEvent($obj, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($obj, $payload);
$fcmclient->sendJoEvent($obj, "jo_fcm_title_outlet_assign", "jo_fcm_body_outlet_assign");
// update redis hub jo count
$this->hub_dist->incrementJoCountForHub($hub);
}
return $error_array;
}
// assign job order
public function assignJobOrder(Request $req, $id)
{
// get object data
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
// initialize error list
$error_array = [];
// make sure this object exists
if (empty($obj))
throw new NotFoundHttpException('The item does not exist');
// check if we can assign
if (!$obj->canAssign())
throw new NotFoundHttpException('Cannot assign rider to this job order.');
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) {
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if rider is set
if (empty($req->request->get('rider'))) {
$error_array['rider'] = 'No rider selected.';
} else {
// get rider
$rider = $em->getRepository(Rider::class)->find($req->request->get('rider'));
if (empty($rider)) {
$error_array['rider'] = 'Invalid rider specified.';
}
}
// check if landmark is set
//if (empty($req->request->get('landmark')))
// $error_array['landmark'] = 'Landmark is required.';
// check if customer is not willing to wait
$will_wait = $req->request->get('flag_willing_to_wait');
$reason = '';
$more_reason = '';
if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// get the reason and text
$reason = $req->request->get('no_wait_reason');
$more_reason = $req->request->get('not_wait_notes');
}
// get source of awareness if any
$soa_type = $req->request->get('source_of_awareness', '');
// get remarks
$remarks = $req->request->get('remarks', '');
// get initial concern if any
$initial_concern = $req->request->get('initial_concern', '');
if (empty($initial_concern))
$error_array['initial_concern'] = 'Initial concern of customer is required.';
// get initial concern notes if any
$initial_concern_notes = $req->request->get('initial_concern_notes', '');
if (empty($initial_concern_notes))
$error_array['initial_concern_notes'] = 'Notes on initial concern of customer is required.';
// get gender if any
$gender = $req->request->get('gender', '');
// get caller classification if any
$caller_class = $req->request->get('caller_class', '');
// get emergency type if any
$etype_id = $req->request->get('emergency_type', 0);
$etype = $em->getRepository(EmergencyType::class)->find($etype_id);
// get ownership type if any
$ownertype_id = $req->request->get('ownership_type', 0);
$owner_type = $em->getRepository(OwnershipType::class)->find($ownertype_id);
// check if customer location is set
$cust_location_id = $req->request->get('cust_location', 0);
if ($cust_location_id == 0)
$error_array['cust_location'] = 'Customer location is required.';
else
{
// get customer location
$cust_location = $em->getRepository(CustomerLocation::class)->find($cust_location_id);
if ($cust_location == null)
$error_array['cust_location'] = 'Invalid customer location';
}
// get current user
$user = $this->security->getUser();
if (empty($error_array)) {
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
// set and save values
$obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($req->request->get('service_type'))
->setWarrantyClass($req->request->get('warranty_class'))
->setSource($req->request->get('source'))
->setStatus(JOStatus::ASSIGNED)
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setDateAssign(new DateTime())
->setRider($rider)
->setLandmark($req->request->get('landmark'))
->setWillWait($req->request->get('flag_willing_to_wait'))
->setReasonNotWait($reason)
->setNotWaitingNotes($more_reason)
->setDeliveryStatus(DeliveryStatus::RIDER_ASSIGN)
->setSourceOfAwareness($soa_type)
->setRemarks($remarks)
->setInitialConcern($initial_concern)
->setInitialConcernNotes($initial_concern_notes)
->setCallerClassification($caller_class)
->setGender($gender)
->setEmergencyType($etype)
->setOwnershipType($owner_type)
->setCustomerLocation($cust_location);
if ($user != null)
{
$obj->setAssignedBy($user);
}
// validate
$errors = $this->validator->validate($obj);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
}
if (empty($error_array))
{
// set rider unavailable
// $rider->setAvailable(false);
// set rider's current job order
$rider->setCurrentJobOrder($obj);
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::RIDER_ASSIGN)
->setJobOrder($obj);
if ($user != null)
{
$event->setUser($user);
}
$em->persist($event);
// validated! save the entity
$em->flush();
// call rider assignment handler's assignJobOrder
$this->rah->assignJobOrder($obj, $rider);
}
return $error_array;
}
// fulfill job order
public function fulfillJobOrder(Request $req, $id)
{
// initialize error list
$error_array = [];
// get object data
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this object exists
if (empty($obj))
throw new NotFoundHttpException('The item does not exist');
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) {
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if landmark is set
//if (empty($req->request->get('landmark')))
// $error_array['landmark'] = 'Landmark is required.';
// check if customer is not willing to wait
$will_wait = $req->request->get('flag_willing_to_wait');
$reason = '';
$more_reason = '';
if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// get the reason and text
$reason = $req->request->get('no_wait_reason');
$more_reason = $req->request->get('not_wait_notes');
}
// get source of awareness if any
$soa_type = $req->request->get('source_of_awareness', '');
// get remarks
$remarks = $req->request->get('remarks', '');
// get initial concern if any
$initial_concern = $req->request->get('initial_concern', '');
if (empty($initial_concern))
$error_array['initial_concern'] = 'Initial concern of customer is required.';
// get initial concern notes if any
$initial_concern_notes = $req->request->get('initial_concern_notes', '');
if (empty($initial_concern_notes))
$error_array['initial_concern_notes'] = 'Notes on initial concern of customer is required.';
// get gender if any
$gender = $req->request->get('gender', '');
// get caller classification if any
$caller_class = $req->request->get('caller_class', '');
// get emergency type if any
$etype_id = $req->request->get('emergency_type', 0);
$etype = $em->getRepository(EmergencyType::class)->find($etype_id);
// get ownership type if any
$ownertype_id = $req->request->get('ownership_type', 0);
$owner_type = $em->getRepository(OwnershipType::class)->find($ownertype_id);
// check if customer location is set
$cust_location_id = $req->request->get('cust_location', 0);
if ($cust_location_id == 0)
$error_array['cust_location'] = 'Customer location is required.';
else
{
// get customer location
$cust_location = $em->getRepository(CustomerLocation::class)->find($cust_location_id);
if ($cust_location == null)
$error_array['cust_location'] = 'Invalid customer location';
}
if (empty($error_array)) {
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
// set and save values
$obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($req->request->get('service_type'))
->setWarrantyClass($req->request->get('warranty_class'))
->setSource($req->request->get('source'))
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setLandmark($req->request->get('landmark'))
->setWillWait($req->request->get('flag_willing_to_wait'))
->setReasonNotWait($reason)
->setNotWaitingNotes($more_reason)
->setDeliveryStatus(DeliveryStatus::FULFILLED)
->setSourceOfAwareness($soa_type)
->setRemarks($remarks)
->setInitialConcern($initial_concern)
->setInitialConcernNotes($initial_concern_notes)
->setGender($gender)
->setCallerClassification($caller_class)
->setEmergencyType($etype)
->setOwnershipType($owner_type)
->setCustomerLocation($cust_location);
// validate
$errors = $this->validator->validate($obj);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
}
if (empty($error_array))
{
$obj->fulfill();
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::FULFILL)
->setJobOrder($obj);
// get current user
$user = $this->security->getUser();
if ($user != null)
{
$event->setUser($user);
}
$event->setUser($user);
$em->persist($event);
// save to customer vehicle battery record
$this->updateVehicleBattery($obj);
// validated! save the entity
$em->flush();
// get rider
$rider = $obj->getRider();
$image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/assets/images/user.gif';
if ($rider->getImageFile() != null)
$image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/uploads/' . $rider->getImageFile();
// call rider assignment handler's fulfillJobOrder
$this->rah->fulfillJobOrder($obj, $image_url, $rider);
// create the warranty if new battery only
if ($this->checkIfNewBattery($obj))
{
$serial = null;
$warranty_class = $obj->getWarrantyClass();
$first_name = $obj->getCustomer()->getFirstName();
$last_name = $obj->getCustomer()->getLastName();
$mobile_number = $obj->getCustomer()->getPhoneMobile();
// check if date fulfilled is null
//if ($obj->getDateFulfill() == null)
// $date_purchase = $obj->getDateCreate();
//else
// $date_purchase = $obj->getDateFulfill();
// use date_schedule for warranty expiration computation
$date_purchase = $obj->getDateSchedule();
// validate plate number
// $plate_number = $this->wh->cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber());
$plate_number = Warranty::cleanPlateNumber($obj->getCustomerVehicle()->getPlateNumber());
if ($plate_number != false)
{
$batt_list = array();
$invoice = $obj->getInvoice();
if (!empty($invoice))
{
// get battery
$invoice_items = $invoice->getItems();
foreach ($invoice_items as $item)
{
$battery = $item->getBattery();
if ($battery != null)
{
$batt_list[] = $item->getBattery();
}
}
}
$user_id = $user->getUsername();
$source = WarrantySource::ADMIN_PANEL;
$this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class, $user_id, $source, $obj->getCustomer(), $obj->getCustomerVehicle()->getVehicle());
}
else
error_log('Invalid plate number for warranty. Plate number = ' . $obj->getCustomerVehicle()->getPlateNumber());
}
// send SMS to customer
// prepend country code to number
$phone_number = $this->country_code . $obj->getCustomer()->getPhoneMobile();
if (!empty($phone_number))
$this->sendSMSToCustomer($phone_number);
}
return $error_array;
}
// cancel job order
public function cancelJobOrder(Request $req, int $id, MQTTClient $mclient, MQTTClientApiv2 $mclientv2, FCMSender $fcmclient)
{
// get object data
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this object exists
if (empty($obj))
throw new NotFoundHttpException('The item does not exist');
$cancel_reason = $req->request->get('cancel_reason');
//error_log('cancel_reason ' . $cancel_reason);
$obj->setDeliveryStatus(DeliveryStatus::CANCELLED);
$obj->cancel($cancel_reason);
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::CANCEL)
->setJobOrder($obj);
// get current user
$user = $this->security->getUser();
if ($user != null)
{
$event->setUser($user);
}
$event->setUser($user);
$em->persist($event);
// save
$em->flush();
// send mobile app event
$payload = [
'event' => 'cancelled',
'reason' => $cancel_reason,
'jo_id' => $obj->getID(),
];
$mclient->sendEvent($obj, $payload);
$mclient->sendRiderEvent($obj, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($obj, $payload);
$fcmclient->sendJoEvent($obj, "jo_fcm_title_cancelled", "jo_fcm_body_cancelled", ['reason' => $cancel_reason]);
}
// set hub for job order
public function setHub($req, $id, $mclient, $mclientv2, $fcmclient)
{
// get object data
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
$user = $this->security->getUser();
// initialize error list
$error_array = [];
// make sure this object exists
if (empty($obj))
throw new NotFoundHttpException('The item does not exist');
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) {
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if hub is set
if (empty($req->request->get('hub'))) {
$error_array['hub'] = 'No hub selected.';
} else {
// get hub
$hub = $em->getRepository(Hub::class)->find($req->request->get('hub'));
if (empty($hub)) {
$error_array['hub'] = 'Invalid hub specified.';
}
}
// check if landmark is set
//if (empty($req->request->get('landmark')))
// $error_array['landmark'] = 'Landmark is required.';
// error_log($req->request->get('landmark'));
// check if customer is not willing to wait
$will_wait = $req->request->get('flag_willing_to_wait');
$reason = '';
$more_reason = '';
if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// get the reason and text
$reason = $req->request->get('no_wait_reason');
$more_reason = $req->request->get('not_wait_notes');
}
// get source of awareness if any
$soa_type = $req->request->get('source_of_awareness', '');
// get remarks
$remarks = $req->request->get('remarks', '');
// get initial concern if any
$initial_concern = $req->request->get('initial_concern', '');
if (empty($initial_concern))
$error_array['initial_concern'] = 'Initial concern of customer is required.';
// get initial concern notes if any
$initial_concern_notes = $req->request->get('initial_concern_notes', '');
if (empty($initial_concern_notes))
$error_array['initial_concern_notes'] = 'Notes on initial concern of customer is required.';
// get gender if any
$gender = $req->request->get('gender', '');
// get caller classification if any
$caller_class = $req->request->get('caller_class', '');
// get emergency type if any
$etype_id = $req->request->get('emergency_type', 0);
$etype = $em->getRepository(EmergencyType::class)->find($etype_id);
// get ownership type if any
$ownertype_id = $req->request->get('ownership_type', 0);
$owner_type = $em->getRepository(OwnershipType::class)->find($ownertype_id);
// check if customer location is set
$cust_location_id = $req->request->get('cust_location', 0);
if ($cust_location_id == 0)
$error_array['cust_location'] = 'Customer location is required.';
else
{
// get customer location
$cust_location = $em->getRepository(CustomerLocation::class)->find($cust_location_id);
if ($cust_location == null)
$error_array['cust_location'] = 'Invalid customer location';
}
// get previously assigned hub, if any
$old_hub = $obj->getHub();
// get previously assigned rider, if any
$old_rider = $obj->getRider();
if (empty($error_array))
{
// rider mqtt event
// NOTE: need to send this before saving because rider will be cleared
$rider_payload = [
'event' => 'cancelled',
'reason' => 'Reassigned',
'jo_id' => $obj->getID(),
];
$mclient->sendRiderEvent($obj, $rider_payload);
// need to unset first rider's current job order
$old_rider = $obj->getRider();
if ($old_rider != null)
{
if (($old_rider->getCurrentJobOrder() != null) &&
($old_rider->getCurrentJobOrder()->getID() == $obj->getID()))
{
$old_rider->setCurrentJobOrder();
// set available flag for first rider
$old_rider->setAvailable(true);
}
}
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
// set and save values
$obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($req->request->get('service_type'))
->setWarrantyClass($req->request->get('warranty_class'))
->setSource($req->request->get('source'))
->setStatus(JOStatus::RIDER_ASSIGN)
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setHub($hub)
->setLandmark($req->request->get('landmark'))
->setWillWait($req->request->get('flag_willing_to_wait'))
->setReasonNotWait($reason)
->setNotWaitingNotes($more_reason)
->setSourceOfAwareness($soa_type)
->setRemarks($remarks)
->setInitialConcern($initial_concern)
->setInitialConcernNotes($initial_concern_notes)
->setGender($gender)
->setCallerClassification($caller_class)
->setEmergencyType($etype)
->setOwnershipType($owner_type)
->setCustomerLocation($cust_location)
->setInventoryCount($req->request->get('hub_inv_count', 0))
->clearRider();
if ($user != null)
{
$obj->setProcessedBy($user);
}
$em->persist($obj);
// validate
$errors = $this->validator->validate($obj);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
}
// check if any errors were found
if (empty($error_array)) {
// add event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::HUB_ASSIGN)
->setJobOrder($obj);
if ($user != null)
{
$event->setUser($user);
}
$em->persist($event);
// validated! save the entity
$em->flush();
// user mqtt event
$payload = [
'event' => 'outlet_assign'
];
$mclient->sendEvent($obj, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($obj, $payload);
$fcmclient->sendJoEvent($obj, "jo_fcm_title_outlet_assign", "jo_fcm_body_outlet_assign");
// update redis hub_jo_count for hub
// decrement old hub's count and increment new hub's count
if ($old_hub != null)
$this->hub_dist->decrementJoCountForHub($old_hub);
if ($hub != null)
$this->hub_dist->incrementJoCountForHub($hub);
}
return $error_array;
}
// reject hub for job order
public function rejectHub($req, $id)
{
// get object data
$em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id);
$processor = $jo->getProcessedBy();
$user = $this->security->getUser();
// check if we're the one processing, return error otherwise
if ($processor == null)
throw new AccessDeniedHttpException('Not the processor');
if ($user != null)
{
if ($processor != null && $processor->getID() != $user->getID())
throw new AccessDeniedHttpException('Not the processor');
}
// initialize error list
$error_array = [];
// make sure job order exists
if (empty($jo))
throw new NotFoundHttpException('The item does not exist');
// check if hub is set
if (empty($req->request->get('hub')))
{
$error_array['hub'] = 'No hub selected.';
}
else
{
// get hub
$hub = $em->getRepository(Hub::class)->find($req->request->get('hub'));
if (empty($hub))
{
$error_array['hub'] = 'Invalid hub specified.';
}
}
// check if this hub has already been rejected on this job order
$robj = $em->getRepository(JORejection::class)->findOneBy([
'job_order' => $jo,
'hub' => $hub
]);
if (!empty($robj))
$error_array['hub'] = 'This hub has already been rejected for the current job order.';
// check if reason is set
if (empty($req->request->get('reason')))
$error_array['reason'] = 'No reason selected.';
else if (!JORejectionReason::validate($req->request->get('reason')))
$error_array['reason'] = 'Invalid reason specified.';
if (empty($error_array))
{
// coordinates
$obj = new JORejection();
// set and save values
$obj->setDateCreate(new DateTime())
->setHub($hub)
->setJobOrder($jo)
->setReason($req->request->get('reason'))
->setRemarks($req->request->get('remarks'))
->setContactPerson($req->request->get('contact_person'));
if ($user != null)
{
$obj->setUser($user);
}
// validate
$errors = $this->validator->validate($obj);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
}
if (empty($error_array))
{
// validated! save the entity
$em->persist($obj);
$em->flush();
$this->sendSMSHubRejection($obj, $jo);
}
return $error_array;
}
// set rider for job order
public function setRider($req, $id, $mclient, $mclientv2, $fcmclient)
{
// initialize error list
$error_array = [];
// get object data
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
$user = $this->security->getUser();
// make sure this object exists
if (empty($obj))
throw new NotFoundHttpException('The item does not exist');
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) {
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if rider is set
if (empty($req->request->get('rider'))) {
$error_array['rider'] = 'No rider selected.';
} else {
// get rider
$rider = $em->getRepository(Rider::class)->find($req->request->get('rider'));
if (empty($rider)) {
$error_array['rider'] = 'Invalid rider specified.';
}
}
// check if landmark is set
//if (empty($req->request->get('landmark')))
// $error_array['landmark'] = 'Landmark is required.';
// check if customer is not willing to wait
$will_wait = $req->request->get('flag_willing_to_wait');
$reason = '';
$more_reason = '';
if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// get the reason and text
$reason = $req->request->get('no_wait_reason');
$more_reason = $req->request->get('not_wait_notes');
}
// get source of awareness if any
$soa_type = $req->request->get('source_of_awareness', '');
// get remarks
$remarks = $req->request->get('remarks', '');
// get initial concern if any
$initial_concern = $req->request->get('initial_concern', '');
if (empty($initial_concern))
$error_array['initial_concern'] = 'Initial concern of customer is required.';
// get initial concern notes if any
$initial_concern_notes = $req->request->get('initial_concern_notes', '');
if (empty($initial_concern_notes))
$error_array['initial_concern_notes'] = 'Notes on initial concern of customer is required.';
// get gender if any
$gender = $req->request->get('gender', '');
// get caller classification if any
$caller_class = $req->request->get('caller_class', '');
// get emergency type if any
$etype_id = $req->request->get('emergency_type', 0);
$etype = $em->getRepository(EmergencyType::class)->find($etype_id);
// get ownership type if any
$ownertype_id = $req->request->get('ownership_type', 0);
$owner_type = $em->getRepository(OwnershipType::class)->find($ownertype_id);
// check if customer location is set
$cust_location_id = $req->request->get('cust_location', 0);
if ($cust_location_id == 0)
$error_array['cust_location'] = 'Customer location is required.';
else
{
// get customer location
$cust_location = $em->getRepository(CustomerLocation::class)->find($cust_location_id);
if ($cust_location == null)
$error_array['cust_location'] = 'Invalid customer location';
}
if (empty($error_array)) {
// rider mqtt event
// NOTE: need to send this before saving because rider will be cleared
$rider_payload = [
'event' => 'cancelled',
'reason' => 'Reassigned',
'jo_id' => $obj->getID(),
];
$mclient->sendRiderEvent($obj, $rider_payload);
// need to unset first rider's current job order
$old_rider = $obj->getRider();
if ($old_rider != null)
{
if (($old_rider->getCurrentJobOrder() != null) &&
($old_rider->getCurrentJobOrder()->getID() == $obj->getID()))
{
$old_rider->setCurrentJobOrder();
// set available flag for first rider
$old_rider->setAvailable(true);
}
}
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
// set and save values
$obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($req->request->get('service_type'))
->setWarrantyClass($req->request->get('warranty_class'))
->setSource($req->request->get('source'))
->setStatus(JOStatus::ASSIGNED)
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setDateAssign(new DateTime())
->setRider($rider)
->setLandmark($req->request->get('landmark'))
->setWillWait($req->request->get('flag_willing_to_wait'))
->setReasonNotWait($reason)
->setNotWaitingNotes($more_reason)
->setSourceOfAwareness($soa_type)
->setRemarks($remarks)
->setDeliveryStatus(DeliveryStatus::RIDER_ASSIGN)
->setInitialConcern($initial_concern)
->setInitialConcernNotes($initial_concern_notes)
->setGender($gender)
->setCallerClassification($caller_class)
->setEmergencyType($etype)
->setOwnershipType($owner_type)
->setCustomerLocation($cust_location);
if ($user != null)
{
$obj->setAssignedBy($user);
}
// validate
$errors = $this->validator->validate($obj);
$em->persist($obj);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
}
// check if any errors were found
if (empty($error_array))
{
// set new rider's current job order
$rider->setCurrentJobOrder($obj);
// set new rider's availability to false
$rider->setAvailable(false);
// add event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::RIDER_ASSIGN)
->setJobOrder($obj);
if ($user != null)
{
$event->setUser($user);
}
$em->persist($event);
// validated! save the entity
$em->flush();
// send event to mobile app
$payload = [
'event' => 'driver_assigned'
];
$mclient->sendEvent($obj, $payload);
$mclient->sendRiderEvent($obj, $payload);
// NOTE: for resq2 app
$mclientv2->sendEvent($obj, $payload);
$mclientv2->sendRiderEvent($obj, $payload);
$fcmclient->sendJoEvent($obj, "jo_fcm_title_driver_assigned", "jo_fcm_body_driver_assigned");
}
return $error_array;
}
// unlock processor
public function unlockProcessor($id)
{
// clear lock
$em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id);
if ($jo != null)
{
$jo->setProcessedBy(null);
$em->flush();
}
}
// unlock assignor
public function unlockAssignor($id)
{
// clear lock
$em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id);
if ($jo != null)
{
$jo->setAssignedBy(null);
$em->flush();
}
}
// CMB code
public function processOneStepJobOrder(Request $req, $id)
{
// initialize error list
$error_array = [];
$em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id);
if (empty($jo))
{
// new job order
$jo = new JobOrder();
}
// check if lat and lng are provided
if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) {
$error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.';
}
// check if customer vehicle is set
if (empty($req->request->get('customer_vehicle'))) {
$error_array['customer_vehicle'] = 'No vehicle selected.';
} else {
// get customer vehicle
$cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle'));
if (empty($cust_vehicle)) {
$error_array['customer_vehicle'] = 'Invalid vehicle specified.';
}
}
// check if hub AND rider is selected
if ((empty($req->request->get('hub_id'))) &&
(empty($req->request->get('rider_id')))) {
$error_array['hub'] = 'No hub selected.';
} else {
if (empty($req->request->get('rider_id'))) {
$error_array['rider'] = 'No rider selected.';
} else {
// get hub
$hub = $em->getRepository(Hub::class)->find($req->request->get('hub_id'));
if (empty($hub)) {
$error_array['hub'] = 'Invalid hub specified.';
} else {
// get rider
$rider = $em->getRepository(Rider::class)->find($req->request->get('rider_id'));
if (empty($rider)) {
$error_array['rider'] = 'Invalid rider specified.';
}
}
}
}
if (empty($error_array))
{
// get current user
$user = $this->security->getUser();
// coordinates
$point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat'));
$stype = $req->request->get('service_type');
// set and save values
$jo->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time')))
->setCoordinates($point)
->setAdvanceOrder($req->request->get('flag_advance') ?? false)
->setServiceType($stype)
->setWarrantyClass($req->request->get('warranty_class'))
->setCustomer($cust_vehicle->getCustomer())
->setCustomerVehicle($cust_vehicle)
->setSource($req->request->get('source'))
->setStatus(JOStatus::ASSIGNED)
->setDeliveryInstructions($req->request->get('delivery_instructions'))
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setORName($req->request->get('or_name'))
->setPromoDetail($req->request->get('promo_detail'))
->setModeOfPayment($req->request->get('mode_of_payment'))
->setLandmark($req->request->get('landmark'))
->setHub($hub)
->setRider($rider);
// check if user is null, meaning call to create came from API
if ($user != null)
{
$jo->setCreatedBy($user);
}
// check if reference JO is set and validate
if (!empty($req->request->get('ref_jo'))) {
// get reference JO
$ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo'));
if (empty($ref_jo)) {
$error_array['ref_jo'] = 'Invalid reference job order specified.';
} else {
$jo->setReferenceJO($ref_jo);
}
}
// call service to generate job order and invoice
$invoice_items = $req->request->get('invoice_items', []);
$promo_id = $req->request->get('invoice_promo');
$invoice_change = $req->request->get('invoice_change', 0);
// check if invoice changed
if ($invoice_change)
{
// NOTE: this is CMB code but for compilation purposes we need to add this
$source = $jo->getSource();
// get the price tier according to location.
$price_tier = $this->pt_manager->getPriceTier($jo->getCoordinates());
$this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $source, $price_tier, $error_array);
}
// validate
$errors = $this->validator->validate($jo);
// add errors to list
foreach ($errors as $error) {
$error_array[$error->getPropertyPath()] = $error->getMessage();
}
// check if errors are found
if (empty($error_array))
{
// validated, no error. save the job order
$em->persist($jo);
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::CREATE)
->setJobOrder($jo);
if ($user != null)
{
$event->setUser($user);
}
$em->persist($event);
$em->flush();
}
}
return $error_array;
}
// initialize incoming job order form
public function initializeIncomingForm()
{
$params['obj'] = new JobOrder();
$params['mode'] = 'create';
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get template to display
$params['template'] = $this->getTwigTemplate('jo_incoming_form');
// return params
return $params;
}
// CMB code
public function initializeOneStepForm()
{
$params['obj'] = new JobOrder();
$params['mode'] = 'onestep';
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get template to display
$params['template'] = $this->getTwigTemplate('jo_onestep');
// return params
return $params;
}
// CMB code
public function initializeOneStepEditForm($id, $map_tools)
{
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
$params['obj'] = $obj;
$params['mode'] = 'onestep-edit';
$params['cvid'] = $obj->getCustomerVehicle()->getID();
$params['vid'] = $obj->getCustomerVehicle()->getVehicle()->getID();
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get the hubs
// TODO: move this snippet to a function
$hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 50, date("H:i:s"));
$params['hubs'] = [];
// format duration and distance into friendly time
foreach ($hubs as $hub) {
// duration
$seconds = $hub['duration'];
if (!empty($seconds) && $seconds > 0) {
$hours = floor($seconds / 3600);
$minutes = ceil(($seconds / 60) % 60);
$hub['duration'] = ($hours > 0 ? number_format($hours) . " hr" . ($hours > 1 ? "s" : '') . ($minutes > 0 ? ", " : '') : '') . ($minutes > 0 ? number_format($minutes) . " min" . ($minutes > 1 ? "s" : '') : '');
} else {
$hub['duration'] = false;
}
// distance
$meters = $hub['distance'];
if (!empty($meters) && $meters > 0) {
$hub['distance'] = round($meters / 1000) . " km";
} else {
$hub['distance'] = false;
}
// counters
$hub['rider_count'] = count($hub['hub']->getAvailableRiders());
$hub['jo_count'] = count($hub['hub']->getForAssignmentJobOrders());
// check for rejection
$hub['flag_rejected'] = false;
$hub_id = $hub['hub']->getID();
$params['hubs'][] = $hub;
}
// get template to display
$params['template'] = $this->getTwigTemplate('jo_onestep_edit_form');
return $params;
}
// initialize open edit job order form
public function initializeOpenEditForm($id)
{
$em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id);
$params['obj'] = $jo;
$params['mode'] = 'open_edit';
$params['cvid'] = $jo->getCustomerVehicle()->getID();
$params['vid'] = $jo->getCustomerVehicle()->getVehicle()->getID();
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get template to display
$params['template'] = $this->getTwigTemplate('jo_open_edit_form');
return $params;
}
// initialize incoming vehicle form
public function initializeIncomingVehicleForm(int $cvid)
{
$params['mode'] = 'create_vehicle';
$params['cvid'] = $cvid;
$em = $this->em;
// get customer vehicle
$cv = $em->getRepository(CustomerVehicle::class)->find($cvid);
$params['vid'] = $cv->getVehicle()->getID();
// make sure this customer vehicle exists
if (empty($cv))
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order does not exist');
}
$jo = new JobOrder();
$jo->setCustomerVehicle($cv)
->setCustomer($cv->getCustomer());
$params['obj'] = $jo;
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get template to display
$params['template'] = $this->getTwigTemplate('jo_incoming_vehicle_form');
return $params;
}
// initialize all job orders form for a specific job order id
public function initializeAllForm($id)
{
$em = $this->em;
$params['mode'] = 'update-all';
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
throw new NotFoundHttpException('The job order does not exist');
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// check JO status to determine the mode and submit_url to return
if ($obj->getStatus() == JOStatus::CANCELLED)
{
$params['mode'] = 'fulfill-cancel';
$params['submit_url'] = 'jo_fulfill_cancel_submit';
}
else
{
$params['mode'] = 'update-all';
$params['submit_url'] = '';
}
// get template to display
$params['template'] = $this->getTwigTemplate('jo_all_form');
$params['obj'] = $obj;
$params['status_cancelled'] = JOStatus::CANCELLED;
// timeline stuff (descending by time)
$params['timeline'] = [
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 4",
'color' => "#f4516c"
],
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 3",
'color' => "#34bfa3"
],
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 2",
'color' => "#716aca"
],
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 1",
'color' => "#ffb822"
],
];
return $params;
}
public function initializeAllViewform($id)
{
$em = $this->em;
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
throw new NotFoundHttpException('The job order does not exist');
$this->fillDropdownParameters($params);
$params['obj'] = $obj;
// get template to display
$params['template'] = $this->getTwigTemplate('jo_all_view_form');
return $params;
}
// initialize dispatch/processing job order form
public function initializeProcessingForm($id, HubSelector $hub_selector, $motiv)
{
$em = $this->em;
// manual transaction since we're locking
$em->getConnection()->beginTransaction();
try
{
// lock and get data
$obj = $em->getRepository(JobOrder::class)->find($id, LockMode::PESSIMISTIC_READ);
// make sure this job order exists
if (empty($obj))
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order does not exist');
}
// check status
if ($obj->getStatus() != JOStatus::PENDING)
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order does not have a pending status');
}
// check if we are the processor
$processor = $obj->getProcessedBy();
// get current user
$user = $this->security->getUser();
if ($user != null)
{
// TODO: go back to list page and display alert / flash that says they cannot access it because they
// are not the processor
if ($processor != null && $processor->getID() != $user->getID())
{
$em->getConnection()->rollback();
throw new AccessDeniedHttpException('Not the processor');
}
// make this user be the processor
$obj->setProcessedBy($user);
}
$em->flush();
$em->getConnection()->commit();
}
catch(PessimisticLockException $e)
{
throw new AccessDeniedHttpException('Not the processor');
}
// NOTE: we are able to lock, everything should be fine now
$params['mode'] = 'update-processing';
$params['status_cancelled'] = JOStatus::CANCELLED;
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get rejections
$rejections = $obj->getHubRejections();
// get rejection reasons
$params['rejection_reasons'] = JORejectionReason::getCollection();
$hub_criteria = new HubCriteria();
// TODO: need to factor out the setting of HubCriteria fields
// get battery (if any)
$skus = [];
$items = [];
$invoice = $obj->getInvoice();
$inv_items = $invoice->getItems();
foreach ($inv_items as $inv_item)
{
$batt = $inv_item->getBattery();
if ($batt == null)
continue;
$skus[] = $batt->getSapCode();
$item_count = 1;
if (!empty($batt->getSapCode()))
{
$sap_code = $batt->getSapCode();
if (isset($items[$sap_code]))
$items[$sap_code] = $item_count + 1;
else
$items[$sap_code] = $item_count;
$hub_criteria->addItem($sap_code, $item_count);
}
}
// get closest hubs
// set more hub criteria fields
$long = $obj->getCoordinates()->getLongitude();
$lat = $obj->getCoordinates()->getLatitude();
// set result limit and location and date_time
$hub_criteria->setPoint($obj->getCoordinates())
->setDateTime($obj->getDateSchedule())
->setLimitResults(50);
// NOTE: set JO type regardless, for now
$hub_criteria->setJoType($obj->getServiceType())
->setOrderDate($obj->getDateCreate())
->setServiceType($obj->getServiceType());
// set customer class
$cust = $obj->getCustomer();
$hub_criteria->setCustomerClass($cust->getCustomerClassification());
// check if hub filter is enabled. If not, use default values
// for the rest of the HubCriteria fields
if ($this->hub_filter_enabled == 'true')
{
// TODO: allow this to be disabled via env or CRM. commenting out for now
// error_log('hub filter is enabled');
//if ($this->hub_geofence->isCovered($long, $lat))
//{
// if true, set other values for HubCriteria
// error_log('Area is covered by hub filtering');
$hub_criteria->setLimitDistance($this->cust_distance_limit)
->setPaymentMethod($obj->getModeOfPayment())
->setRoundRobin(true);
//}
}
// check if emergency or not
$willing_to_wait = $obj->getWillWait();
if ($willing_to_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
// reset distance limit if emergency
//TODO: move to .env the emergency distance limit
$hub_criteria->setLimitDistance(500);
$hub_criteria->setEmergency(true);
}
// set filter flags for inventory and available riders
$hub_criteria->setInventoryCheck();
$hub_criteria->setRidersCheck();
// get JO and customer id for logging purposes
$jo_id = $obj->getID();
$customer_id = $obj->getCustomer()->getID();
$hub_criteria->setJobOrderId($jo_id)
->setJoOrigin($obj->getSource())
->setCustomerId($customer_id);
$hubs = $hub_selector->find($hub_criteria);
$params['hubs'] = [];
$branch_codes = [];
$inv_data = [];
// format duration and distance into friendly time
foreach ($hubs as $hub) {
// duration
$seconds = $hub['duration'];
if (!empty($seconds) && $seconds > 0) {
$hours = floor($seconds / 3600);
$minutes = ceil(($seconds / 60) % 60);
$hub['duration'] = ($hours > 0 ? number_format($hours) . " hr" . ($hours > 1 ? "s" : '') . ($minutes > 0 ? ", " : '') : '') . ($minutes > 0 ? number_format($minutes) . " min" . ($minutes > 1 ? "s" : '') : '');
} else {
$hub['duration'] = false;
}
// distance
$meters = $hub['distance'];
if (!empty($meters) && $meters > 0) {
$hub['distance'] = round($meters / 1000) . " km";
} else {
$hub['distance'] = false;
}
// get rider counts
$rider_counts = $this->getHubRiderCounts($hub['hub']);
// counters
// get only the active riders for the total rider count per hub
// $hub['rider_count'] = count($hub['hub']->getAvailableRiders());
$hub['rider_count'] = count($hub['hub']->getActiveRiders());
$hub['redis_jo_count'] = $hub['jo_count'];
$hub['rider_logged_in'] = $rider_counts['logged_in'];
$hub['rider_in_transit'] = $rider_counts['in_transit'];
$hub['rider_available'] = $rider_counts['available'];
//$hub['redis_jo_count'] = 0;
// $hub['jo_count'] = count($hub['hub']->getForAssignmentJobOrders());
$hub['jo_count'] = $hub['redis_jo_count'];
// check for rejection
$hub['flag_rejected'] = false;
$hub_id = $hub['hub']->getID();
foreach ($rejections as $robj)
{
if ($robj->getHub()->getID() === $hub_id)
{
$hub['flag_rejected'] = true;
break;
}
}
// handle inventory data
$bcode = $hub['hub']->getBranchCode();
//$hub['inventory'] = 0;
if ($bcode != '')
{
$branch_codes[] = $bcode;
$inv_data[$bcode] = [
'hub_id' => $hub_id,
'branch_code' => $bcode,
'inventory' => 0,
];
}
$params['hubs'][$hub_id] = $hub;
}
$params['obj'] = $obj;
// get template to display
$params['template'] = $this->getTwigTemplate('jo_processing_form');
// NOTE: as we have included inventory now from the hub selector, we no longer have to redo it here
// get battery (if any)
/*
$skus = [];
$invoice = $obj->getInvoice();
$inv_items = $invoice->getItems();
foreach ($inv_items as $inv_item)
{
$batt = $inv_item->getBattery();
if ($batt == null)
continue;
$skus[] = $batt->getSapCode();
}
// get inventory
$mres = $motiv->getInventory($branch_codes, $skus);
foreach ($mres as $mres_item)
{
// check if we have a valid response from motiv, ignore otherwise
if (isset($mres_item['BranchCode']))
{
$bcode = $mres_item['BranchCode'];
$inv_count = $mres_item['Quantity'];
if (isset($inv_data[$bcode]))
{
$hub_id = $inv_data[$bcode]['hub_id'];
$params['hubs'][$hub_id]['inventory'] = $inv_count;
}
}
}
*/
// error_log(print_r($mres, true));
return $params;
}
// initialize assign job order form
public function initializeAssignForm($id)
{
$em = $this->em;
// manual transaction since we're locking
$em->getConnection()->beginTransaction();
$params['mode'] = 'update-assigning';
try
{
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order does not exist');
}
// check status
if ($obj->getStatus() != JOStatus::RIDER_ASSIGN)
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order does not have an assigning status');
}
// check if super user
$user = $this->security->getUser();
if ($user != null)
{
if ($user->isSuperAdmin())
{
// do nothing, just allow page to be accessed
}
else
{
// check if hub is assigned to current user
$user_hubs = $user->getHubs();
if (!in_array($obj->getHub()->getID(), $user_hubs))
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order is not on a hub assigned to this user');
}
// check if we are the assignor
$assignor = $obj->getAssignedBy();
if ($assignor != null && $assignor->getID() != $user->getID())
{
$em->getConnection()->rollback();
throw new AccessDeniedHttpException('Not the assignor');
}
// make this user be the assignor
$obj->setAssignedBy($user);
}
}
$em->flush();
$em->getConnection()->commit();
}
catch (PessimisticLockException $e)
{
throw new AccessDeniedHttpException('Not the assignor');
}
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get the rider data to display for table
$rider_data = $this->getRiderDataForAssignment($obj);
$params['rider_data'] = $rider_data;
// get template to display
$params['template'] = $this->getTwigTemplate('jo_assigning_form');
$params['obj'] = $obj;
$params['status_cancelled'] = JOStatus::CANCELLED;
return $params;
}
// initialize fulflll job order form
public function initializeFulfillmentForm($id)
{
$em = $this->em;
$params['mode'] = 'update-fulfillment';
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
{
throw new NotFoundHttpException('The job order does not exist');
}
// check status
if (!in_array($obj->getStatus(), [JOStatus::ASSIGNED, JOStatus::IN_PROGRESS]))
{
throw new NotFoundHttpException('The job order does not have a fulfillment status');
}
// get current user
$user = $this->security->getUser();
// check if hub is assigned to current user
$user_hubs = $user->getHubs();
if (!in_array($obj->getHub()->getID(), $user_hubs))
{
throw new NotFoundHttpException('The job order is not on a hub assigned to this user');
}
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get template to display
$params['template'] = $this->getTwigTemplate('jo_fulfillment_form');
$params['obj'] = $obj;
$params['status_cancelled'] = JOStatus::CANCELLED;
return $params;
}
// initialize hub form
public function initializeHubForm($id, HubSelector $hub_selector, $motiv)
{
$em = $this->em;
$params['mode'] = 'update-reassign-hub';
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
{
throw new NotFoundHttpException('The job order does not exist');
}
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get rejections
$rejections = $obj->getHubRejections();
// get rejection reasons
$params['rejection_reasons'] = JORejectionReason::getCollection();
$hub_criteria = new HubCriteria();
// get battery (if any)
$skus = [];
$items = [];
$invoice = $obj->getInvoice();
$inv_items = $invoice->getItems();
foreach ($inv_items as $inv_item)
{
$batt = $inv_item->getBattery();
if ($batt == null)
continue;
$skus[] = $batt->getSapCode();
$item_count = 1;
if (!empty($batt->getSapCode()))
{
$sap_code = $batt->getSapCode();
if (isset($items[$sap_code]))
$items[$sap_code] = $item_count + 1;
else
$items[$sap_code] = $item_count;
}
$hub_criteria->addItem($sap_code, $item_count);
}
// get closest hubs
// set more hub criteria fields
$long = $obj->getCoordinates()->getLongitude();
$lat = $obj->getCoordinates()->getLatitude();
$hub_criteria->setPoint($obj->getCoordinates())
->setDateTime($obj->getDateSchedule())
->setLimitResults(50);
// NOTE: set JO type regardless, for now
$hub_criteria->setJoType($obj->getServiceType())
->setOrderDate($obj->getDateCreate())
->setServiceType($obj->getServiceType());
// set customer class
$cust = $obj->getCustomer();
$hub_criteria->setCustomerClass($cust->getCustomerClassification());
// TODO: allow this to be disabled via env or CRM. commenting out for now
//if ($this->hub_geofence->isCovered($long, $lat))
//{
// if true, set other values for HubCriteria
// error_log('Area is covered by hub');
$hub_criteria->setLimitDistance($this->cust_distance_limit)
->setPaymentMethod($obj->getModeOfPayment())
->setRoundRobin(true);
//}
// check if emergency or not
$willing_to_wait = $obj->getWillWait();
if ($willing_to_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
{
//TODO: move to .env the emergency distance limit
$hub_criteria->setLimitDistance(500);
$hub_criteria->setEmergency(true);
}
// set filter flags for inventory and available riders
$hub_criteria->setInventoryCheck();
$hub_criteria->setRidersCheck();
// get JO and customer id for logging purposes
$jo_id = $obj->getID();
$customer_id = $cust->getID();
$hub_criteria->setJobOrderId($jo_id)
->setJoOrigin($obj->getSource())
->setCustomerId($customer_id);
$hubs = $hub_selector->find($hub_criteria);
$params['status_cancelled'] = JOStatus::CANCELLED;
$params['hubs'] = [];
$branch_codes = [];
$inv_data = [];
// format duration and distance into friendly time
foreach ($hubs as $hub) {
// duration
$seconds = $hub['duration'];
if (!empty($seconds) && $seconds > 0) {
$hours = floor($seconds / 3600);
$minutes = ceil(($seconds / 60) % 60);
$hub['duration'] = ($hours > 0 ? number_format($hours) . " hr" . ($hours > 1 ? "s" : '') . ($minutes > 0 ? ", " : '') : '') . ($minutes > 0 ? number_format($minutes) . " min" . ($minutes > 1 ? "s" : '') : '');
} else {
$hub['duration'] = false;
}
// distance
$meters = $hub['distance'];
if (!empty($meters) && $meters > 0) {
$hub['distance'] = round($meters / 1000) . " km";
} else {
$hub['distance'] = false;
}
// get rider counts
$rider_counts = $this->getHubRiderCounts($hub['hub']);
// counters
// get only the active riders for the total rider count per hub
// $hub['rider_count'] = count($hub['hub']->getAvailableRiders());
$hub['rider_count'] = count($hub['hub']->getActiveRiders());
// $hub['redis_jo_count'] = $hub['jo_count'];
$hub['redis_jo_count'] = 0;
$hub['rider_logged_in'] = $rider_counts['logged_in'];
$hub['rider_in_transit'] = $rider_counts['in_transit'];
$hub['rider_available'] = $rider_counts['available'];
// $hub['jo_count'] = count($hub['hub']->getForAssignmentJobOrders());
$hub['jo_count'] = $hub['redis_jo_count'];
// check for rejection
$hub['flag_rejected'] = false;
$hub_id = $hub['hub']->getID();
foreach ($rejections as $robj)
{
if ($robj->getHub()->getID() === $hub_id)
{
$hub['flag_rejected'] = true;
break;
}
}
// handle inventory data
$bcode = $hub['hub']->getBranchCode();
//$hub['inventory'] = 0;
if ($bcode != '')
{
$branch_codes[] = $bcode;
$inv_data[$bcode] = [
'hub_id' => $hub_id,
'branch_code' => $bcode,
'inventory' => 0,
];
}
$params['hubs'][$hub_id] = $hub;
}
// NOTE: as we have included inventory now from the hub selector, we no longer have to redo it here
//error_log("TOTAL HUBS FOUND WITH BRANCH CODE: " . count($inv_data));
// get all enabled filters
$enabled_filter_str = $_ENV['ENABLED_HUB_FILTERS'];
$enabled_filters = explode(",", $enabled_filter_str);
// if inventory filter is disabled, fetch inventory here
if (!in_array('InventoryHubFilter', $enabled_filters) || $this->skipInventoryCheck($obj->getCoordinates())) {
error_log("NO INVENTORY CHECKS, GETTING INVENTORY FOR JO " . $obj->getID());
// get battery (if any)
$skus = [];
$invoice = $obj->getInvoice();
$inv_items = $invoice->getItems();
foreach ($inv_items as $inv_item)
{
$batt = $inv_item->getBattery();
if ($batt == null)
continue;
$skus[] = $batt->getSapCode();
}
// get inventory
$mres = $motiv->getInventory($branch_codes, $skus);
$x = 0;
error_log("TOTAL RESULTS FROM MOTIV: " . count($mres) . " OUT OF " . count($branch_codes) . " BRANCH CODES AND " . count($skus) . " SKUS");
foreach ($mres as $mres_item)
{
// check if we have a valid response from motiv, ignore otherwise
if (isset($mres_item['BranchCode']))
{
$bcode = $mres_item['BranchCode'];
$inv_count = $mres_item['Quantity'];
if (isset($inv_data[$bcode]))
{
$hub_id = $inv_data[$bcode]['hub_id'];
$params['hubs'][$hub_id]['inventory'] = $inv_count;
error_log("SETTING HUB " . $hub_id . " INVENTORY TO " . $inv_count);
$x++;
} else {
error_log("CANNOT FIND BCODE FOR " . $bcode);
}
} else {
error_log("CANNOT FIND BCODE FOR RESULT: " . print_r($mres_item, true));
}
}
error_log("SET QUANTITY OF " . $x . " HUBS TO NON ZERO");
// error_log(print_r($mres, true));
}
$params['obj'] = $obj;
// get template to display
$params['template'] = $this->getTwigTemplate('jo_open_hub_form');
return $params;
}
// initialize rider form
public function initializeRiderForm($id)
{
$em = $this->em;
$params['mode'] = 'update-reassign-rider';
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order does not exist');
}
// check status
if ($obj->getStatus() == JOStatus::PENDING)
{
$em->getConnection()->rollback();
throw new NotFoundHttpException('The job order does not have an assigned hub');
}
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get the rider data to display for table
$rider_data = $this->getRiderDataForAssignment($obj);
$params['rider_data'] = $rider_data;
$params['obj'] = $obj;
$params['status_cancelled'] = JOStatus::CANCELLED;
// get template to display
$params['template'] = $this->getTwigTemplate('jo_open_rider_form');
return $params;
}
// generate pdf form for job order
public function generatePDFForm($req, $id, $proj_path)
{
$em = $this->em;
$translator = $this->translator;
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
throw new NotFoundHttpException('The job order does not exist');
// set output filename
$filename = 'job-order-' . $obj->getID() . '.pdf';
// translate the title and the logo for the pdf
$translated_title = $translator->trans('jo_title_pdf');
$translated_logo = $translator->trans('image_jo_pdf');
// generate the pdf
$pdf = new FPDF('P', 'mm', 'letter');
$pdf->AddPage();
$pdf->setTitle($translated_title . ' #' . $obj->getID());
$pdf->SetFillColor(211, 211, 211);
// style defaults
$margin = 10;
$page_width = $pdf->GetPageWidth() - ($margin * 2);
$table_col_width = $page_width / 12;
$line_height = 5;
$jo_line_height = 10;
$table_line_height = 7;
$font_face = 'Arial';
$body_font_size = 9;
$header_font_size = 9;
$jo_font_size = 16;
$col1_x = $margin;
$col2_x = 120;
$label_width = 40;
$val_width = 60;
// insert the logo
$image_path = $proj_path . $translated_logo;
$pdf->Image($image_path, $col1_x, 10);
// insert JO number
$pdf->SetFont($font_face, 'B', $jo_font_size);
$pdf->SetX($col2_x);
$pdf->Cell($label_width, $jo_line_height, $translator->trans('label.pdf.jo_number'));
$pdf->SetTextColor(9, 65, 150);
$pdf->Cell(0, $jo_line_height, $obj->getID());
// insert customer info
$customer = $obj->getCustomer();
$pdf->SetFont($font_face, '', $body_font_size);
$pdf->SetTextColor(0, 0, 0);
$pdf->Ln($line_height * 7);
// get current Y
$y = $pdf->GetY();
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.customer_name'));
$pdf->MultiCell($val_width, $line_height, $customer ? $customer->getFirstName() . ' ' . $customer->getLastName() : '', 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.mobile_phone'));
$pdf->MultiCell(0, $line_height, $customer && $customer->getPhoneMobile() ? $this->country_code . $customer->getPhoneMobile() : '', 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.delivery_date'));
$pdf->MultiCell($val_width, $line_height, $obj->getDateSchedule() ? $obj->getDateSchedule()->format("m/d/Y") : '', 0, 'left');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.landline'));
$pdf->MultiCell(0, $line_height, $customer && $customer->getPhoneLandline() ? $this->country_code . $customer->getPhoneLandline() : '', 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.office_phone'));
$pdf->MultiCell(0, $line_height, $customer && $customer->getPhoneOffice() ? $this->country_code . $customer->getPhoneOffice() : '', 0, 'L');
$pdf->SetX($col2_x);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.fax'));
$pdf->MultiCell($val_width, $line_height, $customer && $customer->getPhoneFax() ? $this->country_code . $customer->getPhoneFax() : '', 0, 'L');
// insert vehicle info
$cv = $obj->getCustomerVehicle();
$vehicle = $cv->getVehicle();
$pdf->Ln();
$pdf->SetFont($font_face, 'B', $header_font_size);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.vehicle_details'));
$pdf->Ln($line_height * 2);
// get current Y
$y = $pdf->GetY();
$pdf->SetFont($font_face, '', $body_font_size);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.plate_number'));
$pdf->MultiCell($val_width, $line_height, $cv ? $cv->getPlateNumber() : '', 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.vehicle_color'));
$pdf->MultiCell(0, $line_height, $cv ? $cv->getColor() : '', 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.brand'));
$pdf->MultiCell($val_width, $line_height, $vehicle && $vehicle->getManufacturer() ? $vehicle->getManufacturer()->getName() : '', 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.model_year'));
$pdf->MultiCell(0, $line_height, $cv ? $cv->getModelYear() : '', 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.make'));
$pdf->MultiCell($val_width, $line_height, $vehicle ? $vehicle->getMake() : '', 0, 'L');
// insert battery info
$battery = $cv->getCurrentBattery();
$pdf->Ln();
$pdf->SetFont($font_face, 'B', $header_font_size);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.battery_details'));
$pdf->Ln($line_height * 2);
$pdf->SetFont($font_face, '', $body_font_size);
// get current Y
$y = $pdf->GetY();
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.current_battery'));
$pdf->MultiCell($val_width, $line_height, $battery && $battery->getManufacturer() && $battery->getModel() && $battery->getSize() ? $battery->getManufacturer()->getName() . ' ' . $battery->getModel()->getName() . ' ' . $battery->getSize()->getName() . ' (' . $battery->getProductCode() . ')' : '', 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.serial_number'));
$pdf->MultiCell(0, $line_height, $cv ? $cv->getWarrantyCode() : '', 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.warranty_exp_date'));
$pdf->MultiCell($val_width, $line_height, $cv && $cv->getWarrantyExpiration() ? $cv->getWarrantyExpiration()->format("d/m/Y") : '', 0, 'L');
// insert transaction details
$pdf->Ln();
$pdf->SetFont($font_face, 'B', $header_font_size);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.transaction_details'));
$pdf->Ln($line_height * 2);
$pdf->SetFont($font_face, '', $body_font_size);
// get current Y
$y = $pdf->GetY();
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.warranty_class'));
$pdf->MultiCell($val_width, $line_height, WarrantyClass::getName($obj->getWarrantyClass()), 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.mode_of_payment'));
$pdf->MultiCell(0, $line_height, ModeOfPayment::getName($obj->getModeOfPayment()), 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.delivery_address'));
$pdf->MultiCell($val_width, $line_height, $obj->getDeliveryAddress(), 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.landmark'));
$pdf->MultiCell(0, $line_height, $obj->getLandMark(), 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.dispatch_time'));
$pdf->MultiCell($val_width, $line_height, $obj->getDateSchedule() ? $obj->getDateSchedule()->format("g:i A") : '', 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.dispatched_by'));
$pdf->MultiCell(0, $line_height, $obj->getProcessedBy() ? $obj->getProcessedBy()->getFullName() : '', 0, 'L');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
// insert delivery instructions
$pdf->SetY($y);
$pdf->Ln();
$pdf->SetFont($font_face, 'B', $header_font_size);
$pdf->Cell(0, $line_height, $translator->trans('label.pdf.delivery_instructions'));
$pdf->Ln();
$pdf->SetFont($font_face, '', $body_font_size);
$pdf->MultiCell(0, $line_height, $obj->getDeliveryInstructions(), 1, 'L');
// insert invoice details
$pdf->Ln();
$pdf->SetFont($font_face, 'B', $header_font_size);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.invoice_details'));
$pdf->Ln();
// invoice table headers
$invoice = $obj->getInvoice();
$pdf->SetFont($font_face, 'B', $header_font_size);
$pdf->Cell($table_col_width * 6, $table_line_height, $translator->trans('label.pdf.item'), 1, 0, 'L', 1);
$pdf->Cell($table_col_width * 2, $table_line_height, $translator->trans('label.pdf.quantity'), 1, 0, 'R', 1);
$pdf->Cell($table_col_width * 2, $table_line_height, $translator->trans('label.pdf.unit_price'), 1, 0, 'R', 1);
$pdf->Cell($table_col_width * 2, $table_line_height, $translator->trans('label.pdf.amount'), 1, 1, 'R', 1);
$pdf->SetFont($font_face, '', $body_font_size);
// build invoice items table
if ($invoice && !empty($invoice->getItems()))
{
foreach ($invoice->getItems() as $item)
{
$pdf->Cell($table_col_width * 6, $table_line_height, $item->getTitle(), 1);
$pdf->Cell($table_col_width * 2, $table_line_height, number_format($item->getQuantity()), 1, 0, 'R');
$pdf->Cell($table_col_width * 2, $table_line_height, number_format($item->getPrice(), 2), 1, 0, 'R');
$pdf->Cell($table_col_width * 2, $table_line_height, number_format($item->getPrice() * $item->getQuantity(), 2), 1, 1, 'R');
}
}
else
{
$pdf->Cell($table_col_width * 12, 7, 'No items', 1, 1);
}
$pdf->Ln($line_height * 2);
// get current Y
$y = $pdf->GetY();
// insert invoice footer details
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.transaction_type'));
$pdf->MultiCell($val_width, $line_height, ServiceType::getName($obj->getServiceType()), 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->SetFont($font_face, 'B');
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.subtotal'));
$pdf->SetFont($font_face, '');
$pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getVATExclusivePrice(), 2) : '', 0, 'R');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.or_name'));
$pdf->MultiCell($val_width, $line_height, $obj->getORName(), 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->SetFont($font_face, 'B');
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.tax'));
$pdf->SetFont($font_face, '');
$pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getVAT(), 2) : '', 0, 'R');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.emp_id_ref'));
$pdf->MultiCell($val_width, $line_height, $obj->getPromoDetail(), 0, 'L');
// get Y after left cell
$y1 = $pdf->GetY();
$pdf->SetXY($col2_x, $y);
$pdf->SetFont($font_face, 'B');
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.discount'));
$pdf->SetFont($font_face, '');
$pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getDiscount(), 2) : '', 0, 'R');
// get Y after right cell
$y2 = $pdf->GetY();
// get row height
$y = max($y1, $y2);
$pdf->SetXY($col1_x, $y);
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.discount_type'));
$pdf->MultiCell($val_width, $line_height, $invoice && $invoice->getPromo() ? $invoice->getPromo()->getName() : '', 0, 'L');
$pdf->SetXY($col2_x, $y);
$pdf->SetFont($font_face, 'B');
$pdf->Cell($label_width, $line_height, $translator->trans('label.pdf.final_amount'));
$pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getTotalPrice(), 2) : '', 0, 'R');
$pdf->SetFont($font_face, '');
$params['obj'] = $pdf;
$params['filename'] = $filename;
return $params;
}
public function getTwigTemplate($id)
{
if (isset($this->template_hash[$id]))
{
return $this->template_hash[$id];
}
return null;
}
public function getOtherParameters()
{
// get riders for dropdown
$params['riders'] = $this->em->getRepository(Rider::class)->findAll();
// get hubs for dropdown
$params['hubs'] = $this->em->getRepository(Hub::class)->findBy(['flag_hub_view' => true]);
return $params;
}
public function updateVehicleBattery(JobOrder $jo)
{
// check if new battery
if (!($this->checkIfNewBattery($jo)))
return;
// customer vehicle
$cv = $jo->getCustomerVehicle();
if ($cv == null)
return;
// invoice
$invoice = $jo->getInvoice();
if ($invoice == null)
return;
// invoice items
$items = $invoice->getItems();
if (count($items) <= 0)
return;
// get first battery from invoice
$battery = null;
foreach ($items as $item)
{
$battery = $item->getBattery();
if ($battery != null)
break;
}
// no battery in order
if ($battery == null)
return;
// warranty expiration
$warr = $jo->getWarrantyClass();
$warr_months = 0;
if ($warr == WarrantyClass::WTY_PRIVATE)
$warr_months = $battery->getWarrantyPrivate();
else if ($warr == WarrantyClass::WTY_COMMERCIAL)
$warr_months = $battery->getWarrantyCommercial();
else if ($warr == WarrantyClass::WTY_TNV)
$warr_months = 12;
$warr_date = new DateTime();
$warr_date->add(new DateInterval('P' . $warr_months . 'M'));
// update customer vehicle battery
$cv->setCurrentBattery($battery)
->setHasMotoliteBattery(true)
->setWarrantyExpiration($warr_date);
}
public function checkIfNewBattery(JobOrder $jo)
{
if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW)
return true;
return false;
}
protected function getHubRiderCounts($hub)
{
$em = $this->em;
$hub_id = $hub->getID();
// get in transit rider count aka flag_available is false AND current_jo_id is not null AND rider is active
$in_transit_query = $em->createQuery('SELECT count(r.id) FROM App\Entity\Rider r JOIN r.hub h WHERE h.id = :hub_id AND r.flag_available = false AND r.current_job_order IS NOT NULL AND r.flag_active = true');
$in_transit_query->setParameters(['hub_id' => $hub_id]);
$in_transit_count = $in_transit_query->getSingleScalarResult();
// get available rider count aka flag_available is true AND current_jo_id is null AND rider is active
$available_query = $em->createQuery('SELECT count(r.id) FROM App\Entity\Rider r JOIN r.hub h WHERE h.id = :hub_id AND r.flag_available = true AND r.current_job_order IS NULL AND r.flag_active = true');
$available_query->setParameters(['hub_id' => $hub_id]);
$available_count = $available_query->getSingleScalarResult();
// on duty count = in transit + available
$logged_in_count = $in_transit_count + $available_count;
$rider_count = [
'logged_in' => $logged_in_count,
'in_transit' => $in_transit_count,
'available' => $available_count,
];
return $rider_count;
}
protected function fillDropdownParameters(&$params)
{
$em = $this->em;
// db loaded
$params['bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll();
$params['trade_in_bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll();
$params['promos'] = $em->getRepository(Promo::class)->findAll();
// list of batteries for trade-in
$ti_batteries = $em->getRepository(Battery::class)->findAll();
$trade_in_batteries = [];
foreach ($ti_batteries as $ti_battery)
{
$battery_name = $ti_battery->getModel()->getName() . ' ' . $ti_battery->getSize()->getName();
$trade_in_batteries[$ti_battery->getID()] = $battery_name;
}
$params['trade_in_batteries'] = $trade_in_batteries;
// list of emergency types
$e_types = $em->getRepository(EmergencyType::class)->findBy([], ['name' => 'ASC']);
$emergency_types = [];
foreach ($e_types as $e_type)
{
$emergency_types[$e_type->getID()] = $e_type->getName();
}
$params['emergency_types'] = $emergency_types;
// list of ownership types
$owner_types = $em->getRepository(OwnershipType::class)->findBy([]);
$ownership_types = [];
foreach ($owner_types as $ownership_type)
{
$ownership_types[$ownership_type->getID()] = $ownership_type->getName();
}
$params['ownership_types'] = $ownership_types;
// list of customer locations
$cust_locations = $em->getRepository(CustomerLocation::class)->findBy([], ['name' => 'ASC']);
$c_locations = [];
foreach ($cust_locations as $cust_location)
{
$c_locations[$cust_location->getID()] = $cust_location->getName();
}
$params['cust_locations'] = $c_locations;
// list of hubs
$hubs = $em->getRepository(Hub::class)->findBy([], ['name' => 'ASC']);
$fac_hubs = [];
foreach ($hubs as $hub)
{
$fac_hubs[$hub->getID()] = $hub->getName() . ' - ' . $hub->getBranch();
}
// list of customer tags
$params['customer_tags'] = $em->getRepository(CustomerTag::class)->findAll();
// name values
$params['service_types'] = ServiceType::getCollection();
$params['warranty_classes'] = WarrantyClass::getCollection();
$params['modes_of_payment'] = ModeOfPayment::getCollection();
$params['statuses'] = JOStatus::getCollection();
$params['discount_apply'] = DiscountApply::getCollection();
$params['trade_in_types'] = TradeInType::getCollection();
$params['facilitated_types'] = FacilitatedType::getCollection();
$params['facilitated_hubs'] = $fac_hubs;
$params['sources'] = TransactionOrigin::getCollection();
$params['willing_to_wait_content'] = WillingToWaitContent::getCollection();
$params['no_wait_reasons'] = CustomerNotWaitReason::getCollection();
$params['no_trade_in_reasons'] = NoTradeInReason::getCollection();
$params['soa_types'] = SourceOfAwareness::getCollection();
$params['initial_concern'] = InitialConcern::getCollection();
$params['genders'] = Gender::getCollection();
$params['caller_classifications'] = CallerClassification::getCollection();
}
protected function initFormTags(&$params)
{
// default to editing, as we have more forms editing than creating
$params['ftags'] = [
'title' => 'Job Order Form',
'vehicle_dropdown' => false,
'invoice_edit' => false,
'set_map_coordinate' => true,
'preset_vehicle' => false,
'ticket_table' => true,
'cancel_button' => true,
];
}
protected function fillFormTags(&$params)
{
$this->initFormTags($params);
switch ($params['mode'])
{
case 'create':
$params['ftags']['vehicle_dropdown'] = true;
$params['ftags']['set_map_coordinate'] = false;
$params['ftags']['invoice_edit'] = true;
$params['ftags']['ticket_table'] = false;
$params['ftags']['cancel_button'] = false;
break;
case 'create_vehicle':
$params['ftags']['set_map_coordinate'] = false;
$params['ftags']['invoice_edit'] = true;
$params['ftags']['preset_vehicle'] = true;
$params['ftags']['ticket_table'] = false;
$params['ftags']['cancel_button'] = false;
break;
case 'open_edit':
$params['ftags']['invoice_edit'] = true;
$params['ftags']['preset_vehicle'] = true;
break;
case 'onestep':
$params['ftags']['vehicle_dropdown'] = true;
$params['ftags']['set_map_coordinate'] = false;
$params['ftags']['invoice_edit'] = true;
$params['ftags']['ticket_table'] = false;
$params['ftags']['cancel_button'] = false;
break;
case 'onestep-edit':
$params['ftags']['invoice_edit'] = true;
$params['ftags']['preset_vehicle'] = true;
break;
}
}
protected function loadTemplates()
{
$this->template_hash = [];
// add all twig templates for job order to hash
// TODO: put this in an array declaration
// $this->template_hash = [
// 'blah' => 'blah',
// ];
$this->template_hash['jo_incoming_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_open_edit_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_incoming_vehicle_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_processing_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_assigning_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_fulfillment_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_open_hub_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_open_rider_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_all_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_list_processing'] = 'job-order/list.processing.html.twig';
$this->template_hash['jo_list_assigning'] = 'job-order/list.assigning.html.twig';
$this->template_hash['jo_list_fulfillment'] = 'job-order/list.fulfillment.html.twig';
$this->template_hash['jo_list_open'] = 'job-order/list.open.html.twig';
$this->template_hash['jo_list_all'] = 'job-order/list.all.html.twig';
$this->template_hash['jo_popup'] = 'job-order/popup.html.twig';
$this->template_hash['jo_hub_list'] = 'job-order/list.hubview.html.twig';
$this->template_hash['jo_hub_view_form'] = 'job-order/form.html.twig';
$this->template_hash['jo_all_view_form'] = 'job-order/form.view.html.twig';
}
protected function checkTier($tier)
{
// check specified tier
switch ($tier) {
case 'proc':
$tier_key = 'jo_proc';
$tier_name = 'Dispatch';
$rows_route = 'jo_proc_rows';
$edit_route = 'jo_proc_form';
$unlock_route = 'jo_proc_unlock';
$jo_status = JOStatus::PENDING;
break;
case 'assign':
$tier_key = 'jo_assign';
$tier_name = 'Assigning';
$rows_route = 'jo_assign_rows';
$edit_route = 'jo_assign_form';
$unlock_route = 'jo_assign_unlock';
$jo_status = JOStatus::RIDER_ASSIGN;
break;
case 'fulfill':
$tier_key = 'jo_fulfill';
$tier_name = 'Fullfillment';
$rows_route = 'jo_fulfill_rows';
$edit_route = 'jo_fulfill_form';
$unlock_route = '';
$jo_status = [
JOStatus::ASSIGNED,
JOStatus::IN_PROGRESS
];
break;
case 'open':
$tier_key = 'jo_open';
$tier_name = 'Open';
$rows_route = 'jo_open_rows';
$edit_route = '';
$unlock_route = '';
$jo_status = [
JOStatus::PENDING,
JOStatus::RIDER_ASSIGN,
JOStatus::ASSIGNED,
JOStatus::IN_PROGRESS,
JOStatus::IN_TRANSIT,
];
break;
case 'all':
$tier_key = 'jo_open';
$tier_name = 'Open';
$rows_route = 'jo_open_rows';
$edit_route = 'jo_all_form';
$unlock_route = '';
$jo_status = '';
break;
case 'hub_view_all':
$tier_key = 'jo_hub';
$tier_name = 'Open';
$rows_route = 'jo_hub_view_rows';
$edit_route = 'jo_hub_view_form';
$unlock_route = '';
$jo_status = [
JOStatus::PENDING,
JOStatus::RIDER_ASSIGN,
JOStatus::ASSIGNED,
JOStatus::IN_PROGRESS,
JOStatus::IN_TRANSIT,
];
break;
default:
throw new AccessDeniedHttpException('No access.');
}
// check acl
if (!($this->security->isGranted($tier_key . '.list')))
throw new AccessDeniedHttpException('No access.');
// return params if allowed access
return [
'key' => $tier_key,
'name' => $tier_name,
'rows_route' => $rows_route,
'edit_route' => $edit_route,
'unlock_route' => $unlock_route,
'jo_status' => $jo_status
];
}
protected function getRiderDataForAssignment($obj)
{
$hub = $obj->getHub();
$rider_data = [];
// get all active riders of hub
$active_riders = $hub->getActiveRiders();
// need to get first name, last name, contact number, plate number, status, image file
// status: Online (rider is logged in app), Offline (rider is not logged in app), In transit
foreach ($active_riders as $rider)
{
// in transit - flag_available = 0 AND current_job_order is not null
// online - flag_available = 1
// offline - flag_available = 0 AND current_job_order is null
$rider_status = '';
if ($rider->isAvailable())
{
$rider_status = 'Online';
}
else
{
if ($rider->getCurrentJobOrder() == null)
$rider_status = 'Offline';
else
$rider_status = 'In transit';
}
$rider_data[$rider->getID()] = [
'first_name' => $rider->getFirstName(),
'last_name' => $rider->getLastName(),
'contact_number' => $rider->getContactNumber(),
'plate_number' => $rider->getPlateNumber(),
'image_file' => $rider->getImageFile(),
'status' => $rider_status,
];
}
return $rider_data;
}
// TODO: re-enable search, figure out how to group the orWhere filters into one, so can execute that plus the pending filter
// check if datatable filter is present and append to query
protected function setQueryFilters($datatable, &$query, $qb, $hubs, $tier, $status)
{
switch ($tier)
{
case 'fulfill':
if (isset($datatable['query']['data-rows-search']))
{
/*
$query->innerJoin('q.cus_vehicle', 'cv')
->innerJoin('q.customer', 'c')
->where('cv.plate_number like :filter')
->orWhere('c.phone_mobile like :filter')
->orWhere('c.first_name like :filter or c.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%'); */
$query->where('q.plate_number like :filter')
->orWhere('q.phone_mobile like :filter')
->orWhere('q.first_name like :filter')
->orWhere('q.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
}
if (isset($datatable['query']['rider']))
{
$query->innerJoin('q.rider', 'r')
->andWhere('r.id = :rider_id')
->setParameter('rider_id', $datatable['query']['rider']);
}
if (isset($datatable['query']['schedule_date']))
{
$start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00';
$end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00';
$date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start);
$date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end);
$query->andWhere('q.date_schedule >= :date_start')
->andWhere('q.date_schedule <= :date_end')
->setParameter('date_start', $date_start)
->setParameter('date_end', $date_end);
}
$query->andWhere('q.status IN (:statuses)')
->andWhere('q.hub IN (:hubs)')
->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY)
->setParameter('hubs', $hubs, Connection::PARAM_STR_ARRAY);
break;
case 'assign':
$query->where('q.status = :status')
->andWhere('q.hub IN (:hubs)')
->setParameter('status', $status)
->setParameter('hubs', $hubs, Connection::PARAM_STR_ARRAY);
break;
case 'open':
if (isset($datatable['query']['data-rows-search']))
{
/*
$query->innerJoin('q.cus_vehicle', 'cv')
->innerJoin('q.customer', 'c')
->where('q.status IN (:statuses)')
->andWhere('cv.plate_number like :filter or c.first_name like :filter or c.last_name like :filter or c.phone_mobile like :filter')
->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY)
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
*/
$query->where('q.plate_number like :filter')
->orWhere('q.phone_mobile like :filter')
->orWhere('q.first_name like :filter')
->orWhere('q.last_name like :filter')
->orWhere('q.status IN (:statuses)')
->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY)
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
}
else
{
$query->where('q.status IN (:statuses)')
->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY);
}
if (isset($datatable['query']['rider']))
{
$query->innerJoin('q.rider', 'r')
->andWhere('r.id = :rider_id')
->setParameter('rider_id', $datatable['query']['rider']);
}
if (isset($datatable['query']['schedule_date']))
{
$start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00';
$end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00';
$date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start);
$date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end);
$query->andWhere('q.date_schedule >= :date_start')
->andWhere('q.date_schedule <= :date_end')
->setParameter('date_start', $date_start)
->setParameter('date_end', $date_end);
}
break;
case 'all':
if (isset($datatable['query']['data-rows-search']))
{
/*
$query->innerJoin('q.cus_vehicle', 'cv')
->innerJoin('q.customer', 'c')
->where('cv.plate_number like :filter')
->orWhere('c.phone_mobile like :filter')
->orWhere('c.first_name like :filter or c.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
*/
$query->where('q.plate_number like :filter')
->orWhere('q.phone_mobile like :filter')
->orWhere('q.first_name like :filter')
->orWhere('q.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
}
if (isset($datatable['query']['rider']))
{
$query->innerJoin('q.rider', 'r')
->andWhere('r.id = :rider_id')
->setParameter('rider_id', $datatable['query']['rider']);
}
if (isset($datatable['query']['schedule_date']))
{
$start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00';
$end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00';
$date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start);
$date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end);
$query->andWhere('q.date_schedule >= :date_start')
->andWhere('q.date_schedule <= :date_end')
->setParameter('date_start', $date_start)
->setParameter('date_end', $date_end);
}
break;
case 'hub_view_all':
if (isset($datatable['query']['data-rows-search']))
{
/*
$query->innerJoin('q.cus_vehicle', 'cv')
->innerJoin('q.customer', 'c')
->where('cv.plate_number like :filter')
->orWhere('c.phone_mobile like :filter')
->orWhere('c.first_name like :filter or c.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
*/
$query->where('q.plate_number like :filter')
->orWhere('q.phone_mobile like :filter')
->orWhere('q.first_name like :filter')
->orWhere('q.last_name like :filter')
->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
}
if (isset($datatable['query']['hub']))
{
$query->innerJoin('q.hub', 'h')
->andWhere('h.id = :hub_id')
->setParameter('hub_id', $datatable['query']['hub']);
}
else
{
$query->innerJoin('q.hub', 'h')
->andWhere('h.flag_hub_view = :flag_hub_view')
->setParameter('flag_hub_view', true);
}
if (isset($datatable['query']['schedule_date']))
{
$start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00';
$end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00';
$date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start);
$date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end);
$query->andWhere('q.date_schedule >= :date_start')
->andWhere('q.date_schedule <= :date_end')
->setParameter('date_start', $date_start)
->setParameter('date_end', $date_end);
}
else
{
$c_date = new DateTime();
$start_curr_date = $c_date->format('Y-m-d') . ' ' . '00:00:00';
$end_curr_date = $c_date->format('Y-m-d') . ' ' . '23:59:00';
$start_current_date = DateTime::createFromFormat('Y-m-d H:i:s', $start_curr_date);
$end_current_date = DateTime::createFromFormat('Y-m-d H:i:s', $end_curr_date);
$query->andWhere('q.date_schedule >= :start_current_date')
->andWhere('q.date_schedule <= :end_current_date')
->andWhere('q.status IN (:statuses)')
->setParameter('start_current_date', $start_current_date)
->setParameter('end_current_date', $end_current_date)
->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY);
}
break;
case 'proc':
$query->where('q.status = :status')
->andWhere('q.source <> :source')
->setParameter('status', $status)
->setParameter('source', TransactionOrigin::MOBILE_APP);
break;
default:
$query->where('q.status = :status')
->setParameter('status', $status);
}
}
public function getEditRoute($jo_id, $tier)
{
if (empty($tier))
return 'jo_open_edit_form';
return $tier;
}
public function getHubViewRows(Request $req, $tier)
{
// check which job order tier is being called for and confirm access
$tier_params = $this->checkTier($tier);
// get current user
$user = $this->security->getUser();
if ($user == null)
throw new AccessDeniedHttpException('No access.');
$hubs = $user->getHubs();
// get query builder
$qb = $this->em->getRepository(JobOrder::class)
->createQueryBuilder('q');
// get datatable params
$datatable = $req->request->get('datatable');
// count total records
$tquery = $qb->select('COUNT(q)');
$this->setQueryFilters($datatable, $tquery, $qb, $hubs, $tier, $tier_params['jo_status']);
$total = $tquery->getQuery()
->getSingleScalarResult();
// get current page number
$page = $datatable['pagination']['page'] ?? 1;
$perpage = $datatable['pagination']['perpage'];
$offset = ($page - 1) * $perpage;
// add metadata
$meta = [
'page' => $page,
'perpage' => $perpage,
'pages' => ceil($total / $perpage),
'total' => $total,
'sort' => 'asc',
'field' => 'id'
];
// build query
$qb = $this->em->getRepository(JobOrder::class)
->createQueryBuilder('q');
$query = $qb->select('q');
$this->setQueryFilters($datatable, $query, $qb, $hubs, $tier, $tier_params['jo_status']);
// check if sorting is present, otherwise use default
if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) {
$order = $datatable['sort']['sort'] ?? 'asc';
$query->orderBy('q.' . $datatable['sort']['field'], $order);
} else {
$query->orderBy('q.date_schedule', 'asc');
}
// get rows for this page
$query_obj = $query->setFirstResult($offset)
->setMaxResults($perpage)
->getQuery();
// error_log($query_obj->getSQL());
$obj_rows = $query_obj->getResult();
$statuses = JOStatus::getCollection();
$service_types = ServiceType::getCollection();
// process rows
$rows = [];
foreach ($obj_rows as $orow) {
// add row data
$row['id'] = $orow->getID();
$row['customer_name'] = $orow->getCustomer()->getFirstName() . ' ' . $orow->getCustomer()->getLastName();
$row['delivery_address'] = $orow->getDeliveryAddress();
$row['date_schedule'] = $orow->getDateSchedule()->format("d M Y g:i A");
$row['type'] = $orow->isAdvanceOrder() ? 'Advanced Order' : 'Immediate';
$row['service_type'] = $service_types[$orow->getServiceType()];
$row['status'] = $statuses[$orow->getStatus()];
$row['flag_advance'] = $orow->isAdvanceOrder();
$row['plate_number'] = $orow->getCustomerVehicle()->getPlateNumber();
$row['is_mobile'] = $orow->getSource() == TransactionOrigin::MOBILE_APP;
$processor = $orow->getProcessedBy();
if ($processor == null)
$row['processor'] = '';
else
$row['processor'] = $orow->getProcessedBy()->getFullName();
$assignor = $orow->getAssignedBy();
if ($assignor == null)
$row['assignor'] = '';
else
$row['assignor'] = $orow->getAssignedBy()->getFullName();
$rows[] = $row;
}
$params['meta'] = $meta;
$params['rows'] = $rows;
$params['tier_params'] = $tier_params;
return $params;
}
public function initializeHubViewForm($id)
{
$em = $this->em;
$params['mode'] = 'update-all';
// get row data
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this row exists
if (empty($obj))
throw new NotFoundHttpException('The job order does not exist');
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get template to display
$params['template'] = $this->getTwigTemplate('jo_hub_view_form');
$params['obj'] = $obj;
$params['status_cancelled'] = JOStatus::CANCELLED;
// timeline stuff (descending by time)
$params['timeline'] = [
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 4",
'color' => "#f4516c"
],
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 3",
'color' => "#34bfa3"
],
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 2",
'color' => "#716aca"
],
[
'date' => date("M j"),
'time' => date("g:i A"),
'event' => "Event 1",
'color' => "#ffb822"
],
];
return $params;
}
public function fulfillCancelledJobOrder(Request $req, $id)
{
// initialize error list
$error_array = [];
// get object data
$em = $this->em;
$obj = $em->getRepository(JobOrder::class)->find($id);
// make sure this object exists
if (empty($obj))
throw new NotFoundHttpException('The item does not exist');
$obj->fulfill();
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
->setTypeID(JOEventType::FULFILL)
->setJobOrder($obj);
// get current user
$user = $this->security->getUser();
if ($user != null)
{
$event->setUser($user);
}
$event->setUser($user);
$em->persist($event);
$em->flush();
}
public function sendSMSToCustomer($phone_number)
{
// TODO: put this in config file or somewhere
$message = $this->translator->trans('message.joborder_completed');
$this->rt->sendSMS($phone_number, $this->translator->trans('message.battery_brand_allcaps'), $message);
}
protected function sendSMSHubRejection($rejection, $jo)
{
$hub = $rejection->getHub();
// check if JO service type is NOT battery sales
if ($jo->getServiceType() !== ServiceType::BATTERY_REPLACEMENT_NEW)
return null;
// check if hub has a notification number
$notif_number = $hub->getNotifNumber();
if (empty($notif_number))
return null;
$phone_number = $this->country_code . $notif_number;
// check if reason is administrative
if ($rejection->getReason() == JORejectionReason::ADMINISTRATIVE)
return null;
// check if reason is discount
if ($rejection->getReason() == JORejectionReason::DISCOUNT)
return null;
// sms content
// Job Order # - can get from jo
// Order Date and Time - get from jo
// Date and Time Rejected - get from rejection
// Enrollee Name - get from rejection ($hub->getName() . ' - ' . $hub->getBranch())
// Reason of Rejection - get from rejection
// Remarks - get from rejection
// Type of Service - get from jo
$jo_id = $jo->getID();
// convert to string format the date fields (order date and time and date and time rejected)
$order_date_time = $jo->getDateCreate()->format('d M Y g:i A');
$reject_date_time = $rejection->getDateCreate()->format('d M Y g:i A');
$enrollee = $hub->getName() . ' - ' . $hub->getBranch();
$reason = JORejectionReason::getName($rejection->getReason());
$remarks = $rejection->getRemarks();
$service_type = ServiceType::getName($jo->getServiceType());
$msg = 'Job Order #: ' . $jo_id . "\n" . 'Order Date and Time: ' . $order_date_time . "\n" .
'Date and Time Rejected: ' . $reject_date_time . "\n" . 'Enrollee Name: ' . $enrollee . "\n" .
'Reason of Rejection: ' . $reason . "\n" . 'Remarks: ' . $remarks . "\n" .
'Type of Service: ' . $service_type;
// error_log($msg);
$this->rt->sendSMS($phone_number, $this->translator->trans('message.battery_brand_allcaps'), $msg);
}
protected function skipInventoryCheck(Point $coordinates): bool
{
$long = $coordinates->getLongitude();
$lat = $coordinates->getLatitude();
// get supported area given a set of coordinates
$query = $this->em->createQuery('SELECT s from App\Entity\SupportedArea s where st_contains(s.coverage_area, point(:long, :lat)) = true');
$area = $query->setParameter('long', $long)
->setParameter('lat', $lat)
->setMaxResults(1)
->getOneOrNullResult();
if ($area !== null) {
// get all exceptions
$exceptions = $area->getHubFilterExceptions();
if (isset($exceptions['no_inventory'])) {
return true;
}
}
// filter is in place
return false;
}
}