Merge branch 'master' of gitlab.com:jankstudio/resq into 187-generate-warranty-entries-for-existing-job-orders

This commit is contained in:
Korina Cordero 2019-04-11 09:06:25 +00:00
commit 7e6a469871
37 changed files with 2290 additions and 62 deletions

View file

@ -75,6 +75,17 @@ class TestCommand extends Command
];
$api->post('/capi/warranties/' . $id . '/claim', $params);
// add battery
$sku = 'WZMB31QT-CPP00-S';
$brand_id = '4';
$size_id = '1';
$params = [
'sku' => $sku,
'brand_id' => $brand_id,
'size_id' => $size_id,
];
$api->post('/capi/batteries', $params);
/*
// plate warranty

View file

@ -211,6 +211,8 @@ access_keys:
acls:
- id: support.menu
label: Menu
- id: general.search
label: Search
- id: ticket
label: Ticket Access
acls:
@ -238,3 +240,13 @@ access_keys:
label: Update
- id: promo.delete
label: Delete
- id: report
label: Reports
acls:
- id: report.menu
label: Menu
- id: report.reject
label: Rejection Report
- id: report.battery.conflict
label: Battery Conflict Report

View file

@ -118,3 +118,7 @@ main_menu:
acl: ticket.list
label: Tickets
parent: support
- id: general_search
acl: general.search
label: Search
parent: support

View file

@ -30,6 +30,12 @@ capi_battery_sizes:
controller: App\Controller\CAPI\BatteryController::getSizes
methods: [GET]
# add battery
capi_battery_add:
path: /capi/batteries
controller: App\Controller\CAPI\BatteryController::addBattery
methods: [POST]
# vehicle api

29
config/routes/report.yaml Normal file
View file

@ -0,0 +1,29 @@
rep_reject_summary_form:
path: /report/rejection_summary
controller: App\Controller\ReportController::rejectSummaryForm
methods: [GET]
rep_reject_summary_submit:
path: /report/rejection_summary
controller: App\Controller\ReportController::rejectSummarySubmit
methods: [POST]
rep_reject_detail_form:
path: /report/rejection_detail
controller: App\Controller\ReportController::rejectDetailForm
methods: [GET]
rep_reject_detail_submit:
path: /report/rejection_detail
controller: App\Controller\ReportController::rejectDetailSubmit
methods: [POST]
rep_battery_conflict_form:
path: /report/battery_conflict
controller: App\Controller\ReportController::batteryConflictForm
methods: [GET]
rep_battery_conflict_submit:
path: /report/battery_conflict
controller: App\Controller\ReportController::batteryConflictSubmit
methods: [POST]

View file

@ -0,0 +1,8 @@
general_search:
path: /search
controller: App\Controller\SearchController::index
search_history:
path: /search/history
controller: App\Controller\SearchController::search
methods: [GET]

73
kml/supported_areas.kml Normal file
View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>ResQ Supported Area</name>
<description/>
<Style id="poly-000000-1200-77-nodesc-normal">
<LineStyle>
<color>ff000000</color>
<width>1.2</width>
</LineStyle>
<PolyStyle>
<color>4d000000</color>
<fill>1</fill>
<outline>1</outline>
</PolyStyle>
<BalloonStyle>
<text><![CDATA[<h3>$[name]</h3>]]></text>
</BalloonStyle>
</Style>
<Style id="poly-000000-1200-77-nodesc-highlight">
<LineStyle>
<color>ff000000</color>
<width>1.8</width>
</LineStyle>
<PolyStyle>
<color>4d000000</color>
<fill>1</fill>
<outline>1</outline>
</PolyStyle>
<BalloonStyle>
<text><![CDATA[<h3>$[name]</h3>]]></text>
</BalloonStyle>
</Style>
<StyleMap id="poly-000000-1200-77-nodesc">
<Pair>
<key>normal</key>
<styleUrl>#poly-000000-1200-77-nodesc-normal</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#poly-000000-1200-77-nodesc-highlight</styleUrl>
</Pair>
</StyleMap>
<Folder>
<name>ResQ Supported Area</name>
<Placemark>
<name>Supported Area</name>
<styleUrl>#poly-000000-1200-77-nodesc</styleUrl>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<tessellate>1</tessellate>
<coordinates>
121.0717128,14.7868868,0
121.0222743,14.7895424,0
120.9302638,14.6793076,0
120.9494899,14.4427135,0
121.0250209,14.3735484,0
121.0744593,14.5171749,0
121.1513636,14.5357864,0
121.1884425,14.5809791,0
121.2021754,14.6248337,0
121.1321376,14.6540653,0
121.129391,14.7616569,0
121.0717128,14.7868868,0
</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
</Folder>
</Document>
</kml>

View file

@ -196,8 +196,102 @@ span.has-danger,
top: -0.45rem;
}
.input-group .form-control:first-child:not(:last-child):not(:focus):not(.focus) {
border-right: 0;
}
.input-group .input-group-append + .form-control:not(:focus):not(.focus) {
border-left: 0;
}
.input-group > .form-control:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group > .form-control:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group .input-group-append > .input-group-text {
border-color: #ebedf2;
background-color: #f4f5f8;
color: #575962;
}
.input-group > .input-group-append > .input-group-text {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group > .input-group-append:not(:last-child) > .input-group-text {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group-text {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
padding: .85rem 1.15rem;
margin-bottom: 0;
font-size: 1rem;
font-weight: 400;
line-height: 1.25;
color: #495057;
text-align: center;
white-space: nowrap;
background-color: #e9ecef;
border: 1px solid #ced4da;
border-radius: .25rem;
}
.input-daterange input:last-child {
border-radius: 0 3px 3px 0;
}
.input-group {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-box-align: stretch;
-ms-flex-align: stretch;
align-items: stretch;
width: 100%;
}
.input-daterange {
width: 100%;
}
@media (min-width: 995px) {
.modal-lg {
max-width: 1024px;
}
}
.input-group-append {
margin-left: -1px;
}
.input-group-prepend, .input-group-append {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.input-group>.input-group-append>.btn, .input-group>.input-group-append>.input-group-text, .input-group>.input-group-prepend:not(:first-child)>.btn, .input-group>.input-group-prepend:not(:first-child)>.input-group-text, .input-group>.input-group-prepend:first-child>.btn:not(:first-child), .input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.btn-icon {
margin-right: .5em;
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ObjectManager;
use App\Entity\JobOrder;
use DateTime;
class AdjustLongLatCommand extends Command
{
protected $em;
public function __construct(ObjectManager $om)
{
$this->em = $om;
parent::__construct();
}
protected function configure()
{
$this->setName('joborder:adjust_longlat')
->setDescription('Separate longitude and latitude from coordinate point.')
->setHelp('Get longitude and latitude from existing point type coordinate. Separate into individual fields for reports purposes.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// get entity manager
$em = $this->em;
$job_orders = $em->getRepository(JobOrder::class)->findAll();
// fulfill each
foreach ($job_orders as $jo)
{
$point = $jo->getCoordinates();
$jo->setCoordinates($point);
}
$em->flush();
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Entity\SupportedArea;
use App\Service\KMLFileImporter;
class ImportKMLFileCommand extends Command
{
protected $importer;
protected function configure()
{
$this->setName('supportedarea:import')
->setDescription('Extracts map data of the supported area from the KML file and saves to database')
->setHelp('Gets the coordinates of the supported area and saves to the database')
->addArgument('file', InputArgument::REQUIRED, 'Path to the KML file');
}
public function __construct(KMLFileImporter $importer)
{
$this->importer = $importer;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$kml_file = $input->getArgument('file');
$this->importer->getMapData($kml_file);
}
}

View file

@ -10,6 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ObjectManager;
use App\Entity\Warranty;
use App\Entity\Battery;
use App\Entity\BatterySize;
use App\Entity\BatteryModel;
use App\Entity\VehicleManufacturer;
@ -31,6 +32,7 @@ use DateTime;
class ImportLegacyJobOrderCommand extends Command
{
protected $em;
protected $batt_hash;
protected $bmodel_hash;
protected $bsize_hash;
protected $vmfg_hash;
@ -50,6 +52,7 @@ class ImportLegacyJobOrderCommand extends Command
$this->loadBatterySizes();
$this->loadVehicleManufacturers();
$this->loadVehicles();
$this->loadBatteries();
$this->jo_hash = [];
@ -312,7 +315,7 @@ class ImportLegacyJobOrderCommand extends Command
continue;
// check if battery is found
$found_battery = $this->findBattery($fields[92], $batt_model, $batt_size);
$found_battery = $this->findBattery($fields[92], $batt_model, $batt_size, $sap_code);
if (!$found_battery)
{
// $output->writeln('battery not found - ' . $fields[92]);
@ -399,7 +402,33 @@ class ImportLegacyJobOrderCommand extends Command
$line .= '\N,';
// date claim
$line .= '\N';
$line .= '\N,';
// claim id
$line .= '\N,';
// sap battery id
if (isset($sap_code))
$line .= $sap_code . ',';
else
$line .= '\N,';
// first name
if (isset($fields[20]) && strlen(trim($fields[20])) > 0)
$line .= $fields[20] . ',';
else
$line .= '\N,';
// last name
if (isset($fields[22]) && strlen(trim($fields[22])) > 0)
$line .= $fields[22] . ',';
else
$line .= '\N,';
// mobile number
if (isset($fields[24]) && strlen(trim($fields[24])) > 0)
$line .= $fields[24] . ',';
else
$line .= '\N';
fwrite($warr_outfile, $line . "\n");
}
@ -769,6 +798,25 @@ class ImportLegacyJobOrderCommand extends Command
}
}
protected function loadBatteries()
{
$this->batt_hash = [];
$batts = $this->em->getRepository(Battery::class)->findAll();
foreach ($batts as $batt)
{
if (($batt->getModel() == null) or ($batt->getSize() == null) or ($batt->getSAPCode() == null))
{
continue;
}
$model_id = $batt->getModel()->getID();
$size_id = $batt->getSize()->getID();
$this->batt_hash[$model_id][$size_id] = $batt->getSAPCode();
}
}
protected function loadVehicleManufacturers()
{
$this->vmfg_hash = [];
@ -817,16 +865,16 @@ class ImportLegacyJobOrderCommand extends Command
return $clean_text;
}
protected function findBattery($batt_field, &$batt_model, &$batt_size)
{
// split battery into model and size
// echo "trying match - " . $fields[92] . "\n";
$res = preg_match("/^(.+)(GOLD|EXCEL|ENDURO|\(Trade-In\))/", $batt_field, $matches);
if (!$res)
{
// echo "no match - " . $fields[92] . "\n";
protected function findBattery($batt_field, &$batt_model, &$batt_size, &$sap_code)
{
// split battery into model and size
// echo "trying match - " . $batt_field . "\n";
$res = preg_match("/^(.+)(GOLD|EXCEL|ENDURO|\(Trade-In\))/", $batt_field, $matches);
if (!$res)
{
//echo "no match - " . $fields[92] . "\n";
return false;
}
}
if ($matches[2] == '(Trade-In)')
return false;
@ -836,29 +884,35 @@ class ImportLegacyJobOrderCommand extends Command
// TODO: what to do about (Trade-In)
// check if we have the size
$found_size = $this->simplifyName($matches[1]);
if (!isset($this->bsize_hash[$found_size]))
{
// try legacy battery lookup
$legacy_size = LegacyBattery::translate($found_size);
if ($legacy_size == null)
{
// echo "no size - $found_size\n";
if (isset($no_sizes[$found_size]))
$no_sizes[$found_size]++;
else
$no_sizes[$found_size] = 1;
return false;
}
// check if we have the size
$found_size = $this->simplifyName($matches[1]);
if (!isset($this->bsize_hash[$found_size]))
{
// try legacy battery lookup
$legacy_size = LegacyBattery::translate($found_size);
if ($legacy_size == null)
{
// echo "no size - $found_size\n";
if (isset($no_sizes[$found_size]))
$no_sizes[$found_size]++;
else
$no_sizes[$found_size] = 1;
return false;
}
$found_size = $legacy_size;
}
$found_size = $legacy_size;
}
$batt_size = $this->bsize_hash[$found_size];
// $batt_size = $found_size;
//get battery using ids of batt_model and batt_size
if (!isset($this->batt_hash[$batt_model][$batt_size]))
return false;
$sap_code = $this->batt_hash[$batt_model][$batt_size];
return true;
}
}
protected function findVehicle($vmfg_field, $vmake_field, $vmodel_field, &$vehicle)
{

View file

@ -0,0 +1,70 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Service\GeneralSearch;
class TestGeneralSearchCommand extends Command
{
protected function configure()
{
$this->setName('test:generalsearch')
->setDescription('Test general search service.')
->setHelp('Test the general search service.')
->addArgument('search_term', InputArgument::REQUIRED, 'Search Terms');
}
public function __construct(GeneralSearch $search)
{
$this->search = $search;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$search_term = $input->getArgument('search_term');
$results = $this->search->search($search_term);
echo "legacy job order results: " . count($results['legacy_job_orders']) . "\n";
foreach($results['legacy_job_orders'] as $legacy_job_order)
{
echo "Plate Number: " . $legacy_job_order->getPlateNumber() . "\n";
echo "Mobile Number: " . $legacy_job_order->getCustMobile() . "\n";
echo "Landline: " . $legacy_job_order->getCustLandline() . "\n";
echo "Name: " . $legacy_job_order->getCustName() . "\n";
echo "Last Name: " . $legacy_job_order->getCustLastName() . "\n";
echo "First Name: " . $legacy_job_order->getCustFirstName() . "\n";
echo "Middle Name: " . $legacy_job_order->getCustMiddleName() . "\n";
}
echo "job order results: " . count($results['job_orders']) . "\n";
foreach($results['job_orders'] as $job_order)
{
echo "Plate Number: " . $job_order->getCustomerVehicle()->getPlateNumber() . "\n";
echo "Mobile Number: " . $job_order->getCustomer()->getPhoneMobile() . "\n";
echo "Landline: " . $job_order->getCustomer()->getPhoneLandline() . "\n";
echo "Office: " . $job_order->getCustomer()->getPhoneOffice() . "\n";
echo "Fax: " . $job_order->getCustomer()->getPhoneFax() . "\n";
echo "Last Name: " . $job_order->getCustomer()->getLastName() . "\n";
echo "First Name: " . $job_order->getCustomer()->getFirstName() . "\n";
}
echo "warranties results: " . count($results['warranties']) . "\n";
foreach($results['warranties'] as $warranty)
{
echo "Plate Number: " . $warranty->getPlateNumber() . "\n";
echo "Mobile Number: " . $warranty->getMobileNumber() . "\n";
echo "Last Name: " . $warranty->getLastName() . "\n";
echo "First Name: " . $warranty->getFirstName() . "\n";
}
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Entity\SupportedArea;
use App\Service\GeofenceTracker;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
class TestGeofenceCommand extends Command
{
protected function configure()
{
$this->setName('test:geofence')
->setDescription('Test geofence tracker service.')
->setHelp('Test the geofence tracker service.')
->addArgument('long', InputArgument::REQUIRED, 'Longitude')
->addArgument('lat', InputArgument::REQUIRED, 'Latitude');
}
public function __construct(GeofenceTracker $geo)
{
$this->geo = $geo;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$long = $input->getArgument('long');
$lat = $input->getArgument('lat');
if ($this->geo->isCovered($long, $lat))
echo "In geofence\n";
else
echo "NOT in geofence\n";
}
}

View file

@ -25,6 +25,7 @@ use App\Ramcar\JOEventType;
use App\Service\InvoiceCreator;
use App\Service\RisingTideGateway;
use App\Service\MQTTClient;
use App\Service\GeofenceTracker;
use App\Entity\MobileSession;
use App\Entity\Customer;
@ -766,7 +767,7 @@ class APIController extends Controller
return $res->getReturnResponse();
}
public function requestJobOrder(Request $req, InvoiceCreator $ic)
public function requestJobOrder(Request $req, InvoiceCreator $ic, GeofenceTracker $geo)
{
// check required parameters and api key
$required_params = [
@ -793,6 +794,20 @@ class APIController extends Controller
// instructions
$instructions = $req->request->get('delivery_instructions', '');
// longitude and latitude
$long = $req->request->get('long');
$lat = $req->request->get('lat');
// geofence
$is_covered = $geo->isCovered($long, $lat);
if (!$is_covered)
{
// TODO: put geofence error message in config file somewhere
$res->setError(true)
->setErrorMessage('Oops! Our service is limited to Metro Manila only. We will update you as soon as we are able to cover your area');
return $res->getReturnResponse();
}
$jo = new JobOrder();
$jo->setSource(TransactionOrigin::MOBILE_APP)
@ -836,9 +851,7 @@ class APIController extends Controller
}
$jo->setWarrantyClass($warr);
// longitude and latitude
$long = $req->request->get('long');
$lat = $req->request->get('lat');
// set coordinates
$point = new Point($long, $lat);
$jo->setCoordinates($point);

View file

@ -119,6 +119,7 @@ class BatteryController extends BaseController
$row['sell_price'] = $orow[0]->getSellingPrice();
$row['warr_private'] = $orow[0]->getWarrantyPrivate();
$row['warr_commercial'] = $orow[0]->getWarrantyCommercial();
$row['warr_tnv'] = $orow[0]->getWarrantyTnv();
$row['res_capacity'] = $orow[0]->getReserveCapacity();
$row['length'] = $orow[0]->getLength();
$row['width'] = $orow[0]->getWidth();
@ -181,6 +182,7 @@ class BatteryController extends BaseController
->setSAPCode($req->request->get('sap_code'))
->setWarrantyPrivate($req->request->get('warr_private'))
->setWarrantyCommercial($req->request->get('warr_commercial'))
->setWarrantyTnv($req->request->get('warr_tnv'))
->setReserveCapacity($req->request->get('res_capacity'))
->setLength($req->request->get('length'))
->setWidth($req->request->get('width'))
@ -303,6 +305,7 @@ class BatteryController extends BaseController
->setSAPCode($req->request->get('sap_code'))
->setWarrantyPrivate($req->request->get('warr_private'))
->setWarrantyCommercial($req->request->get('warr_commercial'))
->setWarrantyTnv($req->request->get('warr_tnv'))
->setReserveCapacity($req->request->get('res_capacity'))
->setLength($req->request->get('length'))
->setWidth($req->request->get('width'))

View file

@ -75,4 +75,72 @@ class BatteryController extends APIController
return new APIResponse(true, 'Battery sizes loaded.', $data);
}
public function addBattery(Request $req, EntityManagerInterface $em)
{
// required parameters
$params = [
'sku',
'brand_id',
'size_id',
];
$msg = $this->checkRequiredParameters($req, $params);
error_log('msg - ' . $msg);
if ($msg)
return new APIResponse(false, $msg);
$sku = $req->request->get('sku');
$brand_id = $req->request->get('brand_id');
$size_id = $req->request->get('size_id');
// check if sku already exists
$batt = $em->getRepository(SAPBattery::class)->find($sku);
if ($batt != null)
return new APIResponse(false, 'Battery SKU already exists.');
// check if brand exists
$batt_brand = $em->getRepository(SAPBatteryBrand::class)->find($brand_id);
if ($batt_brand == null)
return new APIResponse(false, 'Invalid brand.');
// check if size exists
$batt_size = $em->getRepository(SAPBatterySize::class)->find($size_id);
if ($batt_size == null)
return new APIResponse(false, 'Invalid size.');
$new_batt = new SAPBattery();
$new_batt->setID($sku)
->setBrand($batt_brand)
->setSize($batt_size);
try
{
$em->persist($new_batt);
$em->flush();
}
catch (UniqueConstraintViolationException $e)
{
return new APIResponse(false, 'Battery SKU already exists.');
}
// return the new battery data
$data = [
'battery' => $this->generateBatteryData($new_batt),
];
return new APIResponse(true, 'Battery added.', $data);
}
protected function generateBatteryData(SAPBattery $batt)
{
$data = [
'sku' => $batt->getID(),
'brand' => $batt->getBrand()->getID(),
'size' => $batt->getSize()->getID(),
];
return $data;
}
}

View file

@ -97,7 +97,7 @@ class WarrantyController extends APIController
$query = $qb->select('w')
->from('App\\Entity\\Warranty', 'w')
->orderBy('w.serial', $order)
->orderBy('w.date_create', $order)
->setFirstResult($start)
->setMaxResults($max)
->getQuery();

View file

@ -0,0 +1,401 @@
<?php
namespace App\Controller;
use App\Ramcar\BaseController;
use App\Ramcar\JORejectionReason;
use App\Ramcar\ServiceType;
use App\Ramcar\JOStatus;
use App\Entity\JORejection;
use App\Entity\Battery;
use App\Entity\JobOrder;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
use DateTime;
class ReportController extends BaseController
{
public function rejectSummaryForm()
{
$this->denyAccessUnlessGranted('report.reject', null, 'No access.');
$params = $this->initParameters('outlet_list');
return $this->render('report/rejection/summary_form.html.twig', $params);
}
public function rejectSummarySubmit(Request $req)
{
$this->denyAccessUnlessGranted('report.reject', null, 'No access.');
// get query builder
$qb = $this->getDoctrine()
->getRepository(JORejection::class)
->createQueryBuilder('r');
// 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);
// build query
$query = $qb->where('r.date_create >= :start')
->andWhere('r.date_create <= :end')
->setParameter('start', $date_start->format('Y-m-d') . ' 00:00:00')
->setParameter('end', $date_end->format('Y-m-d') . ' 23:59:59')
->getQuery();
// run query
$jors = $query->getResult();
// initialize counter
$counter = [];
// get results
$res = [];
foreach ($jors as $jor)
{
$jo = $jor->getJobOrder();
$hub = $jor->getHub();
$hub_id = $hub->getID();
$hub_name = $hub->getName() . ' - ' . $hub->getBranch();
$reason = $jor->getReason();
if (!isset($counter[$hub_id]))
$counter[$hub_id] = [
'reasons' => [],
'name' => $hub_name,
];
if (!isset($counter[$hub_id][$reason]))
{
$counter[$hub_id]['reasons'][$reason]['name'] = JORejectionReason::getName($reason);
$counter[$hub_id]['reasons'][$reason]['counter'] = 1;
}
else
{
$counter[$hub_id]['reasons'][$reason]['counter'] += 1;
}
$res[] = [
'jo_id' => $jo->getID(),
'jo_date_time' => $jo->getDateSchedule(),
'jor_date_create' => $jo->getDateCreate(),
'hub' => $hub->getName() . ' - ' . $hub->getBranch(),
'reason' => JORejectionReason::getName($jor->getReason()),
'contact' => $jor->getContactPerson(),
'remarks' => $jor->getRemarks(),
];
}
// response
$resp = new StreamedResponse();
$resp->setCallback(function() use ($counter) {
// csv output
$csv_handle = fopen('php://output', 'w+');
fputcsv($csv_handle, ['Enrollee', 'Reason', 'Count']);
foreach ($counter as $centry)
{
$first = true;
foreach ($centry['reasons'] as $creason)
{
// first line has hub name
if ($first)
{
fputcsv($csv_handle, [
$centry['name'],
$creason['name'],
$creason['counter'],
]);
$first = false;
}
else
{
fputcsv($csv_handle, [
'',
$creason['name'],
$creason['counter'],
]);
}
}
}
fclose($csv_handle);
});
$filename = 'reject_' . $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;
/*
return $this->json([
'result' => $res,
'counter' => $counter,
]);
*/
}
public function rejectDetailForm()
{
$this->denyAccessUnlessGranted('report.reject', null, 'No access.');
$params = $this->initParameters('outlet_list');
return $this->render('report/rejection/detail_form.html.twig', $params);
}
public function rejectDetailSubmit(Request $req)
{
$this->denyAccessUnlessGranted('report.reject', null, 'No access.');
// get query builder
$qb = $this->getDoctrine()
->getRepository(JORejection::class)
->createQueryBuilder('r');
// 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);
// build query
$query = $qb->where('r.date_create >= :start')
->andWhere('r.date_create <= :end')
->setParameter('start', $date_start->format('Y-m-d') . ' 00:00:00')
->setParameter('end', $date_end->format('Y-m-d') . ' 23:59:59')
->getQuery();
// run query
$jors = $query->getResult();
// initialize counter
$counter = [];
// get results
$res = [];
foreach ($jors as $jor)
{
$jo = $jor->getJobOrder();
$hub = $jor->getHub();
$hub_id = $hub->getID();
$hub_name = $hub->getName() . ' - ' . $hub->getBranch();
/*
// check if created by app
$create_user = $jo->getCreatedBy();
if ($create_user == null)
$create_user_text = 'Application';
else
$create_user_text = $create_user->getFullName();
*/
$reason = $jor->getReason();
$res[] = [
$jo->getID(),
$jo->getDateSchedule()->format('m/d/Y H:i'),
$jor->getDateCreate()->format('m/d/Y H:i'),
$hub->getName() . ' - ' . $hub->getBranch(),
JORejectionReason::getName($jor->getReason()),
$jor->getContactPerson(),
$jor->getRemarks(),
$jor->getUser()->getFullName(),
ServiceType::getName($jo->getServiceType()),
];
}
// response
$resp = new StreamedResponse();
$resp->setCallback(function() use ($res) {
// csv output
$csv_handle = fopen('php://output', 'w+');
fputcsv($csv_handle, [
'Order #',
'Order Date and Time',
'Date and Time Rejected',
'Enrollee',
'Reason',
'Contact Person',
'Remarks',
'Dispatched By',
'Type of Service',
]);
foreach ($res as $row)
{
fputcsv($csv_handle, $row);
}
fclose($csv_handle);
});
$filename = 'reject_' . $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;
/*
return $this->json([
'result' => $res,
'counter' => $counter,
]);
*/
}
public function batteryConflictForm()
{
$this->denyAccessUnlessGranted('report.battery.conflict', null, 'No access.');
$params = $this->initParameters('outlet_list');
return $this->render('report/battery/batt_conflict_form.html.twig', $params);
}
public function batteryConflictSubmit(Request $req)
{
$this->denyAccessUnlessGranted('report.battery.conflict', null, 'No access.');
// get job order query builder
$job_qb = $this->getDoctrine()
->getRepository(JobOrder::class)
->createQueryBuilder('jo');
$em = $this->getDoctrine()->getManager();
// 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);
// build query for job order
$jo_query = $job_qb->where('jo.date_create >= :start')
->andWhere('jo.date_create <= :end')
->andWhere('jo.status != :status')
->setParameter('start', $date_start->format('Y-m-d') . ' 00:00:00')
->setParameter('end', $date_end->format('Y-m-d') . ' 23:59:59')
->setParameter('status', JOStatus::CANCELLED)
->getQuery();
// run queries
$jos = $jo_query->getResult();
$batteries = $em->getRepository(Battery::class)->findAll();
// create compatibility matrix for battery and vehicle
$comp_matrix = [];
foreach ($batteries as $batt)
{
$vehicles = $batt->getVehicles();
foreach ($vehicles as $vehicle)
{
$comp_matrix[$batt->getID()][$vehicle->getID()] = true;
}
}
// go through the job orders to find the conflicts
$results = [];
foreach ($jos as $jo)
{
$invoice_items = $jo->getInvoice()->getItems();
foreach ($invoice_items as $item)
{
// check if the item is a battery
if ($item->getBattery() != null)
{
$batt_id = $item->getBattery()->getID();
$vehicle_id = $jo->getCustomerVehicle()->getVehicle()->getID();
if (isset($comp_matrix[$batt_id][$vehicle_id]) && $comp_matrix[$batt_id][$vehicle_id])
{
continue;
}
else
{
// get the compatible batteries for the customer vehicle
$batteries = [];
foreach($jo->getCustomerVehicle()->getVehicle()->getBatteries() as $comp_batt)
{
//$batteries[] = [
// 'mfg_name' => $comp_batt->getManufacturer()->getName(),
// 'model_name' => $comp_batt->getModel()->getName(),
// 'size_name' => $comp_batt->getSize()->getName(),
//];
$batteries[] = $comp_batt->getManufacturer()->getName() . ' ' .
$comp_batt->getModel()->getName() . ' ' .
$comp_batt->getSize()->getName();
}
$results[] = [
'jo_id' => $jo->getID(),
'jo_date_create' => $jo->getDateCreate()->format('m/d/Y H:i'),
'cus_vehicle_manufacturer' => $jo->getCustomerVehicle()->getVehicle()->getManufacturer()->getName(),
'cus_vehicle_make' => $jo->getCustomerVehicle()->getVehicle()->getMake(),
'cus_vehicle_model' => $jo->getCustomerVehicle()->getModelYear(),
'battery_model_ordered' => $item->getBattery()->getModel()->getName(),
'battery_size_ordered' => $item->getBattery()->getSize()->getName(),
'compatible_batt' => implode(', ', $batteries),
];
}
}
}
}
$resp = new StreamedResponse();
$resp->setCallback(function() use ($results) {
// csv output
$csv_handle = fopen('php://output', 'w+');
fputcsv($csv_handle, [
'Order #',
'Order Date and Time',
'Manufacturer',
'Make',
'Year',
'Battery Model',
'Battery Size',
'Compatible Batteries'
]);
foreach ($results as $row)
{
fputcsv($csv_handle, $row);
}
fclose($csv_handle);
});
$filename = 'battery_conflict_' . $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;
//return $this->json([
// 'result' => $results,
//]);
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace App\Controller;
use App\Ramcar\BaseController;
use App\Service\GeneralSearch;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Menu\Generator as MenuGenerator;
use App\Access\Generator as ACLGenerator;
class SearchController extends BaseController
{
protected $acl_gen;
public function __construct(MenuGenerator $menu_gen, ACLGenerator $acl_gen)
{
$this->acl_gen = $acl_gen;
parent::__construct($menu_gen);
}
public function index()
{
$this->denyaccessUnlessGranted('general.search', null, 'No access.');
$params = $this->initParameters('general_search');
$params["mode"] = "form";
// response
return $this->render('search/form.html.twig', $params);
}
public function search(Request $req, GeneralSearch $search)
{
$this->denyAccessUnlessGranted('general.search', null, 'No access.');
$params = $this->initParameters('general_search');
$search_term = $req->query->get('search');
$results = $search->search($search_term);
$params['data'] = $results;
$params['search_term'] = $search_term;
$params['mode'] = "results";
// response
return $this->render('search/form.html.twig', $params);
}
}

View file

@ -85,6 +85,13 @@ class Battery
*/
protected $warr_commercial;
// warranty tnv
/**
* @ORM\Column(type="smallint")
* @Assert\NotBlank()
*/
protected $warr_tnv;
// reserve capacity
/**
* @ORM\Column(type="smallint")
@ -248,6 +255,17 @@ class Battery
return $this->warr_commercial;
}
public function setWarrantyTnv($warr_tnv)
{
$this->warr_tnv = $warr_tnv;
return $this;
}
public function getWarrantyTnv()
{
return $this->warr_tnv;
}
public function setReserveCapacity($res_capacity)
{
$this->res_capacity = $res_capacity;

View file

@ -64,6 +64,18 @@ class JobOrder
*/
protected $coordinates;
// we split up coordinates into long / lat for reports purposes
/**
* @ORM\Column(type="decimal", precision=11, scale=8)
*/
protected $coord_long;
// we split up coordinates into long / lat for reports purposes
/**
* @ORM\Column(type="decimal", precision=11, scale=8)
*/
protected $coord_lat;
// is it an advanced order (future date)
/**
* @ORM\Column(type="boolean")
@ -350,6 +362,9 @@ class JobOrder
public function setCoordinates(Point $point)
{
$this->coordinates = $point;
$this->coord_lat = $point->getLatitude();
$this->coord_long = $point->getLongitude();
return $this;
}

View file

@ -355,4 +355,345 @@ class LegacyJobOrder
{
return $this->id;
}
public function setTransDate($trans_date)
{
$this->id = $trans_date;
return $this;
}
public function getTransDate()
{
return $this->trans_date;
}
public function setTransType($trans_type)
{
$this->trans_type = $trans_type;
return $this;
}
public function getTransType()
{
return $this->trans_type;
}
public function setOrigin($origin)
{
$this->origin = $origin;
return $this;
}
public function getOrigin()
{
return $this->origin;
}
public function setCarBrand($car_brand)
{
$this->car_brand = $car_brand;
return $this;
}
public function getCarBrand()
{
return $this->car_brand;
}
public function setCarMake($car_make)
{
$this->car_make = $car_make;
return $this;
}
public function getCarMake()
{
return $this->car_make;
}
public function setCarModel($car_model)
{
$this->car_model = $car_model;
return $this;
}
public function getCarModel()
{
return $this->car_model;
}
public function setCarColor($car_color)
{
$this->car_color = $car_color;
return $this;
}
public function getCarColor()
{
return $this->car_color;
}
public function setCustName($cust_name)
{
$this->cust_name = $cust_name;
return $this;
}
public function getCustName()
{
return $this->cust_name;
}
public function setCustFirstName($cust_first_name)
{
$this->cust_first_name = $cust_first_name;
return $this;
}
public function getCustFirstName()
{
return $this->cust_first_name;
}
public function setCustMiddleName($cust_middle_name)
{
$this->cust_middle_name = $cust_middle_name;
return $this;
}
public function getCustMiddleName()
{
return $this->cust_middle_name;
}
public function setCustLastName($cust_last_name)
{
$this->cust_last_name = $cust_last_name;
return $this;
}
public function getCustLastName()
{
return $this->cust_last_name;
}
public function setCustContact($cust_contact)
{
$this->cust_contact = $cust_contact;
return $this;
}
public function getCustContact()
{
return $this->cust_contact;
}
public function setCustMobile($cust_mobile)
{
$this->cust_mobile = $cust_mobile;
return $this;
}
public function getCustMobile()
{
return $this->cust_mobile;
}
public function setCustLandline($cust_landline)
{
$this->cust_landline = $cust_landline;
return $this;
}
public function getCustLandline()
{
return $this->cust_landline;
}
public function setDeliveryInstructions($delivery_instructions)
{
$this->delivery_instructions = $delivery_instructions;
return $this;
}
public function getDeliveryInstructions()
{
return $this->delivery_instructions;
}
public function setAgentNotes1($agent_notes_1)
{
$this->agent_notes_1 = $agent_notes_1;
return $this;
}
public function getAgentNotes1()
{
return $this->agent_notes_1;
}
public function setDeliveryDate($delivery_date)
{
$this->delivery_date = $delivery_date;
return $this;
}
public function getDeliveryDate()
{
return $this->delivery_date;
}
public function setDeliveryTime($delivery_time)
{
$this->delivery_time = $delivery_time;
return $this;
}
public function getDeliveryTime()
{
return $this->delivery_time;
}
public function setAdvanceOrder($advance_order)
{
$this->advance_order = $advance_order;
return $this;
}
public function getAdvanceOrder()
{
return $this->advance_order;
}
public function setStage($stage)
{
$this->stage = $stage;
return $this;
}
public function getStage()
{
return $this->stage;
}
public function setCancelReason($cancel_reason)
{
$this->cancel_reason = $cancel_reason;
return $this;
}
public function getCancelReason()
{
return $this->cancel_reason;
}
public function setCancelReasonSpecify($cancel_reason_specify)
{
$this->cancel_reason_specify = $cancel_reason_specify;
return $this;
}
public function getCancelReasonSpecify()
{
return $this->cancel_reason_specify;
}
public function setPaymentMethod($payment_method)
{
$this->payment_method = $payment_method;
return $this;
}
public function getPaymentMethod()
{
return $this->payment_method;
}
public function setPreparedBy($prepared_by)
{
$this->prepared_by = $prepared_by;
return $this;
}
public function getPreparedBy()
{
return $this->prepared_by;
}
public function setDispatchTime($dispatch_time)
{
$this->dispatch_time = $dispatch_time;
return $this;
}
public function getDispatchTime()
{
return $this->dispatch_time;
}
public function setDispatchDate($dispatch_date)
{
$this->dispatch_date = $dispatch_date;
return $this;
}
public function getDispatchDate()
{
return $this->dispatch_date;
}
public function setDispatchBy($dispatch_by)
{
$this->dispatch_by = $dispatch_by;
return $this;
}
public function getDispatchBy()
{
return $this->dispatch_by;
}
public function setAddress($address)
{
$this->address = $address;
return $this;
}
public function getAddress()
{
return $this->address;
}
public function setLandmark($landmark)
{
$this->landmark = $landmark;
return $this;
}
public function getLandmark()
{
return $this->landmark;
}
public function setDatePurchase($date_purchase)
{
$this->date_purchase = $date_purchase;
return $this;
}
public function getDatePurchase()
{
return $this->date_purchase;
}
public function setPlateNumber($plate_number)
{
$this->plate_number = $plate_number;
return $this;
}
public function getPlateNumber()
{
return $this->plate_number;
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use CrEOF\Spatial\PHP\Types\Geometry\Polygon;
use DateTime;
/**
* @ORM\Entity
* @ORM\Table(name="supported_area")
*/
class SupportedArea
{
// unique id
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
// date supported area was created
/**
* @ORM\Column(type="datetime")
*/
protected $date_create;
// name of the supported area
/**
* @ORM\Column(type="string", length=80)
*/
protected $name;
// coordinates of the supported area
/**
* @ORM\Column(type="polygon")
*/
protected $coverage_area;
public function __construct()
{
$this->date_create = new DateTime();
}
public function getID()
{
return $this->id;
}
public function setDateCreate(DateTime $date_create)
{
$this->date_create = $date_create;
return $this;
}
public function getDateCreate()
{
return $this->date_Create;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function setCoverageArea(Polygon $polygon)
{
$this->coverage_area = $polygon;
return $this;
}
public function getCoverageArea()
{
return $this->coverage_area;
}
}

View file

@ -12,6 +12,9 @@ class JORejectionReason extends NameValue
const NO_RIDER_AVAILABLE = 'no_rider_available';
const NO_RIDER_IN_TRANSIT = 'no_rider_in_transit';
const REFUSAL = 'refusal';
const STORE_CLOSED = 'store_closed';
const NO_CREDIT_CARD = 'no_credit_card';
const DISCOUNT = 'discount';
const COLLECTION = [
'administrative' => 'ADMINISTRATIVE',
@ -22,5 +25,8 @@ class JORejectionReason extends NameValue
'no_rider_available' => 'NO RIDER - AVAILABLE',
'no_rider_in_transit' => 'NO RIDER - IN TRANSIT',
'refusal' => 'REFUSAL',
'store_closed' => 'STORE CLOSED',
'no_credit_card' => 'NO CREDIT CARD PAYMENT / NO TERMINAL',
'discount' => 'DISCOUNT',
];
}

View file

@ -0,0 +1,68 @@
<?php
namespace App\Service;
use App\Entity\LegacyJobOrder;
use App\Entity\JobOrder;
use App\Entity\Warranty;
use Doctrine\ORM\EntityManagerInterface;
define("SEARCH_MAX_RESULTS", "20");
class GeneralSearch
{
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function search($search_term)
{
// query legacy job orders for plate number, name (first, middle, last), phone number(mobile, landline)
$legacy_job_orders = $this->em->createQuery('SELECT l FROM App\Entity\LegacyJobOrder l
WHERE l.plate_number LIKE :search_term
OR l.cust_mobile LIKE :search_term
OR l.cust_landline LIKE :search_term
OR l.cust_name LIKE :search_term
OR l.cust_last_name LIKE :search_term
OR l.cust_middle_name LIKE :search_term
OR l.cust_first_name LIKE :search_term
OR l.cust_contact LIKE :search_term')
->setParameter('search_term', "%" . $search_term . "%")
->setMaxResults(SEARCH_MAX_RESULTS)
->getResult();
// query current job orders for plate number, name(first, last), phone number(mobile, landline, office, fax)
// join with customervehicle for plate number. join with customer for name and number
$job_orders = $this->em->createQuery('SELECT jo FROM App\Entity\JobOrder jo
JOIN jo.cus_vehicle cv
JOIN jo.customer c
WHERE cv.plate_number LIKE :search_term
OR c.first_name LIKE :search_term
OR c.last_name LIKE :search_term
OR c.phone_mobile LIKE :search_term
OR c.phone_landline LIKE :search_term
OR c.phone_office LIKE :search_term
OR c.phone_fax LIKE :search_term')
->setParameter('search_term', "%" . $search_term . "%")
->setMaxResults(SEARCH_MAX_RESULTS)
->getResult();
// query warranty for plate number, name(first, last), phone number(mobile)
$warranties = $this->em->createQuery('SELECT w FROM App\Entity\Warranty w
WHERE w.plate_number LIKE :search_term
OR w.first_name LIKE :search_term
OR w.last_name LIKE :search_term
OR w.mobile_number LIKE :search_term')
->setParameter('search_term', "%" . $search_term . "%")
->setMaxResults(SEARCH_MAX_RESULTS)
->getResult();
$results = array('legacy_job_orders'=>$legacy_job_orders, 'job_orders'=>$job_orders, 'warranties'=>$warranties);
return $results;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Service;
use App\Entity\SupportedArea;
use Doctrine\ORM\EntityManagerInterface;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
class GeofenceTracker
{
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function isCovered($long, $lat)
{
// see if the point is in any of the polygons
$query = $this->em->createQuery('SELECT count(s) from App\Entity\SupportedArea s where st_contains(s.coverage_area, point(:long, :lat)) = true')
->setParameter('long', $long)
->setParameter('lat', $lat);
// number of polygons that contain the point
$count = $query->getSingleScalarResult();
if ($count > 0)
return true;
return false;
}
}

View file

@ -0,0 +1,82 @@
<?php
namespace App\Service;
use XMLReader;
use App\Entity\SupportedArea;
use Doctrine\ORM\EntityManagerInterface;
use CrEOF\Spatial\PHP\Types\Geometry\Polygon;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
use CrEOF\Spatial\PHP\Types\Geometry\LineString;
class KMLFileImporter
{
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function getMapData($fh)
{
$placemark_name = '';
$reader = new XMLReader();
$reader->open($fh);
while($reader->read())
{
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == "Placemark")
{
while($reader->read())
{
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == "name")
{
$placemark_name = $reader->readInnerXML();
}
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == "coordinates")
{
// each polygon is a new area
$supported_area = new SupportedArea();
$supported_area->setName($placemark_name);
$point_array = [];
$coordinates = $reader->readInnerXML();
// get each line
$coord_split = explode("\n", $coordinates);
// go through all the coordinates
foreach ($coord_split as $coord)
{
// skip blank lines
$coord_trim = trim($coord);
if (strlen($coord_trim) <= 0)
continue;
// echo "$coord_trim\n";
$point_split = explode(',', $coord_trim);
$point_array[] = new Point($point_split[0], $point_split[1]);
}
$area = new Polygon([new LineString($point_array)]);
$supported_area->setCoverageArea($area);
// add supported area
$this->em->persist($supported_area);
}
}
}
}
$reader->close();
$this->em->flush();
}
}

View file

@ -103,53 +103,44 @@
<li class="m-menu__item">
<h3 class="m-menu__heading m-menu__toggle">
<span class="m-menu__link-text">
Finance Reports
Job Order Reports
</span>
<i class="m-menu__ver-arrow la la-angle-right"></i>
</h3>
<ul class="m-menu__inner">
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
<a href="header/actions.html" class="m-menu__link">
<i class="m-menu__link-icon flaticon-map"></i>
<a href="{{ url('rep_reject_summary_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">
Annual Reports
Summary Rejection Report
</span>
</a>
</li>
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
<a href="header/actions.html" class="m-menu__link">
<i class="m-menu__link-icon flaticon-user"></i>
<a href="{{ url('rep_reject_detail_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">
HR Reports
Detailed Rejection Report
</span>
</a>
</li>
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
<a href="header/actions.html" class="m-menu__link">
<i class="m-menu__link-icon flaticon-clipboard"></i>
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
<a href= "{{ url('rep_battery_conflict_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">
IPO Reports
</span>
</a>
</li>
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
<a href="header/actions.html" class="m-menu__link">
<i class="m-menu__link-icon flaticon-graphic-1"></i>
<span class="m-menu__link-text">
Finance Margins
</span>
</a>
</li>
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
<a href="header/actions.html" class="m-menu__link">
<i class="m-menu__link-icon flaticon-graphic-2"></i>
<span class="m-menu__link-text">
Revenue Reports
Battery Conflict Report
</span>
</a>
</li>
</ul>
</li>
<!--
<li class="m-menu__item">
<h3 class="m-menu__heading m-menu__toggle">
<span class="m-menu__link-text">
@ -342,6 +333,7 @@
</li>
</ul>
</li>
-->
</ul>
</div>
</div>

View file

@ -157,6 +157,14 @@
<div class="form-control-feedback hide" data-field="warr_commercial"></div>
<span class="m-form__help">In months</span>
</div>
<div class="col-lg-4">
<label data-field="warr_tnv">
TNV
</label>
<input type="number" name="warr_tnv" class="form-control m-input" value="{{ obj.getWarrantyTnv }}">
<div class="form-control-feedback hide" data-field="warr_tnv"></div>
<span class="m-form__help">In months</span>
</div>
</div>
</div>
<div class="m-form__seperator m-form__seperator--dashed"></div>

View file

@ -578,7 +578,7 @@
<div class="m-form__section">
<div class="form-group m-form__group row">
<div class="col-lg-2">
<label for="faciliateted-type">Rider Facilitated By</label>
<label for="faciliateted-type">Battery Facilitated By</label>
<select name="facilitated_type" class="form-control m-input" id="facilitated-type">
<option value="">None</option>
{% for key, type in facilitated_types %}

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">Battery Conflict 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_battery_conflict_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 %}

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">Detailed Rejection 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_reject_detail_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 %}

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">Rejection 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_reject_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 %}

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">Summary Rejection 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_reject_summary_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 %}

View file

@ -0,0 +1,215 @@
{% 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">
{% if mode == 'form' %}
Search
{% else %}
Search results for "<span class="text-primary">{{ search_term }}</span>"
{% endif %}
</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-search"></i>
</span>
<h3 class="m-portlet__head-text">
Enter a search term
</h3>
</div>
</div>
</div>
<form id="search-form" autocomplete="off" class="m-form m-form--fit m-form--label-align-right m-form--group-seperator-dashed" method="get" action="{{ url('search_history') }}">
<div class="m-portlet__body">
<div class="form-group m-form__group row">
<input name="search" type="search" class="form-control" placeholder="Search for..." value="{{ search_term|default("") }}" required>
</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">Search</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
{% if mode == 'results' %}
<!-- legacy job orders -->
<div class="row">
<div class="col-12">
<div class="m-portlet m-portlet--mobile">
<div class="m-portlet__head">
<div class="m-portlet__head-caption">
<div class="m-portlet__head-title">
<h3 class="m-portlet__head-text">
Legacy Job Orders
</h3>
</div>
</div>
</div>
<div class="m-portlet__body">
{% if data.legacy_job_orders is empty %}
<div class="m-alert m-alert--icon m-alert--icon-solid m-alert--outline alert alert-brand" role="alert">
<div class="m-alert__icon">
<i class="flaticon-exclamation-2"></i>
<span></span>
</div>
<div class="m-alert__text">
No records for legacy job orders.
</div>
</div>
{% else %}
<table class="table table-striped m-table">
<thead>
<tr class="m-table__row--brand">
<th>ID</th>
<th>Last Name</th>
<th>First Name</th>
<th>Plate Number</th>
</tr>
</thead>
<tbody>
{% for key, result in data.legacy_job_orders %}
<tr>
<td>{{ result.getID|default("") }}</td>
<td>{{ result.getCustLastName|default('') }}</td>
<td>{{ result.getCustFirstName|default('') }}</td>
<td>{{ result.getPlateNumber|default('') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
</div>
</div>
<!-- end legacy job orders -->
<!-- job orders -->
<div class="row">
<div class="col-12">
<div class="m-portlet m-portlet--mobile">
<div class="m-portlet__head">
<div class="m-portlet__head-caption">
<div class="m-portlet__head-title">
<h3 class="m-portlet__head-text">
Job Orders
</h3>
</div>
</div>
</div>
<div class="m-portlet__body">
{% if data.job_orders is empty %}
<div class="m-alert m-alert--icon m-alert--icon-solid m-alert--outline alert alert-brand" role="alert">
<div class="m-alert__icon">
<i class="flaticon-exclamation-2"></i>
<span></span>
</div>
<div class="m-alert__text">
No records for job orders.
</div>
</div>
{% else %}
<table class="table table-striped m-table">
<thead>
<tr class="m-table__row--brand">
<th>ID</th>
<th>Last Name</th>
<th>First Name</th>
<th>Plate Number</th>
</tr>
</thead>
<tbody>
{% for key, result in data.job_orders %}
<tr>
<td>{{ result.getID|default("") }}</td>
<td>{{ result.getCustomer.getLastName|default("") }}</td>
<td>{{ result.getCustomer.getFirstName|default("") }}</td>
<td>{{ result.getCustomerVehicle.getPlateNumber|default("") }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
</div>
</div>
<!-- end job orders -->
<!-- warranties -->
<div class="row">
<div class="col-12">
<div class="m-portlet m-portlet--mobile">
<div class="m-portlet__head">
<div class="m-portlet__head-caption">
<div class="m-portlet__head-title">
<h3 class="m-portlet__head-text">
Warranties
</h3>
</div>
</div>
</div>
<div class="m-portlet__body">
{% if data.warranties is empty %}
<div class="m-alert m-alert--icon m-alert--icon-solid m-alert--outline alert alert-brand" role="alert">
<div class="m-alert__icon">
<i class="flaticon-exclamation-2"></i>
<span></span>
</div>
<div class="m-alert__text">
No records for warranties.
</div>
</div>
{% else %}
<table class="table table-striped m-table">
<thead>
<tr class="m-table__row--brand">
<th>ID</th>
<th>Last Name</th>
<th>First Name</th>
<th>Plate Number</th>
</tr>
</thead>
<tbody>
{% for key, result in data.warranties %}
<tr>
<td>{{ result.getID|default("") }}</td>
<td>{{ result.getLastName|default("") }}</td>
<td>{{ result.getFirstName|default("") }}</td>
<td>{{ result.getPlateNumber|default("") }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
</div>
</div>
<!-- end warranties -->
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,2 @@
update battery as batt inner join battery_model as bmodel on batt.model_id = bmodel.id set batt.warr_tnv=12 where bmodel.name='GOLD';

View file

@ -0,0 +1,4 @@
load data local infile '/tmp/plate_numbers.csv' into table plate_number fields terminated by ',';
load data infile '/tmp/legacy_jo_row.csv' into table legacy_job_order_row fields terminated by ',' enclosed by '"' lines terminated by '\n' (job_order_id, item, qty, price, price_level, status, account, enrollee) set id = null;
load data local infile '/tmp/warranty.csv' into table warranty fields terminated by ',' (bty_model_id, bty_size_id, serial, warranty_class, plate_number, status, date_create, date_purchase, date_expire, date_claim, claim_id, sap_bty_id, first_name, last_name, mobile_number) set id = null;
load data infile '/tmp/legacy_jo.csv' into table legacy_job_order fields terminated by '|' (id, trans_date, trans_type, origin, car_brand, car_make, car_model, car_color, cust_name, cust_first_name, cust_middle_name, cust_last_name, cust_contact, cust_mobile, cust_landline, delivery_instructions, agent_notes_1, delivery_date, delivery_time, advance_order, stage, cancel_reason, cancel_reason_specify, payment_method, prepared_by, dispatch_time, dispatch_date, dispatched_by, address, landmark, date_purchase, plate_number);