Resolve "Entity logging service / bundle" #1208

Open
korina.cordero wants to merge 35 commits from 330-entity-logging-service-bundle into master
23 changed files with 821 additions and 93 deletions

View file

@ -14,9 +14,15 @@ APP_SECRET=b344cd6cd151ae1d61403ed55806c5ce
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# Configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
LOGGING_DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_logging
###< doctrine/doctrine-bundle ###
GMAPS_API_KEY=insert_gmapsapikey_here
# influxdb
INFLUXDB_HOST=127.0.0.1
INFLUXDB_PORT=8086
INFLUXDB_DB=logging_db
# rising tide sms gateway
RT_USER=rt_user
RT_PASS=rt_pass

View file

@ -14,6 +14,7 @@
"data-dog/audit-bundle": "^0.1.10",
"edwinhoksberg/php-fcm": "^1.0",
"guzzlehttp/guzzle": "^6.3",
"influxdb/influxdb-php": "^1.15",
"predis/predis": "^1.1",
"sensio/framework-extra-bundle": "^5.1",
"setasign/fpdf": "^1.8",

65
composer.lock generated
View file

@ -1,10 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b101ecfbc1f6f2270f0e8ad326035b7e",
"content-hash": "cbde0e7f1fa49277c6196a3c677c3a51",
"packages": [
{
"name": "catalyst/auth-bundle",
@ -1836,6 +1836,67 @@
],
"time": "2019-07-01T23:21:34+00:00"
},
{
"name": "influxdb/influxdb-php",
"version": "1.15.0",
"source": {
"type": "git",
"url": "https://github.com/influxdata/influxdb-php.git",
"reference": "bf3415f81962e1ab8c939bc1a08a85f500bead35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/bf3415f81962e1ab8c939bc1a08a85f500bead35",
"reference": "bf3415f81962e1ab8c939bc1a08a85f500bead35",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0",
"php": "^5.5 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
},
"suggest": {
"ext-curl": "Curl extension, needed for Curl driver",
"stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP."
},
"type": "library",
"autoload": {
"psr-4": {
"InfluxDB\\": "src/InfluxDB"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gianluca Arbezzano",
"email": "gianarb92@gmail.com"
},
{
"name": "Daniel Martinez",
"email": "danimartcas@hotmail.com"
},
{
"name": "Stephen Hoogendijk",
"email": "stephen@tca0.nl"
}
],
"description": "InfluxDB client library for PHP",
"keywords": [
"client",
"influxdata",
"influxdb",
"influxdb class",
"influxdb client",
"influxdb library",
"time series"
],
"time": "2019-05-30T00:15:14+00:00"
},
{
"name": "jdorn/sql-formatter",
"version": "v1.2.17",

View file

@ -21,6 +21,10 @@ access_keys:
label: Super Admin Role
- id: user.profile
label: User Profile
- id: user.logs
label: User Logs
- id: user.change.history
label: User Change History
- id: role
label: Role Access
acls:

View file

@ -12,6 +12,7 @@ parameters:
app_access_key: 'access_keys'
cvu_brand_id: "%env(CVU_BRAND_ID)%"
country_code: "%env(COUNTRY_CODE)%"
log_db: "%env(INFLUXDB_DB)%"
services:
# default configuration for services in *this* file
@ -202,6 +203,12 @@ services:
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Bing"
App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet"
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google"
# influxdb
InfluxDB\Client:
arguments: ['%env(INFLUXDB_HOST)%', '%env(INFLUXDB_PORT)%']
InfluxDB\Database:
arguments: ['%env(INFLUXDB_DB)%', "@InfluxDB\\Client"]
App\EventListener\JobOrderActiveCacheListener:
arguments:
@ -229,6 +236,28 @@ services:
$loc_key: "%env(LOCATION_RIDER_ACTIVE_KEY)%"
$status_key: "%env(STATUS_RIDER_KEY)%"
App\EventListener\JobOrderStatusListener:
arguments:
$wh: "@App\\Service\\WarrantyHandler"
tags:
- name: 'doctrine.orm.entity_listener'
event: 'postUpdate'
entity: 'App\Entity\JobOrder'
- name: 'doctrine.orm.entity_listener'
event: 'postPersist'
entity: 'App\Entity\JobOrder'
App\EventListener\EntityListener:
arguments:
$token_storage: "@security.token_storage"
$log_db: "@InfluxDB\\Database"
$entities: ['App\Entity\User', 'App\Entity\Role', 'App\Entity\Partner']
tags:
- name: 'doctrine.event_listener'
event: 'onFlush'
- name: 'doctrine.event_listener'
event: 'postPersist'
# API logging
App\EventSubscriber\LogSubscriber:
arguments:

View file

@ -19,7 +19,6 @@ doctrine:
point: CrEOF\Spatial\DBAL\Types\Geometry\PointType
polygon: CrEOF\Spatial\DBAL\Types\Geometry\PolygonType
linestring: CrEOF\Spatial\DBAL\Types\Geometry\LineStringType
multipolygon: CrEOF\Spatial\DBAL\Types\Geometry\MultiPolygonType
orm:
auto_generate_proxy_classes: '%kernel.debug%'
naming_strategy: doctrine.orm.naming_strategy.underscore

View file

@ -12,6 +12,7 @@ parameters:
app_access_key: 'access_keys'
cvu_brand_id: "%env(CVU_BRAND_ID)%"
country_code: "%env(COUNTRY_CODE)%"
log_db: "%env(INFLUXDB_DB)%"
services:
# default configuration for services in *this* file
@ -201,7 +202,13 @@ services:
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Bing"
App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet"
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google"
# influxdb
InfluxDB\Client:
arguments: ['%env(INFLUXDB_HOST)%', '%env(INFLUXDB_PORT)%']
InfluxDB\Database:
arguments: ['%env(INFLUXDB_DB)%', "@InfluxDB\\Client"]
App\EventListener\JobOrderActiveCacheListener:
arguments:
$jo_cache: "@App\\Service\\JobOrderCache"
@ -228,6 +235,28 @@ services:
$loc_key: "%env(LOCATION_RIDER_ACTIVE_KEY)%"
$status_key: "%env(STATUS_RIDER_KEY)%"
App\EventListener\JobOrderStatusListener:
arguments:
$wh: "@App\\Service\\WarrantyHandler"
tags:
- name: 'doctrine.orm.entity_listener'
event: 'postUpdate'
entity: 'App\Entity\JobOrder'
- name: 'doctrine.orm.entity_listener'
event: 'postPersist'
entity: 'App\Entity\JobOrder'
App\EventListener\EntityListener:
arguments:
$token_storage: "@security.token_storage"
$log_db: "@InfluxDB\\Database"
$entities: ['App\Entity\User', 'App\Entity\Role', 'App\Entity\Partner']
tags:
- name: 'doctrine.event_listener'
event: 'onFlush'
- name: 'doctrine.event_listener'
event: 'postPersist'
# inventory manager
App\Service\InventoryManager:
arguments:

View file

@ -41,3 +41,23 @@ user_profile_submit:
path: /profile
controller: App\Controller\UserController::profileSubmit
methods: [POST]
user_view_logs_form:
path: /users/{id}/logs
controller: App\Controller\UserController::viewLogsForm
methods: [GET]
user_view_logs:
path: /user/{id}/logs
controller: App\Controller\UserController::getAuditLogs
methods: [POST]
user_view_change_history_form:
path: /users/{id}/change-history
controller: App\Controller\UserController::viewChangeHistoryForm
methods: [GET]
user_view_change_history:
path: /user/{id}/change-history
controller: App\Controller\UserController::getChangeHistory
methods: [POST]

View file

@ -12,6 +12,7 @@ parameters:
app_access_key: 'access_keys'
cvu_brand_id: "%env(CVU_BRAND_ID)%"
country_code: "%env(COUNTRY_CODE)%"
log_db: "%env(INFLUXDB_DB)%"
services:
# default configuration for services in *this* file
@ -21,7 +22,7 @@ services:
public: false # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
@ -201,6 +202,12 @@ services:
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Bing"
App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet"
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google"
# influxdb
InfluxDB\Client:
arguments: ['%env(INFLUXDB_HOST)%', '%env(INFLUXDB_PORT)%']
InfluxDB\Database:
arguments: ['%env(INFLUXDB_DB)%', "@InfluxDB\\Client"]
App\EventListener\JobOrderActiveCacheListener:
arguments:
@ -228,6 +235,28 @@ services:
$loc_key: "%env(LOCATION_RIDER_ACTIVE_KEY)%"
$status_key: "%env(STATUS_RIDER_KEY)%"
App\EventListener\JobOrderStatusListener:
arguments:
$wh: "@App\\Service\\WarrantyHandler"
tags:
- name: 'doctrine.orm.entity_listener'
event: 'postUpdate'
entity: 'App\Entity\JobOrder'
- name: 'doctrine.orm.entity_listener'
event: 'postPersist'
entity: 'App\Entity\JobOrder'
App\EventListener\EntityListener:
arguments:
$token_storage: "@security.token_storage"
$log_db: "@InfluxDB\\Database"
$entities: ['App\Entity\User', 'App\Entity\Role', 'App\Entity\Partner']
tags:
- name: 'doctrine.event_listener'
event: 'onFlush'
- name: 'doctrine.event_listener'
event: 'postPersist'
# inventory manager
App\Service\InventoryManager:
arguments:

View file

@ -15,6 +15,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Catalyst\MenuBundle\Annotation\Menu;
use InfluxDB\Client;
class UserController extends Controller
{
/**
@ -484,4 +486,67 @@ class UserController extends Controller
]);
}
}
/**
* @Menu(selected="user_list")
*/
public function viewLogsForm($id)
{
$this->denyAccessUnlessGranted('user.logs', null, 'No access.');
$params['id'] = $id;
// response
return $this->render('user/audit.log.html.twig', $params);
}
public function getAuditLogs(Client $client, $id)
{
// fetch database
$log_db = $this->getParameter('log_db');
$database = $client->selectDB($log_db);
// query will return a resultset object
$query_string = 'SELECT * FROM entity_log WHERE "user" = ' . $id;
$result = $database->query($query_string);
// get the points from the resultset, which is an array
$points = $result->getPoints();
return $this->json([
'data' => $points,
]);
}
/**
* @Menu(selected="user_list")
*/
public function viewChangeHistoryForm($id)
{
$this->denyAccessUnlessGranted('user.change.history', null, 'No access.');
$params['id'] = $id;
// response
return $this->render('user/change.history.log.html.twig', $params);
}
public function getChangeHistory(Client $client, $id)
{
// fetch database
$log_db = $this->getParameter('log_db');
$database = $client->selectDB($log_db);
// query will return a resultset object
$query_string = 'SELECT * FROM entity_log WHERE entity_id = \'' . $id . '\'';
$result = $database->query($query_string);
// get the points from the resultset, which is an array
$points = $result->getPoints();
return $this->json([
'data' => $points,
]);
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace App\Entity;
interface AuditableEntity
{
public function fieldDump();
}

View file

@ -14,7 +14,7 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
* @UniqueEntity("id")
* @UniqueEntity("name")
*/
class Role extends BaseRole
class Role extends BaseRole implements AuditableEntity
{
/**
* @ORM\ManyToMany(targetEntity="User", mappedBy="roles", fetch="EXTRA_LAZY")
@ -25,4 +25,18 @@ class Role extends BaseRole
{
parent::__construct();
}
public function fieldDump()
{
// get all fields and their values into an array
$field_array = [
'id' => $this->getID(),
'name' => $this->getName(),
'acl_attributes' => $this->getACLAttributes(),
];
$fields = json_encode($field_array);
return $fields;
}
}

View file

@ -16,7 +16,7 @@ use Serializable;
* @UniqueEntity("username")
* @UniqueEntity("email")
*/
class User extends BaseUser implements Serializable
class User extends BaseUser implements Serializable, AuditableEntity
{
/**
* @ORM\Id
@ -309,4 +309,24 @@ class User extends BaseUser implements Serializable
{
return $this->partners_created;
}
public function fieldDump()
{
// get all fields and their values into an array
$field_array = [
'id' => $this->getID(),
'username' => $this->getUsername(),
'password' => $this->getPassword(),
'first_name' => $this->getFirstName(),
'last_name' => $this->getLastName(),
'email' => $this->getEmail(),
'contact_number' => $this->getContactNumber(),
'roles' => $this->getRoles(),
'hubs' => $this->getHubs(),
];
$fields = json_encode($field_array);
return $fields;
}
}

View file

@ -0,0 +1,194 @@
<?php
namespace App\EventListener;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;
use InfluxDB\Point;
use App\Entity\AuditableEntity;
class EntityListener
{
protected $token_storage;
protected $log_db;
protected $entities;
public function __construct(TokenStorageInterface $token_storage, $log_db,
$entities)
{
$this->token_storage = $token_storage;
$this->log_db = $log_db;
// reconstruct entity array with class name as keys
// with values of true
foreach ($entities as $entity)
{
$this->entities[$entity] = true;
}
}
public function postPersist(LifecycleEventArgs $args)
{
$object = $args->getObject();
$object_class = get_class($object);
if (isset($this->entities[$object_class]))
{
if ($object instanceof AuditableEntity)
{
// get user id for logging
$user = $this->token_storage->getToken()->getUser();
$user_id = $user->getID();
// get entity name
//$obj_class = preg_replace('/.*\\\\/', '', $object_class);
// get fields of object
$fields = $object->fieldDump();
$this->writeToLogDB($object_class, $object->getID(), 'create', $fields, $user_id);
}
else
error_log($object_class . ' does not implement the AuditableEntity interface.');
}
}
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
// get date time of event for logging
//$event_time = new DateTime();
// get user for logging
$user = $this->token_storage->getToken()->getUser();
$user_id = $user->getID();
$field_changes = [];
$unit_of_work = $em->getUnitOfWork();
$created_entities = $unit_of_work->getScheduledEntityInsertions();
$deleted_entities = $unit_of_work->getScheduledEntityDeletions();
$updated_entities = $unit_of_work->getScheduledEntityUpdates();
$updated_collections = $unit_of_work->getScheduledCollectionUpdates();
$deleted_collections = $unit_of_work->getScheduledCollectionDeletions();
// get updated fields. This doesn't include lists of other entities.
foreach ($updated_entities as $updated_entity)
{
// check if entity is in list of entities to log
$entity_class = get_class($updated_entity);
if (isset($this->entities[$entity_class]))
{
if ($updated_entity instanceof AuditableEntity)
{
// get entity name
//$obj_class = preg_replace('/.*\\\\/', '', $entity_class);
$changeset = $unit_of_work->getEntityChangeSet($updated_entity);
$entity_fields = array_keys($changeset);
foreach ($entity_fields as $field)
{
$values = $changeset[$field];
$old_value = $values[0];
$new_value = $values[1];
$field_changes[$field] = [
'old_value' => $values[0],
'new_value' => $values[1],
];
}
$fields = json_encode($field_changes);
$this->writeToLogDB($entity_class, $updated_entity->getID(), 'update', $fields, $user_id);
}
else
error_log($entity_class . ' does not implement the AuditableEntity interface.');
}
}
// get deleted objects
foreach ($deleted_entities as $deleted_entity)
{
// check if entity is in list of entities to log
$entity_class = get_class($deleted_entity);
if (isset($this->entities[$entity_class]))
{
if ($deleted_entity instanceof AuditableEntity)
{
// get entity name
//$obj_class = preg_replace('/.*\\\\/', '', get_class($deleted_entity));
$deleted_id = $deleted_entity->getID();
// get fields of deleted entity
$fields = $deleted_entity->fieldDump();
$this->writeToLogDB($entity_class, $deleted_id, 'delete', $fields, $user_id);
}
else
error_log($entity_class . ' does not implement the AuditableEntity interface.');
}
}
// TODO: find a way to get the list of "old" items, compare with list of updated items
// so we can find out what was added/removed
/*
foreach ($updated_collections as $updated_collection)
{
error_log('in updated_collection snippet');
foreach ($updated_collection as $item)
{
if ($item instanceof Role)
{
error_log('updated_collection item ' . $item->getName());
}
if ($item instanceof Hub)
{
error_log('updated_collection item ' . $item->getName());
}
}
}
foreach ($deleted_collections as $deleted_collection)
{
error_log('in deleted_collection snippet');
foreach ($deleted_collection as $item)
{
if ($item instanceof Role)
{
error_log('deleted_collection item ' . $item->getName());
}
if ($item instanceof Hub)
{
error_log('deleted_collection item ' . $item->getName());
}
}
} */
}
protected function writeToLogDB($entity_type, $entity_id, $action, $content, $user_id)
{
$new_point = new Point(
'entity_log', // measurement
null, // measurement value
['entity_type' => $entity_type, 'entity_id' => $entity_id], // measurement tags
['action' => $action, 'content' => $content, 'user' => $user_id], // measurement fields
exec('date +%s%N') // timestamp in nanoseconds on Linux only
);
$this->log_db->writePoints([$new_point]);
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace App\EventListener;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use App\Entity\JobOrder;
use App\Entity\Warranty;
use App\Service\WarrantyHandler;
use App\Service\JobOrderHandlerInterface;
use App\Ramcar\JOStatus;
class JobOrderStatusListener
{
protected $wh;
protected $jo_handler;
public function __construct(WarrantyHandler $wh, JobOrderHandlerInterface $jo_handler)
{
$this->wh = $wh;
$this->jo_handler = $jo_handler;
}
// when a JO is updated
public function postUpdate(JobOrder $jo, LifecycleEventArgs $args)
{
$status = $jo->getStatus();
if ($status == JOStatus::FULFILLED)
{
$this->createWarrantyFromJO($jo);
}
}
// when a new job order comes in and status is already fulfilled
public function postPersist(JobOrder $jo, LifecycleEventArgs $args)
{
$status = $jo->getStatus();
if ($status == JOStatus::FULFILLED)
{
$this->createWarrantyFromJO($jo);
}
}
protected function createWarrantyFromJO(JobOrder $jo)
{
// create warranty
if ($this->jo_handler->checkIfNewBattery($jo))
{
$serial = null;
$warranty_class = $jo->getWarrantyClass();
$first_name = $jo->getCustomer()->getFirstName();
$last_name = $jo->getCustomer()->getLastName();
$mobile_number = $jo->getCustomer()->getPhoneMobile();
// check if date fulfilled is null
if ($jo->getDateFulfill() == null)
$date_purchase = $jo->getDateCreate();
else
$date_purchase = $jo->getDateFulfill();
// validate plate number
// $plate_number = $this->wh->cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber());
$plate_number = Warranty::cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber());
if ($plate_number != false)
{
$batt_list = array();
$invoice = $jo->getInvoice();
if (!empty($invoice))
{
// get battery
$invoice_items = $invoice->getItems();
foreach ($invoice_items as $item)
{
$battery = $item->getBattery();
if ($battery != null)
{
$batt_list[] = $item->getBattery();
}
}
}
$this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
}
}
}
}

View file

@ -24,7 +24,6 @@ use App\Entity\Hub;
use App\Entity\Promo;
use App\Entity\Rider;
use App\Entity\JORejection;
use App\Entity\Warranty;
use App\Entity\Customer;
use App\Entity\ServiceCharge;
@ -44,7 +43,6 @@ use App\Service\InvoiceGeneratorInterface;
use App\Service\JobOrderHandlerInterface;
use App\Service\RiderAssignmentHandlerInterface;
use App\Service\CustomerHandlerInterface;
use App\Service\WarrantyHandler;
use App\Service\MQTTClient;
use App\Service\APNSClient;
use App\Service\MapTools;
@ -67,7 +65,6 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
protected $translator;
protected $rah;
protected $country_code;
protected $wh;
protected $cust_handler;
protected $template_hash;
@ -75,8 +72,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
public function __construct(Security $security, EntityManagerInterface $em,
InvoiceGeneratorInterface $ic, ValidatorInterface $validator,
TranslatorInterface $translator, RiderAssignmentHandlerInterface $rah,
string $country_code, WarrantyHandler $wh,
CustomerHandlerInterface $cust_handler)
string $country_code, CustomerHandlerInterface $cust_handler)
{
$this->em = $em;
$this->ic = $ic;
@ -85,7 +81,6 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$this->translator = $translator;
$this->rah = $rah;
$this->country_code = $country_code;
$this->wh = $wh;
$this->cust_handler = $cust_handler;
$this->loadTemplates();
@ -977,6 +972,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// call rider assignment handler's fulfillJobOrder
$this->rah->fulfillJobOrder($obj, $image_url, $rider);
/*
// create the warranty if new battery only
if ($this->checkIfNewBattery($obj))
{
@ -1019,7 +1015,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
}
}
} */
// validated! save the entity
$em->flush();
@ -2658,7 +2654,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$cust_vehicle->setWarrantyCode($req->request->get('warranty_code'));
$em->persist($cust_vehicle);
/*
// create the warranty if new battery only
if ($this->checkIfNewBattery($jo))
{
@ -2701,7 +2697,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
}
}
} */
$em->flush();
}

View file

@ -23,7 +23,6 @@ use App\Entity\Hub;
use App\Entity\Promo;
use App\Entity\Rider;
use App\Entity\JORejection;
use App\Entity\Warranty;
use App\Entity\Customer;
use App\Ramcar\InvoiceCriteria;
@ -41,7 +40,6 @@ use App\Ramcar\JORejectionReason;
use App\Service\InvoiceGeneratorInterface;
use App\Service\JobOrderHandlerInterface;
use App\Service\RiderAssignmentHandlerInterface;
use App\Service\WarrantyHandler;
use App\Service\MQTTClient;
use App\Service\APNSClient;
use App\Service\MapTools;
@ -64,14 +62,13 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
protected $translator;
protected $rah;
protected $country_code;
protected $wh;
protected $template_hash;
public function __construct(Security $security, EntityManagerInterface $em,
InvoiceGeneratorInterface $ic, ValidatorInterface $validator,
TranslatorInterface $translator, RiderAssignmentHandlerInterface $rah,
string $country_code, WarrantyHandler $wh)
string $country_code)
{
$this->em = $em;
$this->ic = $ic;
@ -80,7 +77,6 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
$this->translator = $translator;
$this->rah = $rah;
$this->country_code = $country_code;
$this->wh = $wh;
$this->loadTemplates();
}
@ -824,6 +820,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
// call rider assignment handler's fulfillJobOrder
$this->rah->fulfillJobOrder($obj, $image_url, $rider);
/*
// create the warranty if new battery only
if ($this->checkIfNewBattery($obj))
{
@ -862,7 +859,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface
$this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
}
}
} */
}
}

