Merge branch '594-hub-filter-report' into 'master-fix'

Resolve "Hub Filter Report"

See merge request jankstudio/resq!713
This commit is contained in:
Kendrick Chan 2021-07-01 07:14:50 +00:00
commit bad6de1ed2
11 changed files with 313 additions and 22 deletions

View file

@ -344,6 +344,8 @@ access_keys:
label: Advance Order Job Order Report
- id: report.customer.source
label: Customer Source Report
- id: report.hub.filter
label: Hub Filter Report
- id: service
label: Other Services

View file

@ -137,3 +137,13 @@ rep_customer_source_submit:
path: /report/customer_source_report
controller: App\Controller\ReportController::customerSourceSubmit
methods: [POST]
rep_hub_filter_form:
path: /report/hub_filter_report
controller: App\Controller\ReportController::hubFilterForm
methods: [GET]
rep_hub_filter_submit:
path: /report/hub_filter_report
controller: App\Controller\ReportController::hubFilterSubmit
methods: [POST]

View file

@ -1043,6 +1043,11 @@ class APIController extends Controller implements LoggedController
if (!empty($sku))
$hub_criteria->addItem($batt->getSAPCode(), 1);
// get customer id. No JO id at this point
$customer_id = $cust->getID();
$hub_criteria->setCustomerId($customer_id);
// find nearest hubs
$nearest_hubs = $hub_select->find($hub_criteria);
@ -2769,6 +2774,11 @@ class APIController extends Controller implements LoggedController
if (!empty($sku))
$hub_criteria->addItem($batt->getSAPCode(), 1);
// get customer id. No JO id at this point
$customer_id = $cust->getID();
$hub_criteria->setCustomerId($customer_id);
// find nearest hubs
$nearest_hubs = $hub_select->find($hub_criteria);

View file

@ -20,6 +20,7 @@ use App\Entity\Customer;
use App\Entity\BatteryModel;
use App\Entity\BatterySize;
use App\Entity\SMSMessage;
use App\Entity\HubFilterLog;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
@ -1123,6 +1124,62 @@ class ReportController extends Controller
return $resp;
}
/**
* @Menu(selected="outlet_list")
*/
public function hubFilterForm()
{
$this->denyAccessUnlessGranted('report.hub.filter', null, 'No access.');
return $this->render('report/hub-filter/form.html.twig');
}
public function hubFilterSubmit(Request $req, EntityManagerInterface $em)
{
// get dates
$raw_date_start = $req->request->get('date_start');
$raw_date_end = $req->request->get('date_end');
$date_start = DateTime::createFromFormat('m/d/Y', $raw_date_start);
$date_end = DateTime::createFromFormat('m/d/Y', $raw_date_end);
$data = $this->getHubFilterData($req, $em, $raw_date_start, $raw_date_end);
$resp = new StreamedResponse();
$resp->setCallback(function() use ($data) {
// csv output
$csv_handle = fopen('php://output', 'w+');
fputcsv($csv_handle, [
'Filtered Hub ID',
'Hub Name',
'Job Order ID',
'Customer ID',
'Customer Last Name',
'Customer First Name',
'Customer Mobile Number',
'Date Created',
'Reason',
'Date of Rejection',
]);
foreach ($data as $row)
{
fputcsv($csv_handle, $row);
}
fclose($csv_handle);
});
$filename = 'hub_filter_' . $date_start->format('Ymd') . '_' . $date_end->format('Ymd') . '.csv';
$resp->setStatusCode(200);
$resp->headers->set('Content-Type', 'text/csv; charset=utf-8');
$resp->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"');
return $resp;
}
protected function processPopappFile(UploadedFile $csv_file, EntityManagerInterface $em)
{
// attempt to open file
@ -2261,6 +2318,47 @@ class ReportController extends Controller
return $result;
}
protected function getHubFilterData($req, $em, $raw_date_start, $raw_date_end)
{
$date_start = DateTime::createFromFormat('m/d/Y', $raw_date_start);
$date_end = DateTime::createFromFormat('m/d/Y', $raw_date_end);
// change to this format: Y-m-d H:i:s
$new_date_start = $date_start->format('Y-m-d H:i:s');
$new_date_end = $date_end->format('Y-m-d H:i:s');
// pdo connection. Table gets big very fast.
$db = $em->getConnection();
// get hub filter logs
$sql = 'SELECT hf.hub_filtered_id, h.name, hf.jo_id, hf.customer_id, c.last_name, c.first_name, c.phone_mobile, hf.date_create, hf.filter_type_id FROM hub_filter_log hf, hub h, customer c WHERE hf.hub_filtered_id=h.id AND c.id=hf.customer_id AND hf.date_create >= :date_start AND hf.date_create <= :date_end ORDER BY hf.date_create';
$stmt = $db->prepare($sql);
$stmt->execute([
'date_start' => $new_date_start,
'date_end' => $new_date_end,
]);
$result = [];
// go through rows
while ($row = $stmt->fetch(PDO::FETCH_NUM))
{
$result[] = [
$row[0], // filtered hub id
$row[1], // hub name
$row[2], // job order id
$row[3], // customer id
$row[4], // customer last name
$row[5], // customer first name
$row[6], // customer mobile
$row[7], // date create
$row[8], // filter type id
$row[7], // date of rejection
];
}
return $result;
}
protected function getRESQCustomerIDs(EntityManagerInterface $em)
{
// pdo connection

View file

@ -44,9 +44,23 @@ class HubFilterLog
*/
protected $filter_type_id;
// jo id that made request
/**
* @ORM\Column(type="integer", nullable=true)
*/
protected $jo_id;
// customer id that made request
/**
* @ORM\Column(type="integer", nullable=true)
*/
protected $customer_id;
public function __construct()
{
$this->date_create = new DateTime();
$this->jo_id = null;
$this->customer_id = null;
}
public function getID()
@ -80,5 +94,28 @@ class HubFilterLog
{
return $this->filter_type_id;
}
public function setJobOrderId($jo_id)
{
$this->jo_id = $jo_id;
return $this;
}
public function getJobOrderId()
{
return $this->jo_id;
}
public function setCustomerId($customer_id)
{
$this->customer_id = $customer_id;
return $this;
}
public function getCustomerId()
{
return $this->customer_id;
}
}

