Compare commits
26 Commits
retrieving
...
Documentin
| Author | SHA1 | Date | |
|---|---|---|---|
| febf9f1c9d | |||
| 78e4a456f2 | |||
| 99ec548c83 | |||
| 502b7b6c04 | |||
| 9f1a18c557 | |||
| 0558ce048f | |||
| 78b319a0a3 | |||
| 839be59ac5 | |||
| a05a602954 | |||
| e490f6aed1 | |||
| df33525a4b | |||
| 2053742b11 | |||
| f3682019aa | |||
| b3b3194a20 | |||
| 86a0540da7 | |||
| ee168bd637 | |||
| b233346795 | |||
| ca80abc389 | |||
| e3dfaf9f26 | |||
| 477ac28212 | |||
| 3ad687f797 | |||
| b84b9fa2fb | |||
| f2851ec56a | |||
| 03cbcce59f | |||
| f864e74247 | |||
| c97421df32 |
72
README.rst
Normal file
72
README.rst
Normal file
@@ -0,0 +1,72 @@
|
||||
=================
|
||||
Payroll manager
|
||||
=================
|
||||
|
||||
|codebuild|
|
||||
|
||||
This is a simple system to keep a record of employees where they will perform different tasks within the company and will be paid accordingly in a monthly bases.
|
||||
|
||||
.. contents::
|
||||
|
||||
Getting started
|
||||
-----------------
|
||||
|
||||
The system requires the following:
|
||||
- Ubuntu 16.04
|
||||
- php 7.0
|
||||
- composer
|
||||
- docker
|
||||
- docker-compose
|
||||
- mysql 5.7
|
||||
|
||||
Installation
|
||||
-----------------
|
||||
|
||||
Alternatively to installing all the packages and configuring the server it's possible to start up an instance of the system with docker-compose
|
||||
|
||||
To install docker
|
||||
.. code-block:: bash
|
||||
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
||||
|
||||
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
|
||||
sudo apt-get update
|
||||
|
||||
apt-cache policy docker-ce
|
||||
|
||||
sudo apt-get install -y docker-ce
|
||||
|
||||
To install docker compose
|
||||
.. code-block:: bash
|
||||
sudo curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
|
||||
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
And finally the containers can be initialized by running
|
||||
|
||||
.. code-block:: bash
|
||||
sudo docker-compose up --build -d
|
||||
|
||||
Sign in
|
||||
-----------------
|
||||
|
||||
The login page can be accessed at **http://localhost:8085/public/html/login.php**
|
||||
|
||||
To access the platform the user is **sloth** and the pasword **slothness**
|
||||
|
||||
Further reading
|
||||
-----------------
|
||||
To further read about the api and it's front-end
|
||||
`api-payroll <https://github.com/PootisPenserHere/payroll_manager/blob/master/api-payroll/README.rst>`_
|
||||
|
||||
More about the database `database <https://github.com/PootisPenserHere/payroll_manager/blob/master/database/README.rst>`_
|
||||
|
||||
|
||||
Data volumes
|
||||
-----------------
|
||||
Since the application is designed to run within containers a number of volumes has been created to persist the data, they can be found in the volumes directory on the root of the project
|
||||
|
||||
.. |codebuild| image:: https://s3.amazonaws.com/codefactory-us-east-1-prod-default-build-badges/passing.svg
|
||||
:target: https://codebuild.us-east-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiWm42eW80VzA2OXRTc2xIMXErZ1hlS1RpNnFCaDVMWENqSSsyU2x3dUpReEpCRUtaZGRmbklYaFN0anVEWW9NaGYvQ21PNk9tR25rZGtZMjNvR1ArbGdVPSIsIml2UGFyYW1ldGVyU3BlYyI6IjVXYjl3TWZnUVQ1MFZDQ0kiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master
|
||||
:alt: Build status of the master branch on amazon codebuild
|
||||
@@ -1,27 +0,0 @@
|
||||
# Slim Framework 3 Skeleton Application
|
||||
|
||||
Use this skeleton application to quickly setup and start working on a new Slim Framework 3 application. This application uses the latest Slim 3 with the PHP-View template renderer. It also uses the Monolog logger.
|
||||
|
||||
This skeleton application was built for Composer. This makes setting up a new Slim Framework application quick and easy.
|
||||
|
||||
## Install the Application
|
||||
|
||||
Run this command from the directory in which you want to install your new Slim Framework application.
|
||||
|
||||
php composer.phar create-project slim/slim-skeleton [my-app-name]
|
||||
|
||||
Replace `[my-app-name]` with the desired directory name for your new application. You'll want to:
|
||||
|
||||
* Point your virtual host document root to your new application's `public/` directory.
|
||||
* Ensure `logs/` is web writeable.
|
||||
|
||||
To run the application in development, you can run these commands
|
||||
|
||||
cd [my-app-name]
|
||||
php composer.phar start
|
||||
|
||||
Run this command in the application directory to run the test suite
|
||||
|
||||
php composer.phar test
|
||||
|
||||
That's it! Now go build something cool.
|
||||
34
api-payroll/README.rst
Normal file
34
api-payroll/README.rst
Normal file
@@ -0,0 +1,34 @@
|
||||
=======
|
||||
The api
|
||||
=======
|
||||
|
||||
.. contents::
|
||||
|
||||
About
|
||||
-------
|
||||
The project has been built with slim in the backed and jquery with bootstrap for the front, both of them share the public folder from which they can be accessed by the general public.
|
||||
|
||||
Auth
|
||||
------
|
||||
The system uses cookie based sessions which are handled by a midleware, have a time to live of 10 minutes and are refreshed each time a new request is made to the api, further more the contents of the session itself has been secured with openssl.
|
||||
|
||||
Database
|
||||
---------
|
||||
To connect to the database pdo is used, its configuration can be found at **src/settings.php** under the mysql section. The following settings are set as default:
|
||||
- **PDO::ATTR_EMULATE_PREPARES** Has been set to true in order to lower the strain on the database by processing the prepare statements on the server side, if cache performance is desired this option should be changed to false
|
||||
- **PDO::ATTR_ERRMODE** Uses **PDO::ERRMODE_EXCEPTION** which will return all mysql errors as exceptions to prevent further execution of the software
|
||||
- **PDO::ATTR_DEFAULT_FETCH_MODE** uses **PDO::FETCH_ASSOC** and as such the query ouput system wide is expected as an associative array
|
||||
|
||||
Data protection
|
||||
----------------
|
||||
| Encryption has been applied to sensitive data, passwords are protected with with bcrypt and it's configuration can be found in the settings.php file, by default a cost of 12 is used for the hashing as well as a 16 characters randomly generated string (128 bits) as an iv.
|
||||
|
||||
| For data that needs to be both read and written such as names AES in mode cbc with 256 block size has been used.
|
||||
|
||||
| The reason to have choosen AES is the desire to make the process of securing the data both secure and affordable since many hardware manufacturers already have architectures designed to improce the speed of AES.
|
||||
|
||||
| **Important note**: While in this project the encryption password has been saved into the settings.php file it's adviced that in a real use case it's stored more securely or else where entirely such as a key management service.
|
||||
|
||||
Error handling
|
||||
---------------
|
||||
Should an exception be encountered it'll be caught by a middleware that will form a new response body, returning it with a 500 http code and a json object containing the keys status set to error as well as a message key that will contain the exception that was raised.
|
||||
@@ -17,7 +17,8 @@
|
||||
"slim/php-view": "^2.0",
|
||||
"monolog/monolog": "^1.17",
|
||||
"respect/validation": "^1.1",
|
||||
"tuupola/cors-middleware": "^0.5.2"
|
||||
"tuupola/cors-middleware": "^0.5.2",
|
||||
"adbario/slim-secure-session-middleware": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": ">=4.8 < 6.0"
|
||||
|
||||
140
api-payroll/composer.lock
generated
140
api-payroll/composer.lock
generated
@@ -4,9 +4,96 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "93a9656f4e6eb0e25be1bad59ac6f487",
|
||||
"content-hash": "a3fc18885cc45d2733b77fa2081bdc72",
|
||||
"hash": "19bc193d641803843178e87bf0465afe",
|
||||
"content-hash": "9eb8d110ad374a60767f92d76018504a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adbario/php-dot-notation",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/adbario/php-dot-notation.git",
|
||||
"reference": "5e4b1fe29a8ae1140e370d520ed8b85dd5130a1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/adbario/php-dot-notation/zipball/5e4b1fe29a8ae1140e370d520ed8b85dd5130a1f",
|
||||
"reference": "5e4b1fe29a8ae1140e370d520ed8b85dd5130a1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Adbar\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Riku Särkinen",
|
||||
"email": "riku@adbar.io"
|
||||
}
|
||||
],
|
||||
"description": "PHP dot notation array access",
|
||||
"homepage": "https://github.com/adbario/php-dot-notation",
|
||||
"keywords": [
|
||||
"ArrayAccess",
|
||||
"dotnotation",
|
||||
"php"
|
||||
],
|
||||
"time": "2017-03-26 17:44:47"
|
||||
},
|
||||
{
|
||||
"name": "adbario/slim-secure-session-middleware",
|
||||
"version": "1.3.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/adbario/slim-secure-session-middleware.git",
|
||||
"reference": "f107191506b2c362f06f201f998891f315d6aaec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/adbario/slim-secure-session-middleware/zipball/f107191506b2c362f06f201f998891f315d6aaec",
|
||||
"reference": "f107191506b2c362f06f201f998891f315d6aaec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"adbario/php-dot-notation": "~1.2.0",
|
||||
"paragonie/random_compat": "^2.0",
|
||||
"php": ">=5.5",
|
||||
"slim/slim": "~3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Adbar\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Riku Särkinen",
|
||||
"email": "riku@adbar.io"
|
||||
}
|
||||
],
|
||||
"description": "Secure session middleware for Slim 3 framework",
|
||||
"homepage": "https://github.com/adbario/slim-secure-session-middleware",
|
||||
"keywords": [
|
||||
"middleware",
|
||||
"session",
|
||||
"slim"
|
||||
],
|
||||
"time": "2017-08-04 13:51:00"
|
||||
},
|
||||
{
|
||||
"name": "container-interop/container-interop",
|
||||
"version": "1.2.0",
|
||||
@@ -217,6 +304,55 @@
|
||||
],
|
||||
"time": "2018-02-13 20:26:39"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v2.0.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d",
|
||||
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/random.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"polyfill",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"time": "2018-07-04 16:31:37"
|
||||
},
|
||||
{
|
||||
"name": "pimple/pimple",
|
||||
"version": "v3.2.3",
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if(!isset($_SESSION['userName'])){
|
||||
header("Location: ./login.php");
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="../css/bootstrap.min.css">
|
||||
|
||||
@@ -40,10 +31,6 @@ if(!isset($_SESSION['userName'])){
|
||||
<li>
|
||||
<a href="#" data-nav_accion="registerWorkDays.php" ><span class="glyphicon glyphicon-tasks"></span> Management</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#" onclick="loadView();"><span class="glyphicon glyphicon-wrench"></span> Change password</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if(isset($_SESSION['userName'])){
|
||||
header("Location: ./landing.php");
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="../css/bootstrap.min.css">
|
||||
|
||||
|
||||
@@ -10,12 +10,20 @@
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysSearchEmployee">Employee</label>
|
||||
<label class="col-md-4 control-label" for="workDaysSearchEmployee">Search employee</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysSearchEmployee" name="workDaysSearchEmployee" type="text" class="form-control input-md">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysSearchByDate">Search by date</label>
|
||||
<div class="col-md-2">
|
||||
<input id="workDaysSearchByDate" name="workDaysSearchByDate" type="text" class="form-control input-md datepicker">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="registerWorkDaysEmployeeInfo" class="col-md-6">
|
||||
@@ -125,7 +133,7 @@
|
||||
</div>
|
||||
<div class="row col-md-offset-6">
|
||||
<div class="form-group">
|
||||
<a href="#" class="btn btn-lg btn-success " onclick="saveNewWorkDay();">Save</a>
|
||||
<a href="#" class="btn btn-lg btn-success " onclick="processSaveActionWorkDay();">Save</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,8 +11,6 @@ if (PHP_SAPI == 'cli-server') {
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
session_start();
|
||||
|
||||
// Instantiate the app
|
||||
$settings = require __DIR__ . '/../src/settings.php';
|
||||
$app = new \Slim\App($settings);
|
||||
|
||||
@@ -13,12 +13,15 @@ function logout() {
|
||||
window.location.replace(baseUrl + '/html/login.php');
|
||||
},
|
||||
error:function(x,e) {
|
||||
let responseText = $.parseJSON(x["responseText"]);
|
||||
|
||||
if (x.status==0) {
|
||||
$('#modalErrorInternetConnection').modal('show');
|
||||
} else if(x.status==404) {
|
||||
$('#modalError404').modal('show');
|
||||
} else if(x.status==500) {
|
||||
$('#modalError500').modal('show');
|
||||
$('#modalServerResponseError').modal('show');
|
||||
document.getElementById('modalResponseError').innerHTML = responseText['message'];
|
||||
} else if(e=='parsererror') {
|
||||
$('#modalErrorParsererror').modal('show');
|
||||
} else if(e=='timeout'){
|
||||
@@ -59,12 +62,15 @@ function loadView(requestedView){
|
||||
$("#newViewBody").hide().html(data).show('slow');
|
||||
},
|
||||
error:function(x,e) {
|
||||
let responseText = $.parseJSON(x["responseText"]);
|
||||
|
||||
if (x.status==0) {
|
||||
$('#modalErrorInternetConnection').modal('show');
|
||||
} else if(x.status==404) {
|
||||
$('#modalError404').modal('show');
|
||||
} else if(x.status==500) {
|
||||
$('#modalError500').modal('show');
|
||||
$('#modalServerResponseError').modal('show');
|
||||
document.getElementById('modalResponseError').innerHTML = responseText['message'];
|
||||
} else if(e=='parsererror') {
|
||||
$('#modalErrorParsererror').modal('show');
|
||||
} else if(e=='timeout'){
|
||||
|
||||
@@ -7,7 +7,7 @@ $(document).ready(function(){
|
||||
loadEmployeeTypesForWorkDays();
|
||||
|
||||
$('.datepicker').datepicker({
|
||||
format: "yyyy/mm/dd",
|
||||
format: "yyyy-mm-dd",
|
||||
autoclose: true
|
||||
});
|
||||
|
||||
@@ -213,6 +213,21 @@ function loadSalaryDetails(code){
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstraper for the save action
|
||||
*
|
||||
* If the search by date function has been used it's then assumed that the
|
||||
* desired action is to update else the function will attempt to create a new
|
||||
* record for the worked day
|
||||
*/
|
||||
function processSaveActionWorkDay(){
|
||||
if($('#workDaysSearchByDate').val() === ''){
|
||||
saveNewWorkDay();
|
||||
}else {
|
||||
updateNewWorkDay();
|
||||
}
|
||||
}
|
||||
|
||||
function saveNewWorkDay(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
@@ -252,4 +267,93 @@ function saveNewWorkDay(){
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateNewWorkDay(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
let parameters = {
|
||||
"code":$('#hidenEmployeeCodeForWorkDaysCode').val(),
|
||||
"idEmployeeTypePerformed":$('#workDaysEmployeePerformedRol').val(),
|
||||
"deliveries":$('#workDaysEmployeeDeliveries').val(),
|
||||
"date":$('#workDaysEmployeeWorkedDay').val(),
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/workday',
|
||||
type: 'PUT',
|
||||
dataType: 'json',
|
||||
data: parameters,
|
||||
success:function(data){
|
||||
$('#modalServerResponseSuccess').modal('show');
|
||||
document.getElementById('serverResponseSuccess').innerHTML = data['message'];
|
||||
loadSalaryDetails($('#hidenEmployeeCodeForWorkDaysCode').val());
|
||||
},
|
||||
error:function(x,e) {
|
||||
let responseText = $.parseJSON(x["responseText"]);
|
||||
|
||||
if (x.status==0) {
|
||||
$('#modalErrorInternetConnection').modal('show');
|
||||
} else if(x.status==404) {
|
||||
$('#modalError404').modal('show');
|
||||
} else if(x.status==500) {
|
||||
$('#modalServerResponseError').modal('show');
|
||||
document.getElementById('modalResponseError').innerHTML = responseText['message'];
|
||||
} else if(e=='parsererror') {
|
||||
$('#modalErrorParsererror').modal('show');
|
||||
} else if(e=='timeout'){
|
||||
$('#modalErrorTimeout').modal('show');
|
||||
} else {
|
||||
$('#modalErrorOther').modal('show');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If the search by date field is changed from its default empty status it'll
|
||||
* load the data of the given work day and enable the update mode
|
||||
*/
|
||||
$('#workDaysSearchByDate').on("change", function(data){
|
||||
let baseUrl = getbaseUrl();
|
||||
let date = $(this).val();
|
||||
let code = $('#hidenEmployeeCodeForWorkDaysCode').val();
|
||||
|
||||
// The employee hasn't been picked
|
||||
if (code === ''){
|
||||
$('#modalServerResponseError').modal('show');
|
||||
document.getElementById('modalResponseError').innerHTML = 'Please select an employee in the search form first.';
|
||||
return false; // Exits the function
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/salary/date/' + date + '/code/' + code,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
$('#workDaysEmployeeRol').val(data['idEmployeeType']);
|
||||
$('#workDaysEmployeeContractType').val(data['contractType']);
|
||||
$('#workDaysEmployeeWorkedDay').val(date);
|
||||
$('#workDaysEmployeeDeliveries').val(data['deliveries']);
|
||||
$('#workDaysEmployeePerformedRol').val(data['idEmployeeTypePerformed']);
|
||||
},
|
||||
error:function(x,e) {
|
||||
let responseText = $.parseJSON(x["responseText"]);
|
||||
|
||||
if (x.status==0) {
|
||||
$('#modalErrorInternetConnection').modal('show');
|
||||
} else if(x.status==404) {
|
||||
$('#modalError404').modal('show');
|
||||
} else if(x.status==500) {
|
||||
$('#modalServerResponseError').modal('show');
|
||||
document.getElementById('modalResponseError').innerHTML = responseText['message'];
|
||||
} else if(e=='parsererror') {
|
||||
$('#modalErrorParsererror').modal('show');
|
||||
} else if(e=='timeout'){
|
||||
$('#modalErrorTimeout').modal('show');
|
||||
} else {
|
||||
$('#modalErrorOther').modal('show');
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -9,24 +9,38 @@ class EmployeeApplication{
|
||||
private $cryptographyService;
|
||||
private $asserts;
|
||||
private $settings;
|
||||
private $session;
|
||||
|
||||
function __construct($employeeSettings, $mysql, $cryptographyService, $asserts){
|
||||
/**
|
||||
* EmployeeApplication constructor.
|
||||
* @param $employeeSettings
|
||||
* @param $mysql
|
||||
* @param $cryptographyService
|
||||
* @param $asserts
|
||||
* @param $session
|
||||
* @throws Exception
|
||||
*/
|
||||
function __construct($employeeSettings, $mysql, $cryptographyService, $asserts, $session){
|
||||
$this->settings = $employeeSettings;
|
||||
|
||||
$this->cryptographyService = $cryptographyService;
|
||||
$this->pdo = $mysql;
|
||||
$this->asserts = $asserts;
|
||||
$this->session = $session;
|
||||
|
||||
$this->databaseSelectQueryErrorMessage = 'There was an error inserting the record.';
|
||||
if(!$this->session->verifySession()){
|
||||
throw new Exception('A session is requited to access this resouerce.');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of the types of employee used in the system
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function listEmployeeTypes(){
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
id, name
|
||||
FROM
|
||||
employeeType
|
||||
@@ -37,7 +51,7 @@ class EmployeeApplication{
|
||||
$results = $stmt->fetchAll();
|
||||
|
||||
if(!$results){
|
||||
exit($this->databaseSelectQueryErrorMessage);
|
||||
throw new Exception("The types of employees could not be found..");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
@@ -63,11 +77,12 @@ class EmployeeApplication{
|
||||
$this->asserts->isNotEmpty($firstName, "The first name can't be empty.");
|
||||
$this->asserts->isNotEmpty($middleName, "The middle name can't be empty.");
|
||||
$this->asserts->isNotEmpty($birthDate, "The birth date can't be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($birthDate, "The birth date can't be in the future.");
|
||||
$this->asserts->isNotEmpty($email, "The email can't be empty.");
|
||||
$this->asserts->isNotEmpty($phone, "The phone number can't be empty.");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO persons (firstName, middleName, lastName, birthDate, email, phone)
|
||||
$stmt = $this->pdo->prepare("INSERT INTO persons (firstName, middleName, lastName, birthDate, email, phone)
|
||||
VALUES (:firstName, :middleName, :lastName, :birthDate, :email, :phone)");
|
||||
$this->pdo->beginTransaction();
|
||||
$stmt->execute(array(':firstName' => $firstName, ':middleName' => $middleName, ':lastName' => $lastName,
|
||||
@@ -81,7 +96,6 @@ class EmployeeApplication{
|
||||
} catch( PDOExecption $e ) {
|
||||
$this->pdo->rollback();
|
||||
throw new Exception('There was an error while trying to save a new person.');
|
||||
$this->logger->warning("There was an error in the EmployeeApplication->saveNewPerson caused by: $e ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +113,7 @@ class EmployeeApplication{
|
||||
$this->asserts->isNotEmpty($code, "The code can't be empty.");
|
||||
$this->asserts->isNotEmpty($contractType, "The contract type can't be empty.");
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO employees (idEmployeeType, idPerson, code, contractType)
|
||||
$stmt = $this->pdo->prepare("INSERT INTO employees (idEmployeeType, idPerson, code, contractType)
|
||||
VALUES (:idEmployeeType, :idPerson, :code, :contractType)");
|
||||
$this->pdo->beginTransaction();
|
||||
$stmt->execute(array(':idEmployeeType' => $idEmployeeType, ':idPerson' => $idPerson, ':code' => $code,
|
||||
@@ -140,6 +154,7 @@ class EmployeeApplication{
|
||||
|
||||
$birthDate = $requestData['birthDate'];
|
||||
$this->asserts->isNotEmpty($birthDate, "The birth date can't be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($birthDate, "The birth date can't be in the future.");
|
||||
|
||||
$email = $requestData['email'];
|
||||
$this->asserts->isNotEmpty($email, "The email can't be empty.");
|
||||
@@ -190,12 +205,13 @@ class EmployeeApplication{
|
||||
/**
|
||||
* @param $idEmployee
|
||||
* @return Integer
|
||||
* @throws Exception
|
||||
*/
|
||||
function getIdPersonByIdEmployee($idEmployee){
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
idPerson
|
||||
FROM
|
||||
employees
|
||||
@@ -206,7 +222,7 @@ class EmployeeApplication{
|
||||
$stmt->execute(array(':idEmployee' => $idEmployee));
|
||||
$results = $stmt->fetchAll();
|
||||
if(!$results){
|
||||
exit($this->databaseSelectQueryErrorMessage);
|
||||
throw new Exception("An error occurred while trying to find the person associated with the employee..");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
@@ -216,11 +232,12 @@ class EmployeeApplication{
|
||||
/**
|
||||
* @param $code string
|
||||
* @return integer
|
||||
* @throws Exception
|
||||
*/
|
||||
function getIdEmployeeTypeByCode($code){
|
||||
$this->asserts->isNotEmpty($code, "The code can't be empty.");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT COALESCE((SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT COALESCE((SELECT
|
||||
et.id
|
||||
FROM
|
||||
employees e
|
||||
@@ -232,7 +249,7 @@ class EmployeeApplication{
|
||||
$stmt->execute(array(':code' => $code));
|
||||
$results = $stmt->fetchAll();
|
||||
if(!$results){
|
||||
exit($this->databaseSelectQueryErrorMessage);
|
||||
throw new Exception("The employee could not be found.");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
@@ -242,12 +259,13 @@ class EmployeeApplication{
|
||||
/**
|
||||
* @param $code string
|
||||
* @return integer
|
||||
* @throws Exception
|
||||
*/
|
||||
function getIdEmployeeByCode($code){
|
||||
$this->asserts->isNotEmpty($code, "The code can't be empty.");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
id
|
||||
FROM
|
||||
employees
|
||||
@@ -259,7 +277,7 @@ class EmployeeApplication{
|
||||
$stmt->execute(array(':code' => $code));
|
||||
$results = $stmt->fetchAll();
|
||||
if(!$results){
|
||||
exit($this->databaseSelectQueryErrorMessage);
|
||||
throw new Exception("The employee could not be found.");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
@@ -271,11 +289,12 @@ class EmployeeApplication{
|
||||
*
|
||||
* @param $idEmployee
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function getEmployeeDataById($idEmployee){
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
e.id AS idEmployee,
|
||||
p.id AS idPerson,
|
||||
p.firstName,
|
||||
@@ -297,7 +316,7 @@ class EmployeeApplication{
|
||||
$stmt->execute(array(':idEmployee' => $idEmployee));
|
||||
$results = $stmt->fetchAll();
|
||||
if(!$results){
|
||||
exit($this->databaseSelectQueryErrorMessage);
|
||||
throw new Exception("The employee could not be found.");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
@@ -310,6 +329,7 @@ class EmployeeApplication{
|
||||
*
|
||||
* @param $idEmployee
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function proxyGetEmployeeDataById($idEmployee){
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
@@ -341,6 +361,7 @@ class EmployeeApplication{
|
||||
/**
|
||||
* @param $code string
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function getEmployeeDataByCode($code){
|
||||
$this->asserts->isNotEmpty($code, "The code can't be empty.");
|
||||
@@ -364,12 +385,13 @@ class EmployeeApplication{
|
||||
$this->asserts->isNotEmpty($firstName, "The first name can't be empty.");
|
||||
$this->asserts->isNotEmpty($middleName, "The middle name can't be empty.");
|
||||
$this->asserts->isNotEmpty($birthDate, "The birth date can't be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($birthDate, "The birth date can't be in the future.");
|
||||
$this->asserts->isNotEmpty($email, "The email can't be empty.");
|
||||
$this->asserts->isNotEmpty($phone, "The phone number can't be empty.");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("UPDATE persons
|
||||
SET
|
||||
$stmt = $this->pdo->prepare("UPDATE persons
|
||||
SET
|
||||
firstName = :firstName,
|
||||
middleName = :middleName,
|
||||
lastName = :lastName,
|
||||
@@ -402,8 +424,8 @@ class EmployeeApplication{
|
||||
$this->asserts->isNotEmpty($contractType, "The contract type can't be empty.");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("UPDATE employees
|
||||
SET
|
||||
$stmt = $this->pdo->prepare("UPDATE employees
|
||||
SET
|
||||
idEmployeeType = :idEmployeeType,
|
||||
code = :code,
|
||||
contractType = :contractType
|
||||
@@ -423,6 +445,7 @@ class EmployeeApplication{
|
||||
/**
|
||||
* @param $requestData object
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function updateEmployeeData($requestData){
|
||||
// Getting and validating the data
|
||||
@@ -446,6 +469,7 @@ class EmployeeApplication{
|
||||
|
||||
$birthDate = $requestData['birthDate'];
|
||||
$this->asserts->isNotEmpty($birthDate, "The birth date can't be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($birthDate, "The birth date can't be in the future.");
|
||||
|
||||
$email = $requestData['email'];
|
||||
$this->asserts->isNotEmpty($email, "The email can't be empty.");
|
||||
@@ -498,8 +522,8 @@ class EmployeeApplication{
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("UPDATE employees
|
||||
SET
|
||||
$stmt = $this->pdo->prepare("UPDATE employees
|
||||
SET
|
||||
status = 'INACTIVE'
|
||||
WHERE
|
||||
id = :idEmployee");
|
||||
@@ -520,9 +544,10 @@ class EmployeeApplication{
|
||||
* currently active in the system
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function getIdEmployeeFromAllActiveEmployees(){
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
id
|
||||
FROM
|
||||
employees
|
||||
@@ -533,7 +558,7 @@ class EmployeeApplication{
|
||||
$results = $stmt->fetchAll();
|
||||
|
||||
if(!$results){
|
||||
exit($this->databaseSelectQueryErrorMessage);
|
||||
throw new Exception("The employee could not be found.");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
@@ -545,6 +570,7 @@ class EmployeeApplication{
|
||||
* all currently active employees
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function listAllActiveEmployees(){
|
||||
$ids = $this->getIdEmployeeFromAllActiveEmployees();
|
||||
@@ -571,6 +597,7 @@ class EmployeeApplication{
|
||||
*
|
||||
* @param $partialName string
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function findEmployeeByFullName($partialName){
|
||||
$fullList = $this->listAllActiveEmployees();
|
||||
@@ -584,6 +611,39 @@ class EmployeeApplication{
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $idEmployee integer
|
||||
* @param $date date
|
||||
* @return integer
|
||||
* @throws Exception
|
||||
*/
|
||||
function findIdPaymentPerDayByEmployeeAndDate($idEmployee, $date){
|
||||
$this->asserts->isNotEmpty($idEmployee, "The code can't be empty.");
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
|
||||
$this->asserts->isNotEmpty($date, "The code can't be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($date, "The date can't be in the future.");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
id
|
||||
FROM
|
||||
paymentsPerEmployeePerDay
|
||||
WHERE
|
||||
date = :date AND idEmployee = :idEmployee),
|
||||
0) AS id;
|
||||
");
|
||||
|
||||
$stmt->execute(array(':date' => $date, ':idEmployee' => $idEmployee));
|
||||
$results = $stmt->fetchAll();
|
||||
if(!$results){
|
||||
throw new Exception("The registry of the worked day could not be found.");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
return $results[0]['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to determine if the date has already been saved as a worked day for
|
||||
* an employee, so long as it's currently active in the database
|
||||
@@ -598,9 +658,10 @@ class EmployeeApplication{
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
|
||||
$this->asserts->isNotEmpty($date, "The code can't be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($date, "The date can't be in the future.");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
paymentsPerEmployeePerDay
|
||||
@@ -612,7 +673,7 @@ class EmployeeApplication{
|
||||
$stmt->execute(array(':date' => $date, ':idEmployee' => $idEmployee));
|
||||
$results = $stmt->fetchAll();
|
||||
if(!$results){
|
||||
throw new Exception('Unable to determine the usage of date for the worked days.');
|
||||
throw new Exception('Unable to find the date of the worked days.');
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
@@ -633,13 +694,14 @@ class EmployeeApplication{
|
||||
function saveWorkedDay($idEmployee, $date, $baseAmount, $bonusTime, $deliveries){
|
||||
$this->asserts->isNotEmpty($idEmployee, "The idEmployee can't be empty.");
|
||||
$this->asserts->isNotEmpty($date, "The date can't be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($date, "The date can't be in the future.");
|
||||
$this->asserts->isNotEmpty($baseAmount, "The base payment per day can't be empty.");
|
||||
$this->asserts->isNotEmpty($bonusTime, "The bonus per worked hours can't be empty.");
|
||||
$this->asserts->isNotEmpty($deliveries, "The payment for deliveries can't be empty.");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO paymentsPerEmployeePerDay
|
||||
(idEmployee, date, baseAmount, bonusTime, deliveries)
|
||||
$stmt = $this->pdo->prepare("INSERT INTO paymentsPerEmployeePerDay
|
||||
(idEmployee, date, baseAmount, bonusTime, deliveries)
|
||||
VALUES (:idEmployee, :date, :baseAmount, :bonusTime, :deliveries)");
|
||||
$this->pdo->beginTransaction();
|
||||
$stmt->execute(array(':idEmployee' => $idEmployee, ':date' => $date, ':baseAmount' => $baseAmount,
|
||||
@@ -657,14 +719,77 @@ class EmployeeApplication{
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the data from the front end for the new worked day for a
|
||||
* employee and saves it
|
||||
* Changes the status in the detail table for the registry of worked days so
|
||||
* that it behaves as if deleted
|
||||
* @param $idEmployee
|
||||
* @param $date
|
||||
*/
|
||||
function dissablePaymentPerDayDetailsByEmployeeAndDate($idEmployee, $date){
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
$this->asserts->isNotEmpty($date, "The worked date cannot be empty.");
|
||||
$this->asserts->dateIsNotInTheFuture($date, "The date can't be in the future.");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("UPDATE paymentsPerEmployeePerDayDetail a
|
||||
INNER JOIN
|
||||
paymentsPerEmployeePerDay b ON b.id = a.idPaymentPerEmployeePerDay
|
||||
SET
|
||||
a.status = 'INACTIVE'
|
||||
WHERE
|
||||
b.date = :date AND b.idEmployee = :idEmployee");
|
||||
$this->pdo->beginTransaction();
|
||||
$stmt->execute(array(':date' => $date, ':idEmployee' => $idEmployee));
|
||||
$this->pdo->commit();
|
||||
|
||||
$stmt = null;
|
||||
} catch( PDOExecption $e ) {
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id integer - references paymentsPerEmployeePerDay
|
||||
* @param $baseAmount double
|
||||
* @param $bonusTime double
|
||||
* @param $deliveries double
|
||||
*/
|
||||
function updateWorkedDayPayments($id, $baseAmount, $bonusTime, $deliveries){
|
||||
$this->asserts->higherThanZero($id, "id payment must be higher than 0");
|
||||
$this->asserts->higherThanZero($baseAmount, "baseAmount must be higher than 0");
|
||||
$this->asserts->higherThanZero($bonusTime, "bonusTime must be higher than 0");
|
||||
$this->asserts->higherThanZero($deliveries, "deliveries must be higher than 0");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("UPDATE paymentsPerEmployeePerDay
|
||||
SET
|
||||
baseAmount = :baseAmount,
|
||||
bonusTime = :bonusTime,
|
||||
deliveries = :deliveries
|
||||
WHERE
|
||||
id = :id");
|
||||
$this->pdo->beginTransaction();
|
||||
$stmt->execute(array(':baseAmount' => $baseAmount, ':bonusTime' => $bonusTime, ':deliveries' => $deliveries,
|
||||
':id' => $id));
|
||||
$this->pdo->commit();
|
||||
|
||||
$stmt = null;
|
||||
} catch( PDOExecption $e ) {
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the data from the front end for the work day, this coulld be
|
||||
* for an update or a creation of a new registry
|
||||
*
|
||||
* The function will take the request body, validate it and pass the
|
||||
* processed data back to the wrapper method
|
||||
*
|
||||
* @param $requestData object
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function SaveNewWorkDay($requestData){
|
||||
function validateDataForStorageWorkDay($requestData){
|
||||
$code = $requestData['code'];
|
||||
$this->asserts->isNotEmpty($code, "The code can't be empty.");
|
||||
|
||||
@@ -683,10 +808,7 @@ class EmployeeApplication{
|
||||
|
||||
$date = $requestData['date'];
|
||||
$this->asserts->isNotEmpty($date, "The worked date cannot be empty.");
|
||||
|
||||
if($this->checkDateNotUsedWorkDayPerEmployee($idEmployee, $date) > 0){
|
||||
throw new Exception("This date has already been saved as a worked day.");
|
||||
}
|
||||
$this->asserts->dateIsNotInTheFuture($date, "The date can't be in the future.");
|
||||
|
||||
// The emplpoyee can't take that rol
|
||||
if($idEmployeeType != 3 and $idEmployeeType != $idEmployeeTypePerformed){
|
||||
@@ -714,11 +836,74 @@ class EmployeeApplication{
|
||||
$bonusTime = $perHourBonus * $this->settings['hoursPerWorkDay'];
|
||||
$bonusDeliveries = $deliveries * $this->settings['bonusPerDelivery'];
|
||||
|
||||
$this->saveWorkedDay($idEmployee, $date, $baseAmountPaid, $bonusTime, $bonusDeliveries);
|
||||
$contractType = $this->getContractTypeByEmployee($idEmployee);
|
||||
|
||||
$result = array(
|
||||
'idEmployee' => (int)$idEmployee,
|
||||
'date' => $date,
|
||||
'baseAmountPaid' => $baseAmountPaid,
|
||||
'bonusTime' => $bonusTime,
|
||||
'bonusDeliveries' => $bonusDeliveries,
|
||||
'contractType' => $contractType,
|
||||
'idEmployeeType' => (int)$idEmployeeType,
|
||||
'idEmployeeTypePerformed' => (int)$idEmployeeTypePerformed,
|
||||
'hoursPerWorkDay' => $this->settings['hoursPerWorkDay'],
|
||||
'paymentPerHour' => $this->settings['paymentPerHour'],
|
||||
'perHourBonus' => $perHourBonus,
|
||||
'deliveries' => $deliveries,
|
||||
'bonusPerDelivery' => $this->settings['bonusPerDelivery']
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function to store a new day that has been worked by an employee
|
||||
*
|
||||
* @param $requestData object
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function newWorkedDay($requestData){
|
||||
$data = $this->validateDataForStorageWorkDay($requestData);
|
||||
|
||||
if($this->checkDateNotUsedWorkDayPerEmployee($data['idEmployee'], $data['date']) > 0){
|
||||
throw new Exception("This date has already been saved as a worked day.");
|
||||
}
|
||||
|
||||
$idPaymentPerEmployeePerDay = $this->saveWorkedDay($data['idEmployee'], $data['date'],
|
||||
$data['baseAmountPaid'], $data['bonusTime'], $data['bonusDeliveries']);
|
||||
|
||||
$this->storeWorkDayDetails($idPaymentPerEmployeePerDay, $data['idEmployeeType'],
|
||||
$data['idEmployeeTypePerformed'], $data['contractType'], $data['hoursPerWorkDay'],
|
||||
$data['paymentPerHour'], $data['perHourBonus'], $data['deliveries'], $data['bonusPerDelivery']);
|
||||
|
||||
return array('status' => 'success', 'message' => 'The worked day has been saved.', 'data' => $requestData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper method to update a worked day for an employee
|
||||
*
|
||||
* @param $requestData object
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function updateWorkDay($requestData){
|
||||
$data = $this->validateDataForStorageWorkDay($requestData);
|
||||
|
||||
$this->dissablePaymentPerDayDetailsByEmployeeAndDate($data['idEmployee'], $data['date']);
|
||||
|
||||
$idPaymentPerEmployeePerDay = $this->findIdPaymentPerDayByEmployeeAndDate($data['idEmployee'], $data['date']);
|
||||
|
||||
$this->updateWorkedDayPayments($idPaymentPerEmployeePerDay, $data['baseAmountPaid'], $data['bonusTime'], $data['bonusDeliveries']);
|
||||
|
||||
$this->storeWorkDayDetails($idPaymentPerEmployeePerDay, $data['idEmployeeType'],
|
||||
$data['idEmployeeTypePerformed'], $data['contractType'], $data['hoursPerWorkDay'],
|
||||
$data['paymentPerHour'], $data['perHourBonus'], $data['deliveries'], $data['bonusPerDelivery']);
|
||||
|
||||
return array('status' => 'success', 'message' => 'The worked day has been updated.', 'data' => $requestData);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of days the employee has worked for a given year and month only
|
||||
* taking into accout the active ones
|
||||
@@ -735,13 +920,13 @@ class EmployeeApplication{
|
||||
$this->asserts->higherThanZero($year, "year must be higher than 0");
|
||||
$this->asserts->higherThanZero($month, "month must be higher than 0");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
COALESCE((SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
paymentsPerEmployeePerDay
|
||||
WHERE
|
||||
idEmployee = :idEmployee
|
||||
idEmployee = :idEmployee
|
||||
AND YEAR(date) = :year
|
||||
AND MONTH(date) = :month
|
||||
AND status = 'ACTIVE'),
|
||||
@@ -768,12 +953,12 @@ class EmployeeApplication{
|
||||
* @throws Exception
|
||||
*/
|
||||
function getDataWorkedDaysByEmployee($idEmployee, $year, $month){
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
baseAmount, bonusTime, deliveries
|
||||
FROM
|
||||
paymentsPerEmployeePerDay
|
||||
WHERE
|
||||
idEmployee = :idEmployee AND
|
||||
idEmployee = :idEmployee AND
|
||||
YEAR(date) = :year
|
||||
AND MONTH(date) = :month
|
||||
AND status = 'ACTIVE'");
|
||||
@@ -798,7 +983,7 @@ class EmployeeApplication{
|
||||
$this->asserts->isNotEmpty($idEmployee, "The code can't be empty.");
|
||||
$this->asserts->higherThanZero($idEmployee, "idEmployee must be higher than 0");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
contractType
|
||||
FROM
|
||||
employees
|
||||
@@ -815,6 +1000,121 @@ class EmployeeApplication{
|
||||
return $results[0]['contractType'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a backup of the information used to calculate the amount that the employee
|
||||
* will be paid for the submitted day
|
||||
*
|
||||
* @param $idPaymentPerEmployeePerDay integer
|
||||
* @param $idEmployeeType integer
|
||||
* @param $idEmployeeTypePerformed integer
|
||||
* @param $contractType string
|
||||
* @param $hoursWorked double
|
||||
* @param $paymentPerHour double
|
||||
* @param $bonusPerHour double
|
||||
* @param $deliveries integer
|
||||
* @param $paymentPerDelivery double
|
||||
* @return integer
|
||||
* @throws Exception
|
||||
*/
|
||||
function storeWorkDayDetails($idPaymentPerEmployeePerDay, $idEmployeeType, $idEmployeeTypePerformed, $contractType, $hoursWorked,
|
||||
$paymentPerHour, $bonusPerHour, $deliveries, $paymentPerDelivery){
|
||||
$this->asserts->isNotEmpty($idPaymentPerEmployeePerDay, "The idPaymentPerEmployeePerDay can't be empty.");
|
||||
$this->asserts->isNotEmpty($idEmployeeType, "The idEmployeeType can't be empty.");
|
||||
$this->asserts->isNotEmpty($idEmployeeTypePerformed, "The idEmployeeTypePerformed can't be empty.");
|
||||
$this->asserts->isNotEmpty($contractType, "The contractType can't be empty.");
|
||||
$this->asserts->isNotEmpty($hoursWorked, "The hoursWorked can't be empty.");
|
||||
$this->asserts->isNotEmpty($paymentPerHour, "The paymentPerHour can't be empty.");
|
||||
$this->asserts->isNotEmpty($bonusPerHour, "The bonusPerHour can't be empty.");
|
||||
$this->asserts->isNotEmpty($deliveries, "The deliveries can't be empty.");
|
||||
$this->asserts->isNotEmpty($paymentPerDelivery, "The paymentPerDelivery can't be empty.");
|
||||
|
||||
try {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO paymentsPerEmployeePerDayDetail
|
||||
(idPaymentPerEmployeePerDay, idEmployeeType, idEmployeeTypePerformed,
|
||||
contractType, hoursWorked, paymentPerHour, bonusPerHour, deliveries, paymentPerDelivery)
|
||||
VALUES
|
||||
(:idPaymentPerEmployeePerDay, :idEmployeeType, :idEmployeeTypePerformed,
|
||||
:contractType, :hoursWorked, :paymentPerHour, :bonusPerHour, :deliveries, :paymentPerDelivery)");
|
||||
$this->pdo->beginTransaction();
|
||||
$stmt->execute(array(':idPaymentPerEmployeePerDay' => $idPaymentPerEmployeePerDay,
|
||||
':idEmployeeType' => $idEmployeeType,
|
||||
':idEmployeeTypePerformed' => $idEmployeeTypePerformed,
|
||||
':contractType' => $contractType,
|
||||
':hoursWorked' => $hoursWorked,
|
||||
':paymentPerHour' => $paymentPerHour,
|
||||
':bonusPerHour' => $bonusPerHour,
|
||||
':deliveries' => $deliveries,
|
||||
':paymentPerDelivery' => $paymentPerDelivery)
|
||||
);
|
||||
$id = $this->pdo->lastInsertId();
|
||||
$this->pdo->commit();
|
||||
|
||||
return $id;
|
||||
|
||||
$stmt = null;
|
||||
} catch( PDOExecption $e ) {
|
||||
$this->pdo->rollback();
|
||||
throw new Exception("An error occured while saving the work day details.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date date
|
||||
* @param $code string
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function getDataWorkDayByDateAndCode($date, $code){
|
||||
$idEmployee = $this->getIdEmployeeByCode($code);
|
||||
$this->asserts->dateIsNotInTheFuture($date, "The date can't be in the future.");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT
|
||||
b.idPaymentPerEmployeePerDay,
|
||||
b.idEmployeeType,
|
||||
b.idEmployeeTypePerformed,
|
||||
b.contractType,
|
||||
b.hoursWorked,
|
||||
b.paymentPerHour,
|
||||
b.bonusPerHour,
|
||||
b.deliveries,
|
||||
b.paymentPerDelivery
|
||||
FROM
|
||||
paymentsPerEmployeePerDay a
|
||||
INNER JOIN
|
||||
paymentsPerEmployeePerDayDetail b ON b.idPaymentPerEmployeePerDay = a.id
|
||||
WHERE
|
||||
a.idEmployee = :idEmployee
|
||||
AND a.date = :date
|
||||
AND a.status = 'ACTIVE'
|
||||
AND b.status = 'ACTIVE'
|
||||
ORDER BY b.id DESC
|
||||
LIMIT 1");
|
||||
$stmt->execute(array(':idEmployee' => $idEmployee, ':date' => $date));
|
||||
|
||||
$results = $stmt->fetchAll();
|
||||
|
||||
if(!$results){
|
||||
throw new Exception("No data of the work day was found.");
|
||||
}
|
||||
$stmt = null;
|
||||
|
||||
foreach($results as $row){
|
||||
$data = array(
|
||||
'idPaymentPerEmployeePerDay' => (int)$row['idPaymentPerEmployeePerDay'],
|
||||
'idEmployeeType' => (int)$row['idEmployeeType'],
|
||||
'idEmployeeTypePerformed' => (int)$row['idEmployeeTypePerformed'],
|
||||
'contractType' => $row['contractType'],
|
||||
'hoursWorked' => (int)$row['hoursWorked'],
|
||||
'paymentPerHour' => (int)$row['paymentPerHour'],
|
||||
'bonusPerHour' => (int)$row['bonusPerHour'],
|
||||
'deliveries' => (int)$row['deliveries'],
|
||||
'paymentPerDelivery' => (int)$row['paymentPerDelivery']
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the worked days for an employee and determines how much they're
|
||||
* getting paid
|
||||
@@ -878,4 +1178,4 @@ class EmployeeApplication{
|
||||
return $salary;
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -4,23 +4,24 @@ namespace App\Application;
|
||||
use Exception;
|
||||
|
||||
class SessionApplication{
|
||||
private $session;
|
||||
private $pdo;
|
||||
private $cryptographyService;
|
||||
private $asserts;
|
||||
|
||||
function __construct($mysql, $cryptographyService, $asserts){
|
||||
function __construct($session, $mysql, $cryptographyService, $asserts){
|
||||
$this->session = $session;
|
||||
$this->cryptographyService = $cryptographyService;
|
||||
$this->pdo = $mysql;
|
||||
$this->asserts = $asserts;
|
||||
|
||||
$this->databaseSelectQueryErrorMessage = 'There was an error inserting the record.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
function verifySession(){
|
||||
return isset($_SESSION['userName']);
|
||||
$userName = $this->session->get('userName');
|
||||
return isset($userName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ class SessionApplication{
|
||||
$session['loggedIn'] = $this->verifySession();
|
||||
|
||||
if($this->verifySession()){
|
||||
$session['userName'] = $_SESSION['userName'];
|
||||
$session['userName'] = $this->session->get('userName');
|
||||
}
|
||||
|
||||
return $session;
|
||||
@@ -85,7 +86,12 @@ class SessionApplication{
|
||||
}
|
||||
|
||||
if($this->cryptographyService->decryptPassword($password, $storedPassword)){
|
||||
$_SESSION['userName'] = $userName;
|
||||
$this->session->set('userName', $userName);
|
||||
|
||||
if(!$this->verifySession()){
|
||||
throw new Exception('An error occurred while trying to create the session.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
@@ -118,9 +124,14 @@ class SessionApplication{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function destroySession(){
|
||||
session_destroy();
|
||||
$this->session->clear();
|
||||
|
||||
if($this->verifySession()){
|
||||
throw new Exception('An error occurred while trying to end the session.');
|
||||
}
|
||||
|
||||
return array('status' => 'success', 'message' => 'Successfully logged out.');
|
||||
}
|
||||
|
||||
@@ -18,6 +18,13 @@ $container['logger'] = function ($c) {
|
||||
return $logger;
|
||||
};
|
||||
|
||||
// Session handler
|
||||
$container['session'] = function ($container) {
|
||||
return new \Adbar\Session(
|
||||
$container->get('settings')['session']['namespace']
|
||||
);
|
||||
};
|
||||
|
||||
// Mysql connection
|
||||
$container['mysql'] = function ($c) {
|
||||
$mysqlSettings = $c->get('settings')['mysql'];
|
||||
@@ -60,7 +67,8 @@ $container['asserts'] = function ($c) {
|
||||
|
||||
// The session application
|
||||
$container['sessionApplication'] = function ($c) {
|
||||
$sessionApplication = new App\Application\SessionApplication($c['mysql'], $c['cryptographyService'], $c['asserts']);
|
||||
$sessionApplication = new App\Application\SessionApplication($c['session'], $c['mysql'],
|
||||
$c['cryptographyService'], $c['asserts']);
|
||||
return $sessionApplication;
|
||||
};
|
||||
|
||||
@@ -68,6 +76,6 @@ $container['sessionApplication'] = function ($c) {
|
||||
$container['employeeApplication'] = function ($c) {
|
||||
$employeeSettings = $c->get('settings')['employee'];
|
||||
$employeeApplication = new App\Application\EmployeeApplication($employeeSettings,
|
||||
$c['mysql'], $c['cryptographyService'], $c['asserts']);
|
||||
$c['mysql'], $c['cryptographyService'], $c['asserts'], $c['sessionApplication']);
|
||||
return $employeeApplication;
|
||||
};
|
||||
|
||||
@@ -19,4 +19,6 @@ $app->add(new \Tuupola\Middleware\Cors([
|
||||
->withHeader("Content-Type", "application/json")
|
||||
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
|
||||
}
|
||||
]));
|
||||
]));
|
||||
|
||||
$app->add(new \Adbar\SessionMiddleware($app->getContainer()->get('settings')['session']));
|
||||
@@ -102,7 +102,15 @@ $app->post('/api/employee/workday', function ($request, $response) {
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->SaveNewWorkDay($requestData)));
|
||||
->write(json_encode($this->employeeApplication->newWorkedDay($requestData)));
|
||||
});
|
||||
|
||||
$app->put('/api/employee/workday', function ($request, $response) {
|
||||
$requestData = $request->getParsedBody();
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->updateWorkDay($requestData)));
|
||||
});
|
||||
|
||||
$app->get('/api/employee/salary/{code}', function (Request $request, Response $response, array $args) {
|
||||
@@ -111,4 +119,13 @@ $app->get('/api/employee/salary/{code}', function (Request $request, Response $r
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->calculateSalaryByCode($code)));
|
||||
});
|
||||
});
|
||||
|
||||
$app->get('/api/employee/salary/date/{date}/code/{code}', function (Request $request, Response $response, array $args) {
|
||||
$date = $args['date'];
|
||||
$code = $args['code'];
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->getDataWorkDayByDateAndCode($date, $code)));
|
||||
});
|
||||
|
||||
@@ -95,5 +95,16 @@ class Asserts{
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
* @param $errorMessage
|
||||
* @throws Exception
|
||||
*/
|
||||
function dateIsNotInTheFuture($date, $errorMessage){
|
||||
if ($date > date('Y-m-d')){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -16,6 +16,35 @@ return [
|
||||
'level' => \Monolog\Logger::DEBUG,
|
||||
],
|
||||
|
||||
// Session handle settings
|
||||
'session' => [
|
||||
// Session cookie settings
|
||||
'name' => 'payroll-laziness-rocks',
|
||||
'lifetime' => 10,
|
||||
'path' => '/',
|
||||
'domain' => null,
|
||||
'secure' => false,
|
||||
'httponly' => true,
|
||||
|
||||
// Set session cookie path, domain and secure automatically
|
||||
'cookie_autoset' => true,
|
||||
|
||||
// Path where session files are stored, PHP's default path will be used if set null
|
||||
'save_path' => null,
|
||||
|
||||
// Session cache limiter
|
||||
'cache_limiter' => 'nocache',
|
||||
|
||||
// Extend session lifetime after each user activity
|
||||
'autorefresh' => true,
|
||||
|
||||
// Encrypt session data if string is set
|
||||
'encryption_key' => '7de431684c34cf2c898268cff71392f38c4175dde050c9ee69502b81571484e0',
|
||||
|
||||
// Session namespace
|
||||
'namespace' => 'slim'
|
||||
],
|
||||
|
||||
// Cryptography settings
|
||||
'cryptography' => [
|
||||
'encryptionAlgorithm' => 'AES-256-CBC',
|
||||
|
||||
@@ -20,7 +20,7 @@ class BaseTestCase extends \PHPUnit_Framework_TestCase
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $withMiddleware = true;
|
||||
protected $withMiddleware = false;
|
||||
|
||||
/**
|
||||
* Process the application given a request method and URI
|
||||
|
||||
22
database/README.rst
Normal file
22
database/README.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
================
|
||||
``The database``
|
||||
================
|
||||
|
||||
.. contents::
|
||||
|
||||
About the container
|
||||
--------------------
|
||||
|
||||
The database container is created in two stages to bypass some limitations with docker, firstly a new image will be created based on the Dockerfile which will be based on mysql 5.7 and it'll be passed the .sql scripts to initialize the database as well as a config file to configure the port that will be exposed.
|
||||
|
||||
Initializing
|
||||
-------------
|
||||
When the database is being created as an image it'll take all the scripts in the **/docker-entrypoint-initdb.d** directory and execute them in alphabetical order which will result in the database with its tables and initial data being created.
|
||||
|
||||
Accession
|
||||
----------
|
||||
The newly created container will have two users *root** and **sloth** both of which will have the password **12345678** and it'll be accessible in the port 3307
|
||||
|
||||
Persistence
|
||||
-----------
|
||||
A volume containing the data from **/var/lib/mysql** will be created to persist the information, once its created running the container build again will execute the starting scripts
|
||||
@@ -71,6 +71,7 @@ CREATE TABLE IF NOT EXISTS `employees` (
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP comment 'The date on which the registry was created',
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment 'The date of the last time the row was modified',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_contractType` (`contractType`),
|
||||
UNIQUE (`code`)
|
||||
);
|
||||
|
||||
@@ -89,3 +90,25 @@ CREATE TABLE IF NOT EXISTS `paymentsPerEmployeePerDay` (
|
||||
FOREIGN KEY (idEmployee) REFERENCES employees(id),
|
||||
UNIQUE (`idEmployee`, `date`, `status`)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS paymentsPerEmployeePerDayDetail;
|
||||
CREATE TABLE IF NOT EXISTS `paymentsPerEmployeePerDayDetail` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`idPaymentPerEmployeePerDay` INT UNSIGNED NOT NULL comment 'References the payment for the work day',
|
||||
`idEmployeeType` INT UNSIGNED NOT NULL comment 'The type of employee',
|
||||
`idEmployeeTypePerformed` INT UNSIGNED NOT NULL comment 'The employee working for the day as',
|
||||
`contractType` ENUM('INTERNO', 'EXTERNO') NOT NULL comment 'The type of contract',
|
||||
`hoursWorked` DOUBLE(10,2) NOT NULL DEFAULT 0.0 comment 'Hours worked for the day',
|
||||
`paymentPerHour` DOUBLE(10,2) NOT NULL DEFAULT 0.0 comment 'Payment per hour worked',
|
||||
`bonusPerHour` DOUBLE(10,2) NOT NULL DEFAULT 0.0 comment 'Bonus payment per hour worked',
|
||||
`deliveries` INT UNSIGNED NOT NULL DEFAULT 0 comment 'Total amount of deliveries for the day',
|
||||
`paymentPerDelivery` DOUBLE(10,2) NOT NULL DEFAULT 0.0 comment 'Payment for each delivery done',
|
||||
`status` ENUM('ACTIVE', 'INACTIVE') NOT NULL DEFAULT 'ACTIVE',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP comment 'The date on which the registry was created',
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment 'The date of the last time the row was modified',
|
||||
PRIMARY KEY (`id`),
|
||||
FOREIGN KEY (idPaymentPerEmployeePerDay) REFERENCES paymentsPerEmployeePerDay(id),
|
||||
FOREIGN KEY (idEmployeeType) REFERENCES employeeType(id),
|
||||
FOREIGN KEY (idEmployeeTypePerformed) REFERENCES employeeType(id),
|
||||
FOREIGN KEY (contractType) REFERENCES employees(contractType)
|
||||
);
|
||||
Reference in New Issue
Block a user