View file

@ -18,7 +18,6 @@ use App\Service\RiderAPIHandlerInterface;
use App\Service\RedisClientProvider;
use App\Service\RiderCache;
use App\Service\MQTTClient;
use App\Service\WarrantyHandler;
use App\Service\JobOrderHandlerInterface;
use App\Service\InvoiceGeneratorInterface;
@ -30,7 +29,6 @@ use App\Entity\Promo;
use App\Entity\Battery;
use App\Entity\BatteryModel;
use App\Entity\BatterySize;
use App\Entity\Warranty;
use DateTime;
@ -42,7 +40,6 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
protected $rcache;
protected $country_code;
protected $mclient;
protected $wh;
protected $jo_handler;
protected $ic;
protected $session;
@ -50,8 +47,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
public function __construct(EntityManagerInterface $em, RedisClientProvider $redis,
EncoderFactoryInterface $ef, RiderCache $rcache,
string $country_code, MQTTClient $mclient,
WarrantyHandler $wh, JobOrderHandlerInterface $jo_handler,
InvoiceGeneratorInterface $ic)
JobOrderHandlerInterface $jo_handler, InvoiceGeneratorInterface $ic)
{
$this->em = $em;
$this->redis = $redis;
@ -59,7 +55,6 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$this->rcache = $rcache;
$this->country_code = $country_code;
$this->mclient = $mclient;
$this->wh = $wh;
$this->jo_handler = $jo_handler;
$this->ic = $ic;
@ -523,6 +518,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$this->em->flush();
/*
// create warranty
if($this->jo_handler->checkIfNewBattery($jo))
{
@ -561,7 +557,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
}
}
} */
// send mqtt event (fulfilled)
$rider = $this->session->getRider();

View file

@ -18,7 +18,6 @@ use App\Service\RiderAPIHandlerInterface;
use App\Service\RedisClientProvider;
use App\Service\RiderCache;
use App\Service\MQTTClient;
use App\Service\WarrantyHandler;
use App\Service\JobOrderHandlerInterface;
use App\Service\InvoiceGeneratorInterface;
@ -41,7 +40,6 @@ class ResqRiderAPIHandler implements RiderAPIHandlerInterface
protected $rcache;
protected $country_code;
protected $mclient;
protected $wh;
protected $jo_handler;
protected $ic;
protected $session;
@ -49,8 +47,7 @@ class ResqRiderAPIHandler implements RiderAPIHandlerInterface
public function __construct(EntityManagerInterface $em, RedisClientProvider $redis,
EncoderFactoryInterface $ef, RiderCache $rcache,
string $country_code, MQTTClient $mclient,
WarrantyHandler $wh, JobOrderHandlerInterface $jo_handler,
InvoiceGeneratorInterface $ic)
JobOrderHandlerInterface $jo_handler, InvoiceGeneratorInterface $ic)
{
$this->em = $em;
$this->redis = $redis;
@ -58,7 +55,6 @@ class ResqRiderAPIHandler implements RiderAPIHandlerInterface
$this->rcache = $rcache;
$this->country_code = $country_code;
$this->mclient = $mclient;
$this->wh = $wh;
$this->jo_handler = $jo_handler;
$this->ic = $ic;
@ -525,6 +521,7 @@ class ResqRiderAPIHandler implements RiderAPIHandlerInterface
$this->em->flush();
/*
// create warranty
if($this->jo_handler->checkIfNewBattery($jo))
{
@ -559,7 +556,7 @@ class ResqRiderAPIHandler implements RiderAPIHandlerInterface
}
$this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
}
} */
// send mqtt event (fulfilled)
$rider = $this->session->getRider();

View file

@ -110,6 +110,9 @@
"guzzlehttp/psr7": {
"version": "1.4.2"
},
"influxdb/influxdb-php": {
"version": "1.15.0"
},
"jdorn/sql-formatter": {
"version": "v1.2.17"
},

View file

@ -0,0 +1,79 @@
{% 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">Audit Logs</h3>
</div>
</div>
</div>
<!-- END: Subheader -->
<div class="m-content">
<!--Begin::Section-->
<div class="row">
<div class="col-xl-12">
<div class="m-portlet m-portlet--mobile">
<div class="m-portlet__body">
<!--begin: Datatable -->
<div id="data-rows"></div>
<!--end: Datatable -->
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(function() {
console.log( {{ id }} );
var options = {
data: {
type: 'remote',
source: {
read: {
url: '{{ url('user_view_logs', {'id': id}) }}',
method: 'POST'
}
},
saveState: {
cookie: false,
webstorage: false
},
},
columns: [
{
field: 'time',
title: 'Time'
},
{
field: 'action',
title: 'Action'
},
{
field: 'content',
title: 'Content'
},
{
field: 'entity_id',
title: 'Entity ID'
},
{
field: 'entity_type',
title: 'Entity Type'
},
{
field: 'user',
title: 'User'
},
],
};
var table = $("#data-rows").mDatatable(options);
});
</script>
{% endblock %}

View file

@ -0,0 +1,79 @@
{% 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">Audit Logs</h3>
</div>
</div>
</div>
<!-- END: Subheader -->
<div class="m-content">
<!--Begin::Section-->
<div class="row">
<div class="col-xl-12">
<div class="m-portlet m-portlet--mobile">
<div class="m-portlet__body">
<!--begin: Datatable -->
<div id="data-rows"></div>
<!--end: Datatable -->
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(function() {
console.log( {{ id }} );
var options = {
data: {
type: 'remote',
source: {
read: {
url: '{{ url('user_view_change_history', {'id': id}) }}',
method: 'POST'
}
},
saveState: {
cookie: false,
webstorage: false
},
},
columns: [
{
field: 'time',
title: 'Time'
},
{
field: 'action',
title: 'Action'
},
{
field: 'content',
title: 'Content'
},
{
field: 'entity_id',
title: 'Entity ID'
},
{
field: 'entity_type',
title: 'Entity Type'
},
{
field: 'user',
title: 'User'
},
],
};
var table = $("#data-rows").mDatatable(options);
});
</script>
{% endblock %}

View file

@ -57,6 +57,16 @@
<div class="form-control-feedback hide" data-field="username"></div>
<span class="m-form__help">Unique alias for this user</span>
</div>
<div class="col-lg-3">
{% if mode == 'update' %}
<a href="{{ url('user_view_logs_form', {'id':obj.getID()}) }}" class="btn btn-success">View Audit Logs</a>
{% endif %}
</div>
<div class="col-lg-3">
{% if mode == 'update' %}
<a href="{{ url('user_view_change_history_form', {'id':obj.getID()}) }}" class="btn btn-success">View Change History</a>
{% endif %}
</div>
</div>
<div class="form-group m-form__group row">
<div class="col-lg-6">
@ -182,72 +192,73 @@
{% endblock %}
{% block scripts %}
<script>
$(function() {
$("#row-form").submit(function(e) {
var form = $(this);
<script>
$(function() {
$("#row-form").submit(function(e) {
var form = $(this);
e.preventDefault();
e.preventDefault();
$.ajax({
method: "POST",
url: form.prop('action'),
data: form.serialize()
}).done(function(response) {
// remove all error classes
removeErrors();
swal({
title: 'Done!',
text: 'Your changes have been saved!',
type: 'success',
onClose: function() {
window.location.href = "{{ url(mode == 'profile' ? 'home' : 'user_list') }}";
}
});
}).fail(function(response) {
if (response.status == 422) {
var errors = response.responseJSON.errors;
var firstfield = false;
$.ajax({
method: "POST",
url: form.prop('action'),
data: form.serialize()
}).done(function(response) {
// remove all error classes
removeErrors();
swal({
title: 'Done!',
text: 'Your changes have been saved!',
type: 'success',
onClose: function() {
window.location.href = "{{ url(mode == 'profile' ? 'home' : 'user_list') }}";
}
});
}).fail(function(response) {
if (response.status == 422) {
var errors = response.responseJSON.errors;
var firstfield = false;
// remove all error classes first
removeErrors();
// remove all error classes first
removeErrors();
// display errors contextually
$.each(errors, function(field, msg) {
var formfield = $("[name='" + field + "']");
var label = $("label[data-field='" + field + "']");
var msgbox = $(".form-control-feedback[data-field='" + field + "']");
// display errors contextually
$.each(errors, function(field, msg) {
var formfield = $("[name='" + field + "']");
var label = $("label[data-field='" + field + "']");
var msgbox = $(".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');
// 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);
// check if this field comes first in DOM
var domfield = formfield.get(0);
if (!firstfield || (firstfield && firstfield.compareDocumentPosition(domfield) === 2)) {
firstfield = domfield;
}
});
if (!firstfield || (firstfield && firstfield.compareDocumentPosition(domfield) === 2)) {
firstfield = domfield;
}
});
// focus on first bad field
firstfield.focus();
// focus on first bad field
firstfield.focus();
// scroll to above that field to make it visible
$('html, body').animate({
scrollTop: $(firstfield).offset().top - 200
}, 100);
}
});
});
// scroll to above that field to make it visible
$('html, body').animate({
scrollTop: $(firstfield).offset().top - 200
}, 100);
}
});
});
// 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');
}
});
</script>
// 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');
}
});
</script>
{% endblock %}