Add route and processing for hub rejections #183

This commit is contained in:
Ramon Gutierrez 2019-02-12 00:14:15 +08:00
parent 345ca27a31
commit 623c80421a
4 changed files with 327 additions and 9 deletions

View file

@ -170,3 +170,8 @@ jo_open_edit_submit:
path: /job-order/{id}/open-edit
controller: App\Controller\JobOrderController::openEditSubmit
methods: [POST]
jo_reject_hub:
path: /job-order/{id}/reject-hub
controller: App\Controller\JobOrderController::rejectHubSubmit
methods: [POST]

View file

@ -14,6 +14,7 @@ use App\Ramcar\ModeOfPayment;
use App\Ramcar\TransactionOrigin;
use App\Ramcar\JOEventType;
use App\Ramcar\FacilitatedType;
use App\Ramcar\JORejectionReason;
use App\Entity\JobOrder;
use App\Entity\BatteryManufacturer;
@ -25,6 +26,7 @@ use App\Entity\Promo;
use App\Entity\Rider;
use App\Entity\Battery;
use App\Entity\JOEvent;
use App\Entity\JORejection;
use App\Service\InvoiceCreator;
use App\Service\MapTools;
@ -899,6 +901,12 @@ class JobOrderController extends BaseController
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get rejections
$rejections = $obj->getHubRejections();
// get rejection reasons
$params['rejection_reasons'] = JORejectionReason::getCollection();
// get closest hubs
$hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 50, date("H:i:s"));
@ -930,6 +938,19 @@ class JobOrderController extends BaseController
// 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();
foreach ($rejections as $robj)
{
if ($robj->getHub()->getID() === $hub_id)
{
$hub['flag_rejected'] = true;
break;
}
}
$params['hubs'][] = $hub;
}
@ -1505,6 +1526,12 @@ class JobOrderController extends BaseController
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
// get rejections
$rejections = $obj->getHubRejections();
// get rejection reasons
$params['rejection_reasons'] = JORejectionReason::getCollection();
// get closest hubs
$hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 50, date("H:i:s"));
@ -1538,6 +1565,19 @@ class JobOrderController extends BaseController
$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();
foreach ($rejections as $robj)
{
if ($robj->getHub()->getID() === $hub_id)
{
$hub['flag_rejected'] = true;
break;
}
}
$params['hubs'][] = $hub;
}
@ -2540,4 +2580,103 @@ class JobOrderController extends BaseController
// redirect to list
return $this->redirectToRoute('jo_assign');
}
public function rejectHubSubmit(Request $req, ValidatorInterface $validator, $id)
{
$this->denyAccessUnlessGranted('jo_proc.list', null, 'No access.');
// get object data
$em = $this->getDoctrine()->getManager();
$jo = $em->getRepository(JobOrder::class)->find($id);
$processor = $jo->getProcessedBy();
$user = $this->getUser();
// check if we're the one processing, return error otherwise
if ($processor == null)
throw $this->createAccessDeniedException('Not the processor');
if ($processor != null && $processor->getID() != $user->getID())
throw $this->createAccessDeniedException('Not the processor');
// initialize error list
$error_array = [];
// make sure job order exists
if (empty($jo))
throw $this->createNotFoundException('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)
->setUser($this->getUser())
->setJobOrder($jo)
->setReason($req->request->get('reason'))
->setRemarks($req->request->get('remarks'))
->setContactPerson($req->request->get('contact_person'));
// validate
$errors = $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);
}
// validated! save the entity
$em->persist($obj);
$em->flush();
// return successful response
return $this->json([
'success' => 'Changes have been saved!',
'request' => $req->request->all()
]);
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Ramcar;
class JORejectionReason extends NameValue
{
const ADMINISTRATIVE = 'administrative';
const NO_STOCK_SALES = 'no_stock_sales';
const NO_STOCK_SERVICE = 'no_stock_service';
const LINE_NO_ANSWER = 'line_no_answer';
const LINE_BUSY = 'line_busy';
const NO_RIDER_AVAILABLE = 'no_rider_available';
const NO_RIDER_IN_TRANSIT = 'no_rider_in_transit';
const REFUSAL = 'refusal';
const COLLECTION = [
'administrative' => 'ADMINISTRATIVE',
'no_stock_sales' => 'NO STOCK - SALES',
'no_stock_service' => 'NO STOCK - SERVICE',
'line_no_answer' => 'LINE NO ANSWER',
'line_busy' => 'LINE BUSY',
'no_rider_available' => 'NO RIDER - AVAILABLE',
'no_rider_in_transit' => 'NO RIDER - IN TRANSIT',
'refusal' => 'REFUSAL',
];
}

View file