View file

@ -18,6 +18,8 @@ class HubCriteria
protected $payment_method; // payment method of JO
protected $flag_emergency; // flag if emergency or not
protected $flag_round_robin; // flag if we use round robin or not
protected $jo_id; // JO id. This is null if called from mobile API
protected $customer_id; // customer id
public function __construct()
{
@ -31,6 +33,8 @@ class HubCriteria
$this->payment_method = '';
$flag_emergency = false;
$flag_round_robin = false;
$jo_id = null;
$customer_id = null;
}
public function setPoint(Point $point)
@ -145,5 +149,27 @@ class HubCriteria
return $this->flag_round_robin;
}
public function setJobOrderId($jo_id)
{
$this->jo_id = $jo_id;
return $this;
}
public function getJobOrderId()
{
return $this->jo_id;
}
public function setCustomerId($customer_id)
{
$this->customer_id = $customer_id;
return $this;
}
public function getCustomerId()
{
return $this->customer_id;
}
}

View file

@ -16,12 +16,14 @@ class HubFilterLogger
$this->em = $em;
}
public function logFilteredHub(Hub $hub, $filter_type)
public function logFilteredHub(Hub $hub, $filter_type, $jo_id, $customer_id)
{
$hub_filter_log = new HubFilterLog();
$hub_filter_log->setHub($hub)
->setFilterTypeId($filter_type);
->setFilterTypeId($filter_type)
->setJobOrderId($jo_id)
->setCustomerId($customer_id);
$this->em->persist($hub_filter_log);
$this->em->flush();

View file

@ -51,37 +51,39 @@ class HubSelector
$payment_method = $criteria->getPaymentMethod();
$flag_emergency = $criteria->isEmergency();
$flag_round_robin = $criteria->isRoundRobin();
$jo_id = $criteria->getJobOrderId();
$customer_id = $criteria->getCustomerId();
$results = [];
// get all the hubs within distance
$filtered_hubs = $this->getClosestHubs($point, $limit_distance);
$filtered_hubs = $this->getClosestHubs($point, $limit_distance, $jo_id, $customer_id);
error_log('closest hubs ' . json_encode($filtered_hubs));
if (!$flag_emergency)
{
// filter the first hub results for date and opening times
$hubs_date_time = $this->filterHubsByDateAndTime($filtered_hubs, $date_time);
$hubs_date_time = $this->filterHubsByDateAndTime($filtered_hubs, $date_time, $jo_id, $customer_id);
$filtered_hubs = $hubs_date_time;
//error_log('date_time hubs ' . json_encode($filtered_hubs));
// filter jo types
$hubs_jo_type = $this->filterHubsByJoType($filtered_hubs, $jo_type);
$hubs_jo_type = $this->filterHubsByJoType($filtered_hubs, $jo_type, $jo_id, $customer_id);
$filtered_hubs = $hubs_jo_type;
//error_log('jo_type hubs ' . json_encode($filtered_hubs));
// filter hubs by payment methods
$hubs_payment_method = $this->filterHubsByPaymentMethod($filtered_hubs, $payment_method);
$hubs_payment_method = $this->filterHubsByPaymentMethod($filtered_hubs, $payment_method, $jo_id, $customer_id);
$filtered_hubs = $hubs_payment_method;
//error_log('payment hubs ' . json_encode($filtered_hubs));
// inventory filter
$hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check,
$jo_type, $items);
$jo_type, $items, $jo_id, $customer_id);
$filtered_hubs = $hubs_inventory;
//error_log('inventory hubs ' . json_encode($filtered_hubs));
@ -93,7 +95,7 @@ class HubSelector
//error_log('round robin hubs ' . json_encode($filtered_hubs));
// max results filter
$hubs_max_result = $this->filterHubsByMaxResults($filtered_hubs, $limit_results);
$hubs_max_result = $this->filterHubsByMaxResults($filtered_hubs, $limit_results, $jo_id, $customer_id);
$filtered_hubs = $hubs_max_result;
}
@ -120,7 +122,7 @@ class HubSelector
}
protected function filterHubsByMaxResults($hubs, $limit_result)
protected function filterHubsByMaxResults($hubs, $limit_result, $jo_id, $customer_id)
{
if (empty($hubs))
return $hubs;
@ -133,13 +135,13 @@ class HubSelector
if ($i < $limit_result)
$results[] = $hubs[$i];
else
$this->hub_filter_logger->logFilteredHub($hubs[$i]['hub'], 'max_results');
$this->hub_filter_logger->logFilteredHub($hubs[$i]['hub'], 'max_results', $jo_id, $customer_id);
}
return $results;
}
protected function filterHubsByJoType($hubs, $jo_type)
protected function filterHubsByJoType($hubs, $jo_type, $jo_id, $customer_id)
{
if (empty($hubs))
return $hubs;
@ -163,13 +165,13 @@ class HubSelector
'duration' => $hub_data['duration'],
];
else
$this->hub_filter_logger->logFilteredHub($hub, 'job_order_type');
$this->hub_filter_logger->logFilteredHub($hub, 'job_order_type', $jo_id, $customer_id);
}
return $results;
}
protected function filterHubsByPaymentMethod($hubs, $payment_method)
protected function filterHubsByPaymentMethod($hubs, $payment_method, $jo_id, $customer_id)
{
if (empty($hubs))
return $hubs;
@ -201,16 +203,16 @@ class HubSelector
}
if (!$flag_found_pmethod)
$this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method');
$this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method', $jo_id, $customer_id);
}
else
$this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method');
$this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method', $jo_id, $customer_id);
}
return $results;
}
protected function filterHubsByDateAndTime($hubs, $date_time)
protected function filterHubsByDateAndTime($hubs, $date_time, $jo_id, $customer_id)
{
if (empty($hubs))
return $hubs;
@ -245,13 +247,13 @@ class HubSelector
];
}
else
$this->hub_filter_logger->logFilteredHub($hub, 'date_and_time');
$this->hub_filter_logger->logFilteredHub($hub, 'date_and_time', $jo_id, $customer_id);
}
return $results;
}
protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items)
protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id)
{
if (empty($hubs))
return $hubs;
@ -290,7 +292,7 @@ class HubSelector
error_log($message);
$this->sendSMSMessage($hub, $items);
$this->hub_filter_logger->logFilteredHub($hub, 'no_inventory');
$this->hub_filter_logger->logFilteredHub($hub, 'no_inventory', $jo_id, $customer_id);
}
}
if ($jo_type == ServiceType::BATTERY_REPLACEMENT_WARRANTY)
@ -317,7 +319,7 @@ class HubSelector
error_log($message);
$this->sendSMSMessage($hub, $items);
$this->hub_filter_logger->logFilteredHub($hub, 'no_inventory');
$this->hub_filter_logger->logFilteredHub($hub, 'no_inventory', $jo_id, $customer_id);
}
}
}
@ -326,7 +328,7 @@ class HubSelector
return $results;
}
protected function getClosestHubs(Point $point, $limit_distance)
protected function getClosestHubs(Point $point, $limit_distance, $jo_id, $customer_id)
{
// get closest hubs based on st_distance function from db
$query_string = 'SELECT h, st_distance(h.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Hub h WHERE h.status_open = true ORDER BY dist';
@ -367,7 +369,7 @@ class HubSelector
}
else
{
$this->hub_filter_logger->logFilteredHub($row[0], 'distance');
$this->hub_filter_logger->logFilteredHub($row[0], 'not_within_distance', $jo_id, $customer_id);
}
}

