From 54bc9455a17f4cc332e14e20cdc407eb69c58ad1 Mon Sep 17 00:00:00 2001 From: Kendrick Chan Date: Sun, 10 Feb 2019 22:16:22 +0800 Subject: [PATCH 1/5] Add Reject button for nearest hub section of job order dispatch #183 --- templates/job-order/form.html.twig | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/templates/job-order/form.html.twig b/templates/job-order/form.html.twig index e7d7972c..1d7b2d27 100644 --- a/templates/job-order/form.html.twig +++ b/templates/job-order/form.html.twig @@ -561,7 +561,9 @@ --> Available Riders Jobs For Assignment - Contact Numbers + Contact Numbers + Action + @@ -581,6 +583,9 @@ {{ hub.rider_count }} {{ hub.jo_count }} {{ hub.hub.getContactNumbers|replace({"\n": ', '}) }} + + + {% endfor %} @@ -804,7 +809,7 @@ {% endif %} {% if ftags.set_map_coordinate and is_granted('joborder.cancel') and not obj.isCancelled %} - Cancel Job Order + Cancel Job Order {% endif %} {% if mode != 'create' %} Back @@ -1469,6 +1474,11 @@ $(function() { var ticketTable = $("#data-tickets").mDatatable(ticketOptions); {% endif %} + // reject job order + $(".button-reject").click(function(e) { + return false; + }); + // cancel job order $(".btn-cancel-job-order").click(function(e) { var url = $(this).prop('href'); From 9f1c5b9cc975cad33e1bf71cd27d02d230b82688 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Mon, 11 Feb 2019 18:41:05 +0800 Subject: [PATCH 2/5] Add JORejection entity #183 --- src/Entity/JORejection.php | 139 +++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/Entity/JORejection.php diff --git a/src/Entity/JORejection.php b/src/Entity/JORejection.php new file mode 100644 index 00000000..92b559fe --- /dev/null +++ b/src/Entity/JORejection.php @@ -0,0 +1,139 @@ +id; + } + + public function getDateCreate() + { + return $this->date_create; + } + + public function setDateCreate(DateTime $date_create) + { + $this->date_create = $date_create; + return $this; + } + + public function getUser() + { + return $this->user; + } + + public function setUser(User $user) + { + $this->user = $user; + return $this; + } + + public function getHub() + { + return $this->user; + } + + public function setHub(Hub $hub) + { + $this->hub = $hub; + return $this; + } + + public function getJobOrder() + { + return $this->user; + } + + public function setJobOrder(JobOrder $job_order) + { + $this->job_order = $job_order; + return $this; + } + + public function getReason() + { + return $this->reason; + } + + public function setReason($reason) + { + $this->reason = $reason; + return $this; + } + + public function getRemarks() + { + return $this->remarks; + } + + public function setRemarks($remarks) + { + $this->remarks = $remarks; + return $this; + } + + public function getContactPerson() + { + return $this->contact_person; + } + + public function setContactPerson($contact_person) + { + $this->contact_person = $contact_person; + return $this; + } +} From b63695e8467edb1cb43b8e6af58b12ae1a857904 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Mon, 11 Feb 2019 21:01:57 +0800 Subject: [PATCH 3/5] Fix typos on JORejection entity #183 --- src/Entity/JORejection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Entity/JORejection.php b/src/Entity/JORejection.php index 92b559fe..98e102d1 100644 --- a/src/Entity/JORejection.php +++ b/src/Entity/JORejection.php @@ -35,7 +35,7 @@ class JORejection protected $hub; /** - * @ORM\ManyToOne(targetEntity="JobOrder") + * @ORM\ManyToOne(targetEntity="JobOrder", inversedBy="hub_rejections") * @ORM\JoinColumn(name="jo_id", referencedColumnName="id", nullable=true) */ protected $job_order; @@ -84,7 +84,7 @@ class JORejection public function getHub() { - return $this->user; + return $this->hub; } public function setHub(Hub $hub) @@ -95,7 +95,7 @@ class JORejection public function getJobOrder() { - return $this->user; + return $this->job_order; } public function setJobOrder(JobOrder $job_order) From 345ca27a31d2ff59b7147164f16b46fcccdb6dc8 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 12 Feb 2019 00:12:58 +0800 Subject: [PATCH 4/5] Fix associations for JORejection to JobOrder #183 --- src/Entity/JORejection.php | 11 ++++++++++- src/Entity/JobOrder.php | 11 +++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Entity/JORejection.php b/src/Entity/JORejection.php index 98e102d1..107361d4 100644 --- a/src/Entity/JORejection.php +++ b/src/Entity/JORejection.php @@ -6,10 +6,12 @@ use Doctrine\ORM\Mapping as ORM; use DateTime; /** - * @ORM\Entity(repositoryClass="App\Repository\JORejectionRepository") + * @ORM\Entity + * @ORM\Table(name="jo_rejection") */ class JORejection { + // unique id /** * @ORM\Id() * @ORM\GeneratedValue() @@ -17,39 +19,46 @@ class JORejection */ protected $id; + // date the entry was created /** * @ORM\Column(type="datetime") */ protected $date_create; + // user who rejected hub /** * @ORM\ManyToOne(targetEntity="User") * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true) */ protected $user; + // hub that was rejected /** * @ORM\ManyToOne(targetEntity="Hub") * @ORM\JoinColumn(name="hub_id", referencedColumnName="id", nullable=true) */ protected $hub; + // job order where hub was rejected /** * @ORM\ManyToOne(targetEntity="JobOrder", inversedBy="hub_rejections") * @ORM\JoinColumn(name="jo_id", referencedColumnName="id", nullable=true) */ protected $job_order; + // generic reason for this rejection /** * @ORM\Column(type="string", length=255) */ protected $reason; + // remarks about this rejection /** * @ORM\Column(type="text", nullable=true) */ protected $remarks; + // contact person of hub about this rejection /** * @ORM\Column(type="string", length=255, nullable=true) */ diff --git a/src/Entity/JobOrder.php b/src/Entity/JobOrder.php index ee401c82..346319dd 100644 --- a/src/Entity/JobOrder.php +++ b/src/Entity/JobOrder.php @@ -262,6 +262,12 @@ class JobOrder */ protected $facilitated_by; + // hubs rejected for this job order + /** + * @ORM\OneToMany(targetEntity="JORejection", mappedBy="job_order") + */ + protected $hub_rejections; + public function __construct() { $this->date_create = new DateTime(); @@ -776,4 +782,9 @@ class JobOrder { return $this->facilitated_by; } + + public function getHubRejections() + { + return $this->hub_rejections; + } } From 623c80421a394f3cc18b920df04bdb88c01f7b14 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 12 Feb 2019 00:14:15 +0800 Subject: [PATCH 5/5] Add route and processing for hub rejections #183 --- config/routes/job_order.yaml | 5 + src/Controller/JobOrderController.php | 139 +++++++++++++++++++++ src/Ramcar/JORejectionReason.php | 26 ++++ templates/job-order/form.html.twig | 166 ++++++++++++++++++++++++-- 4 files changed, 327 insertions(+), 9 deletions(-) create mode 100644 src/Ramcar/JORejectionReason.php diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml index 14375bcb..05cb08c8 100644 --- a/config/routes/job_order.yaml +++ b/config/routes/job_order.yaml @@ -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] diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 6641e247..3a5a8efa 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -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() + ]); + } } diff --git a/src/Ramcar/JORejectionReason.php b/src/Ramcar/JORejectionReason.php new file mode 100644 index 00000000..a07042e6 --- /dev/null +++ b/src/Ramcar/JORejectionReason.php @@ -0,0 +1,26 @@ + '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', + ]; +} diff --git a/templates/job-order/form.html.twig b/templates/job-order/form.html.twig index 1d7b2d27..4d654fa4 100644 --- a/templates/job-order/form.html.twig +++ b/templates/job-order/form.html.twig @@ -584,7 +584,11 @@ {{ hub.jo_count }} {{ hub.hub.getContactNumbers|replace({"\n": ', '}) }} - + {% if hub.flag_rejected %} + + {% else %} + + {% endif %} {% endfor %} @@ -823,6 +827,68 @@ + + {% if mode in ['update-processing', 'update-reassign-hub'] %} + + + {% 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(' 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) {