@ -584,7 +584,11 @@
<td class="text-right">{{ hub.jo_count }}</td>
<td>{{ hub.hub.getContactNumbers|replace({"\n": ', '}) }}</td>
<td>
<button type="button" class="button-reject btn btn-primary">Reject</button>
{% if hub.flag_rejected %}
<button type="button" class="btn btn-danger btn-disabled" disabled>Rejected</button>
{% else %}
<button type="button" data-hub-id="{{ hub.hub.getID }}" data-hub-name="{{ hub.hub.getName }}" data-hub-branch="{{ hub.hub.getBranch }}" class="btn-reject btn btn-info">Reject</button>
{% endif %}
</td>
</tr>
{% endfor %}
@ -823,6 +827,68 @@
</div>
</div>
</div>
{% if mode in ['update-processing', 'update-reassign-hub'] %}
<!-- rejection modal -->
<div id="modal-rejection" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="modal-rejection-label" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<form id="hub-reject-form" class="m-form" method="post" action="{{ url('jo_reject_hub', {'id': obj.getID} ) }}">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-rejection-label">Reject Hub</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="m-form__section m-form__section--first m-form__section--last">
<div class="form-group m-form__group row">
<div class="col-md-6">
<label for="hub-reject-name" data-field="hub_name">Hub</label>
<input type="text" name="hub_name" id="hub-reject-name" class="form-control m-input" readonly>
<div class="form-control-feedback hide" data-field="hub_name"></div>
</div>
<div class="col-md-6">
<label for="hub-reject-branch" data-field="hub_branch">Branch</label>
<input type="text" name="hub_branch" id="hub-reject-branch" class="form-control m-input" readonly>
<div class="form-control-feedback hide" data-field="hub_branch"></div>
</div>
</div>
<div class="form-group m-form__group row">
<div class="col-md-6">
<label for="hub-reject-reason" data-field="reason">Reason</label>
<select id="hub-reject-reason" class="form-control m-input" name="reason">
{% for key, reason in rejection_reasons %}
<option value="{{ key }}">{{ reason }}</option>
{% endfor %}
</select>
<div class="form-control-feedback hide" data-field="reason"></div>
</div>
<div class="col-md-6">
<label for="hub-reject-contact-person" data-field="contact_person">Contact Person</label>
<input type="text" name="contact_person" id="hub-reject-contact-person" class="form-control m-input">
<div class="form-control-feedback hide" data-field="contact_person"></div>
</div>
</div>
<div class="form-group m-form__group row">
<div class="col-12">
<label for="hub-reject-remarks" data-field="remarks">Remarks</label>
<textarea name="remarks" id="hub-reject-remarks" class="form-control m-input" rows="8"></textarea>
<div class="form-control-feedback hide" data-field="remarks"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="hidden" id="hub-reject-id" name="hub">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Reject</button>
</div>
</div>
</form>
</div>
</div>
{% endif %}
{% endblock %}
{% block scripts %}
@ -1014,10 +1080,10 @@ $(function() {
});
// remove all error classes
function removeErrors() {
$(".form-control-danger").removeClass('form-control-danger');
$("[data-field]").removeClass('has-danger');
$(".form-control-feedback[data-field]").addClass('hide');
function removeErrors(formSelector = '') {
$(formSelector + " .form-control-danger").removeClass('form-control-danger');
$(formSelector + " [data-field]").removeClass('has-danger');
$(formSelector + " .form-control-feedback[data-field]").addClass('hide');
}
// store selected vehicle data
@ -1474,10 +1540,92 @@ $(function() {
var ticketTable = $("#data-tickets").mDatatable(ticketOptions);
{% endif %}
// reject job order
$(".button-reject").click(function(e) {
return false;
});
{% if mode in ['update-processing', 'update-reassign-hub'] %}
// reject hub
$(".btn-reject").click(function(e) {
var hubID = $(this).data('hub-id');
var hubName = $(this).data('hub-name');
var hubBranch = $(this).data('hub-branch');
$("#hub-reject-id").val(hubID);
$("#hub-reject-name").val(hubName);
$("#hub-reject-branch").val(hubBranch);
$("#modal-rejection").modal('show');
return false;
});
// focus on reason field
$("#modal-rejection").on('shown.bs.modal', function() {
$("#hub-reject-reason").focus();
});
// reset reject form on close modal
$("#modal-rejection").on('hidden.bs.modal', function() {
$("#hub-reject-contact-person, #hub-reject-remarks").val("");
$("#hub-reject-reason").prop('selectedIndex', 0);
});
// submit hub rejection
$("#hub-reject-form").submit(function(e) {
var form = $(this);
var btnSubmit = form.find('[type="submit"]');
// disable the button
btnSubmit.prop('disabled', true).html('<i class="fas fa-spinner fa-spin fa-fw"></i> Please wait');
e.preventDefault();
$.ajax({
method: "POST",
url: form.prop('action'),
data: form.serialize()
}).done(function(response) {
// set button state
var btnReject = $("#hubs-table").find("tr[data-id='" + $("#hub-reject-id").val() + "']").find('.btn-reject');
btnReject.removeClass('btn-reject btn-info').addClass('btn-danger btn-disabled').prop('disabled', true).html('Rejected');
// remove all error classes
removeErrors("#hub-reject-form");
btnSubmit.prop('disabled', false).html('Reject');
// hide modal
$("#modal-rejection").modal('hide');
}).fail(function(response) {
if (response.status == 422) {
form_in_process = false;
var errors = response.responseJSON.errors;
var firstfield = false;
// remove all error classes first
removeErrors("#hub-reject-form");
btnSubmit.prop('disabled', false).html('Reject');
// display errors contextually
$.each(errors, function(field, msg) {
var formfield = form.find("[name='" + field + "'], [data-name='" + field + "']");
var label = form.find("label[data-field='" + field + "']");
var msgbox = form.find(".form-control-feedback[data-field='" + field + "']");
// add error classes to bad fields
formfield.addClass('form-control-danger');
label.addClass('has-danger');
msgbox.html(msg).addClass('has-danger').removeClass('hide');
// check if this field comes first in DOM
var domfield = formfield.get(0);
if (!firstfield || (firstfield && firstfield.compareDocumentPosition(domfield) === 2)) {
firstfield = domfield;
}
});
// focus on first bad field
firstfield.focus();
}
});
});
{% endif %}
// cancel job order
$(".btn-cancel-job-order").click(function(e) {