View file

@ -1996,6 +1996,13 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
if ($willing_to_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
$hub_criteria->setEmergency(true);
// get JO and customer id for logging purposes
$jo_id = $obj->getID();
$customer_id = $obj->getCustomer()->getID();
$hub_criteria->setJobOrderId($jo_id)
->setCustomerId($customer_id);
$hubs = $hub_selector->find($hub_criteria);
$params['hubs'] = [];
@ -2293,6 +2300,13 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
if ($willing_to_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT)
$hub_criteria->setEmergency(true);
// get JO and customer id for logging purposes
$jo_id = $obj->getID();
$customer_id = $obj->getCustomer()->getID();
$hub_criteria->setJobOrderId($jo_id)
->setCustomerId($customer_id);
$hubs = $hub_selector->find($hub_criteria);
$params['status_cancelled'] = JOStatus::CANCELLED;

View file

@ -249,6 +249,14 @@
Customer Source Report
</span>
</a>
<a href="{{ url('rep_hub_filter_form') }}" class="m-menu__link">
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
<span></span>
</i>
<span class="m-menu__link-text">
Hub Filter Report
</span>
</a>
</li>
</ul>
</li>

View file

@ -0,0 +1,82 @@
{% extends 'base.html.twig' %}
{% block body %}
<!-- BEGIN: Subheader -->
<div class="m-subheader">
<div class="d-flex align-items-center">
<div class="mr-auto">
<h3 class="m-subheader__title">Hub Filter Report</h3>
</div>
</div>
</div>
<!-- END: Subheader -->
<div class="m-content">
<!--Begin::Section-->
<div class="row">
<div class="col-xl-6">
<div class="m-portlet m-portlet--mobile">
<div class="m-portlet__head">
<div class="m-portlet__head-caption">
<div class="m-portlet__head-title">
<span class="m-portlet__head-icon">
<i class="fa fa-calendar"></i>
</span>
<h3 class="m-portlet__head-text">
Select a date range
</h3>
</div>
</div>
</div>
<form id="row-form" autocomplete="off" class="m-form m-form--fit m-form--label-align-right m-form--group-seperator-dashed" method="post" action="{{ url('rep_hub_filter_submit') }}">
<div class="m-portlet__body">
<div class="form-group m-form__group row">
<div class="input-daterange input-group" id="date-range">
<input role="presentation" type="text" class="form-control m-input" name="date_start" placeholder="Start date" />
<div class="input-group-append">
<span class="input-group-text"><i class="la la-ellipsis-h"></i></span>
</div>
<input role="presentation" type="text" class="form-control" name="date_end" placeholder="End date" />
</div>
</div>
</div>
<div class="m-portlet__foot m-portlet__foot--fit">
<div class="m-form__actions m-form__actions--solid m-form__actions--right">
<div class="row">
<div class="col-lg-12">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(function() {
$("#date-range").datepicker({
orientation: "bottom"
});
$("#row-form").submit(function(e) {
var form = $(this);
if (!$("[name='date_start']").val() || !$("[name='date_end']").val()) {
e.preventDefault();
swal({
title: 'Whoops!',
text: 'Please fill in both date fields.',
type: 'warning'
});
return false;
}
});
});
</script>
{% endblock %}