Compare commits
134 Commits
Author | SHA1 | Date | |
---|---|---|---|
1912508766 | |||
627da598fd | |||
7533bfa812 | |||
101ecf7c3c | |||
369bfc6a46 | |||
569f6f5872 | |||
df2c3951d4 | |||
956c22f73c | |||
dcac46b1fb | |||
6e1b70305b | |||
fa8422c8b0 | |||
4a22f4d307 | |||
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 | |||
6b22fc4694 | |||
caa31136f1 | |||
c97421df32 | |||
568c6d23a9 | |||
15a7ea5ef5 | |||
93099d316f | |||
e9f80a9989 | |||
6ce90bd318 | |||
69e3a31d52 | |||
1ee650e020 | |||
61eb809204 | |||
b98b4077b4 | |||
a8148bbb93 | |||
b0a19c6daa | |||
38d3466f16 | |||
68723e82fe | |||
6615e5471a | |||
49e114c18a | |||
e9effb7fcc | |||
a5a1656518 | |||
19d1f57240 | |||
99a020024e | |||
abd8168dbf | |||
79059fb91b | |||
1b2484fbf2 | |||
168a6a352e | |||
8185a42331 | |||
64a6ddafb5 | |||
54ef088fe0 | |||
dc45f02ad2 | |||
b829f09330 | |||
b03d152f11 | |||
0ec20a4c67 | |||
1605b1cea3 | |||
d4fadf08bc | |||
de84da4482 | |||
697d0fe769 | |||
cb5d30b4e0 | |||
6c91cad46c | |||
ccd9e10351 | |||
30f755c0b2 | |||
231e7fe2e6 | |||
f820a22a4f | |||
0cdfd21fa2 | |||
fee3db486f | |||
87181696e1 | |||
8b22c0db9c | |||
7feb3a6f5d | |||
18ee0ad333 | |||
e5d90bc32d | |||
1500aef977 | |||
112f78c1de | |||
08702b2cdf | |||
21013cf6ac | |||
4928481f72 | |||
![]() |
2d11218076 | ||
59a4d6e4a5 | |||
7cf083a612 | |||
d4135188bd | |||
6d29ac3f23 | |||
0449f202ef | |||
23868b60ee | |||
8a2d5b2afa | |||
7ceb2aad93 | |||
3902435690 | |||
f16e9fe72c | |||
d2b9163537 | |||
403541580d | |||
663ea7cc3e | |||
f93b41f14e | |||
24f1ce1ed7 | |||
b25346e3d5 | |||
f2237d9209 | |||
7fc9ca8c75 | |||
97fca1d7d3 | |||
058e19a49a | |||
bffeb6e9f4 | |||
9a3e876afe | |||
dadea504d0 | |||
1390427ec0 | |||
6c4e42e337 | |||
f4d1ce1ab7 | |||
ba307555f0 | |||
57ee1fbd72 | |||
63a7186464 | |||
4a8df33184 | |||
0deb89ed53 | |||
f441696b96 | |||
666b17c0dc | |||
30420975c4 | |||
2d3f52372c | |||
3d7a574396 | |||
90f11867a5 | |||
69b636620a | |||
304e3045c7 | |||
692f52b533 | |||
6b289695c7 |
71
README.rst
Normal file
71
README.rst
Normal file
@ -0,0 +1,71 @@
|
||||
=================
|
||||
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
|
5
api-payroll/.dockerignore
Normal file
5
api-payroll/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
Dockerfile
|
||||
README.md
|
||||
buildspec.yml
|
||||
CONTRIBUTING.md
|
||||
docker-compose.yml
|
2
api-payroll/.htaccess
Normal file
2
api-payroll/.htaccess
Normal file
@ -0,0 +1,2 @@
|
||||
Options -Indexes
|
||||
Deny from all
|
52
api-payroll/Dockerfile
Normal file
52
api-payroll/Dockerfile
Normal file
@ -0,0 +1,52 @@
|
||||
# Stage 1 - the build process
|
||||
FROM composer:1.7.1 as build-deps
|
||||
ENV COMPOSER_ALLOW_SUPERUSER 1
|
||||
|
||||
WORKDIR /root
|
||||
|
||||
COPY . .
|
||||
RUN composer install
|
||||
RUN composer test
|
||||
|
||||
# Stage 2 - the production environment
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt-get -y update && apt-get -y upgrade
|
||||
|
||||
RUN apt-get -y install apache2 \
|
||||
php7.0 \
|
||||
libapache2-mod-php7.0 \
|
||||
php7.0-cli \
|
||||
php7.0-common \
|
||||
php7.0-mbstring \
|
||||
php7.0-gd \
|
||||
php7.0-intl \
|
||||
php7.0-xml \
|
||||
php7.0-mysql \
|
||||
php7.0-mcrypt
|
||||
|
||||
# Enable apache mods
|
||||
RUN a2enmod php7.0
|
||||
RUN a2enmod rewrite
|
||||
|
||||
# Update the PHP.ini file, enable <? ?> tags and quieten logging
|
||||
RUN sed -i "s/short_open_tag = Off/short_open_tag = On/" /etc/php/7.0/apache2/php.ini
|
||||
RUN sed -i "s/error_reporting = .*$/error_reporting = E_ERROR | E_WARNING | E_PARSE/" /etc/php/7.0/apache2/php.ini
|
||||
|
||||
# Manually set up the apache environment variables
|
||||
ENV APACHE_RUN_USER www-data
|
||||
ENV APACHE_RUN_GROUP www-data
|
||||
ENV APACHE_LOG_DIR /var/log/apache2
|
||||
ENV APACHE_LOCK_DIR /var/lock/apache2
|
||||
|
||||
WORKDIR /var/www/site
|
||||
COPY --from=build-deps /root .
|
||||
|
||||
RUN touch logs/app.log
|
||||
RUN chmod 777 logs/app.log
|
||||
|
||||
# Update the default apache site
|
||||
ADD docker/apache-config.conf /etc/apache2/sites-enabled/000-default.conf
|
||||
|
||||
# By default start up apache in the foreground
|
||||
CMD /usr/sbin/apache2ctl -D FOREGROUND
|
@ -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.
|
@ -14,8 +14,9 @@ phases:
|
||||
- echo Entered the build phase...
|
||||
- echo Build started on `date`
|
||||
- composer test
|
||||
- sudo docker-compose up --build -d
|
||||
post_build:
|
||||
commands:
|
||||
- echo Entered the post_build phase...
|
||||
- sudo docker-compose down --rmi all -v
|
||||
- echo Build completed on `date`
|
||||
|
||||
|
@ -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",
|
||||
|
15
api-payroll/docker/apache-config.conf
Normal file
15
api-payroll/docker/apache-config.conf
Normal file
@ -0,0 +1,15 @@
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin me@mydomain.com
|
||||
DocumentRoot /var/www/site
|
||||
|
||||
<Directory /var/www/site/>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride All
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
</VirtualHost>
|
0
api-payroll/logs/app.log
Executable file
0
api-payroll/logs/app.log
Executable file
@ -1,3 +1,5 @@
|
||||
allow from all
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
|
7
api-payroll/public/css/bootstrap-datepicker.min.css
vendored
Normal file
7
api-payroll/public/css/bootstrap-datepicker.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6
api-payroll/public/css/bootstrap.min.css
vendored
Normal file
6
api-payroll/public/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
243
api-payroll/public/css/landing.css
Normal file
243
api-payroll/public/css/landing.css
Normal file
@ -0,0 +1,243 @@
|
||||
body {
|
||||
background-color: #e3e3e3;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.datepicker{
|
||||
color: black;
|
||||
}
|
||||
|
||||
#newViewBody{
|
||||
top:17%;
|
||||
left:1%;
|
||||
width:98%;
|
||||
float:left;
|
||||
}
|
||||
|
||||
#navigation_spot{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#footer {
|
||||
|
||||
}
|
||||
|
||||
#newViewBody .modal-body{
|
||||
color: #000;
|
||||
border:none;
|
||||
}
|
||||
|
||||
#newViewBody .panel-default{
|
||||
border: 3px solid #4A89A5;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#newViewBody .panel > .panel-heading {
|
||||
background-image: none;
|
||||
background-color: #4A89A5;
|
||||
color: white;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#newViewBody .btn-default{
|
||||
border: 2px solid #62655F;
|
||||
background: #F9DFAF;
|
||||
}
|
||||
|
||||
#newViewBody .btn-default:hover{
|
||||
border: 2px solid #62655F;
|
||||
background: #F9DFAF;
|
||||
}
|
||||
|
||||
#newViewBody .alert-success{
|
||||
background: #C6E97C;
|
||||
}
|
||||
|
||||
.modalHeaderError{
|
||||
background-color: #d9534f;
|
||||
}
|
||||
|
||||
|
||||
.modalHeaderSuccess{
|
||||
background-color: #5bc0de;
|
||||
}
|
||||
|
||||
|
||||
.metro{
|
||||
width:auto;
|
||||
height:auto;
|
||||
}
|
||||
.metroBox{
|
||||
margin: 0 auto;
|
||||
width:100%;
|
||||
padding: 0;
|
||||
height:auto;
|
||||
display:table;
|
||||
overflow: hidden;
|
||||
}
|
||||
.metroBox h3{
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.metroBox a{
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
}
|
||||
.boxElement{
|
||||
color: #fff;
|
||||
height:210px;
|
||||
width: 318px;
|
||||
float:left;
|
||||
margin:0 5px 5px 0;
|
||||
padding:0 1% 0 1%;
|
||||
}
|
||||
.boxElement:hover{
|
||||
color: #fff;
|
||||
background: #483D8B;
|
||||
text-decoration: none;
|
||||
}
|
||||
.amarelo{
|
||||
background:#f4c20d;
|
||||
}
|
||||
.vermelho{
|
||||
background:#da542d;
|
||||
}
|
||||
.azul{
|
||||
background:#009bad;
|
||||
}
|
||||
.azulFuerte{
|
||||
background:#5636b0;
|
||||
}
|
||||
.verde{
|
||||
background-color: #009f00;
|
||||
}
|
||||
.violet{
|
||||
background-color: #a400ab;
|
||||
}
|
||||
.iconPanel{
|
||||
font-size: 130px;
|
||||
}
|
||||
@media (max-width: 310px){
|
||||
.boxElement{
|
||||
width: 245px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 311px) and (max-width: 353px){
|
||||
.boxElement{
|
||||
width: 265px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 354px) and (max-width: 365px){
|
||||
.boxElement{
|
||||
width: 320px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 366px) and (max-width: 520px){
|
||||
.boxElement{
|
||||
width: 320px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 521px) and (max-width: 549px){
|
||||
.boxElement{
|
||||
width: 235px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 550px) and (max-width: 590px){
|
||||
.boxElement{
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 591px) and (max-width: 610px){
|
||||
.boxElement{
|
||||
width: 265px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 611px) and (max-width: 630px){
|
||||
.boxElement{
|
||||
width: 275px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 631px) and (max-width: 655px){
|
||||
.boxElement{
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 656px) and (max-width: 699px){
|
||||
.boxElement{
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 700px) and (max-width: 739px){
|
||||
.boxElement{
|
||||
width: 320px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 740px) and (max-width: 769px){
|
||||
.boxElement{
|
||||
width: 340px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 770px) and (max-width: 1230px){
|
||||
.boxElement{
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.formato_texto_custom{
|
||||
width: 97%;
|
||||
float: left;
|
||||
border: 1px solid #222;
|
||||
padding: 1%;
|
||||
margin: .5%;
|
||||
}
|
||||
|
||||
.cliente_muestra_producto{
|
||||
width: 96%;
|
||||
padding: 1%;
|
||||
margin: 0 1% 2% 1%;
|
||||
}
|
||||
|
||||
textarea{
|
||||
resize: none;
|
||||
}
|
||||
|
||||
#custom-bootstrap-menu.navbar-default .navbar-brand {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default {
|
||||
font-size: 14px;
|
||||
background-color: rgba(27, 35, 78, 1);
|
||||
border-width: 1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default .navbar-nav>li>a {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: rgba(27, 35, 78, 1);
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default .navbar-nav>li>a:hover,
|
||||
#custom-bootstrap-menu.navbar-default .navbar-nav>li>a:focus {
|
||||
color: rgba(106, 171, 232, 1);
|
||||
background-color: rgba(27, 35, 78, 1);
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default .navbar-nav>.active>a,
|
||||
#custom-bootstrap-menu.navbar-default .navbar-nav>.active>a:hover,
|
||||
#custom-bootstrap-menu.navbar-default .navbar-nav>.active>a:focus {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: rgba(27, 35, 78, 1);
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default .navbar-toggle {
|
||||
border-color: #1b234e;
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default .navbar-toggle:hover,
|
||||
#custom-bootstrap-menu.navbar-default .navbar-toggle:focus {
|
||||
background-color: #1b234e;
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default .navbar-toggle .icon-bar {
|
||||
background-color: #1b234e;
|
||||
}
|
||||
#custom-bootstrap-menu.navbar-default .navbar-toggle:hover .icon-bar,
|
||||
#custom-bootstrap-menu.navbar-default .navbar-toggle:focus .icon-bar {
|
||||
background-color: #1b234e;
|
||||
}
|
81
api-payroll/public/css/login.css
Normal file
81
api-payroll/public/css/login.css
Normal file
@ -0,0 +1,81 @@
|
||||
body {
|
||||
background: url(../imagenes/grey_background.jpg);
|
||||
background-size: cover;
|
||||
font-family: Montserrat;
|
||||
}
|
||||
@media only screen and (min-device-width: 480px) {
|
||||
body {
|
||||
background: url('../imagenes/grey_background.jpg') no-repeat fixed center center;
|
||||
}
|
||||
}
|
||||
.logo {
|
||||
width: 213px;
|
||||
height: 60px;
|
||||
margin: 30px auto;
|
||||
}
|
||||
.login-block {
|
||||
width: 320px;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
border-top: 5px solid #5bc0de;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.login-block h1 {
|
||||
text-align: center;
|
||||
color: #000;
|
||||
font-size: 18px;
|
||||
text-transform: uppercase;
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.login-block input {
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
font-family: Montserrat;
|
||||
padding: 0 20px 0 50px;
|
||||
outline: none;
|
||||
}
|
||||
.login-block input#user {
|
||||
background: #fff url('../imagenes/login_username.png') 20px top no-repeat;
|
||||
background-size: 16px 80px;
|
||||
}
|
||||
.login-block input#user:focus {
|
||||
background: #fff url('../imagenes/login_username.png') 20px bottom no-repeat;
|
||||
background-size: 16px 80px;
|
||||
}
|
||||
.login-block input#password {
|
||||
background: #fff url('../imagenes/login_password.png') 20px top no-repeat;
|
||||
background-size: 16px 80px;
|
||||
}
|
||||
.login-block input#password:focus {
|
||||
background: #fff url('../imagenes/login_password.png') 20px bottom no-repeat;
|
||||
background-size: 16px 80px;
|
||||
}
|
||||
.login-block input:active, .login-block input:focus {
|
||||
border: 1px solid #5bc0de;
|
||||
}
|
||||
.login-block #loginButon {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: #009bad;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #000;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
font-family: Montserrat;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#modalLoginErrorHeader{
|
||||
background-color: #d9534f;
|
||||
}
|
BIN
api-payroll/public/fonts/glyphicons-halflings-regular.ttf
Normal file
BIN
api-payroll/public/fonts/glyphicons-halflings-regular.ttf
Normal file
Binary file not shown.
BIN
api-payroll/public/fonts/glyphicons-halflings-regular.woff
Normal file
BIN
api-payroll/public/fonts/glyphicons-halflings-regular.woff
Normal file
Binary file not shown.
BIN
api-payroll/public/fonts/glyphicons-halflings-regular.woff2
Normal file
BIN
api-payroll/public/fonts/glyphicons-halflings-regular.woff2
Normal file
Binary file not shown.
113
api-payroll/public/html/editEmployee.php
Normal file
113
api-payroll/public/html/editEmployee.php
Normal file
@ -0,0 +1,113 @@
|
||||
<script src="../js/getBaseUrl.js"></script>
|
||||
<script src="../js/editEmployee.js"></script>
|
||||
|
||||
<form class="form-horizontal" id="editEmployee">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Edit employee</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="editEmploySearch">Employee</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmploySearch" name="editEmploySearch" type="text" class="form-control input-md">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="editEmployeeFirstName">First name</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmployeeFirstName" name="editEmployeeFirstName" 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="editEmployeeMiddleName">Middle name</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmployeeMiddleName" name="editEmployeeMiddleName" 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="editEmployeeLastName">Last name</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmployeeLastName" name="editEmployeeLastName" 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="editEmployeeBirthDate">Birth date</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmployeeBirthDate" name="editEmployeeBirthDate" type="text" class="form-control input-md datepicker">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" id="hidenEmployeeCode">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="editEmployeeCode">Code</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmployeeCode" name="editEmployeeCode" type="text" class="form-control input-md">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="editEmployeeEmail">Email</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmployeeEmail" name="editEmployeeEmail" 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="editEmployeePhone">Phone</label>
|
||||
<div class="col-md-5">
|
||||
<input id="editEmployeePhone" name="editEmployeePhone" 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="editEmployeeType">Rol</label>
|
||||
<div class="col-md-5">
|
||||
<select class="form-control input-md" name="editEmployeeType" id="editEmployeeType">
|
||||
<option>Employee type</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="editEmployeeContractType">Contract type</label>
|
||||
<div class="col-md-5">
|
||||
<select class="form-control input-md" name="editEmployeeContractType" id="editEmployeeContractType">
|
||||
<option>Contract type</option>
|
||||
<option value="INTERNO">interno</option>
|
||||
<option value="EXTERNO">Externo</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row col-md-offset-6">
|
||||
<div class="form-group">
|
||||
<a href="#" class="btn btn-lg btn-success " onclick="updateEmployee();">Update</a>
|
||||
<a href="#" class="btn btn-lg btn-primary " onclick="loadView(currentView);">Clear</a>
|
||||
<a href="#" class="btn btn-lg btn-danger " onclick="deleteEmployee();">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
219
api-payroll/public/html/landing.php
Normal file
219
api-payroll/public/html/landing.php
Normal file
@ -0,0 +1,219 @@
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="../css/bootstrap.min.css">
|
||||
|
||||
<!-- jQuery library -->
|
||||
<script src="../js/jquery.min.js"></script>
|
||||
|
||||
<!-- Latest compiled JavaScript -->
|
||||
<script src="../js/bootstrap.min.js"></script>
|
||||
|
||||
<body>
|
||||
<div class="col-md-12" id="navigation_spot">
|
||||
<!-- NavBar-->
|
||||
<div id="custom-bootstrap-menu" class="navbar navbar-default " role="navigation">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header"><a class="navbar-brand" href="#"></a>
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-menubuilder"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-menubuilder">
|
||||
<ul class="nav navbar-nav navbar-left" id="nevatation-options">
|
||||
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-user"></span> Employees<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-nav_accion="newEmployee.php"> New employee</a></li>
|
||||
<li><a href="#" data-nav_accion="editEmployee.php"> Modify employee</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="#" data-nav_accion="registerWorkDays.php" ><span class="glyphicon glyphicon-tasks"></span> Management</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="#" onclick="logout();"><span class="fa fa-fw fa-power-off"></span> logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12" id="newViewBody"></div>
|
||||
|
||||
<!--
|
||||
=================================================================================
|
||||
Modals for errors encountered by ajax
|
||||
=================================================================================
|
||||
-->
|
||||
|
||||
<div id="modalErrorInternetConnection" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderError">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">An error has occurred</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please verify your internet connection and try again.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalError404" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderError">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">An error has occurred</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Unable to find the requested url in the sever.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalError500" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderError">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">An error has occurred</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The server has encountered an internal error, please try again later.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalErrorParsererror" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderError">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">An error has occurred</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The response from the sever wasn't a proper JSON format</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalErrorTimeout" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderError">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">An error has occurred</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The request timeout, please try again or verify your connection.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalErrorOther" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderError">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">An error has occurred</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>An unknown error occurred.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
=================================================================================
|
||||
Generic response modals
|
||||
=================================================================================
|
||||
-->
|
||||
|
||||
<div id="modalServerResponseError" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderError">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">An error has occurred</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="modalResponseError"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalServerResponseSuccess" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="modal-header modalHeaderSuccess" id="modalHeaderServerResponseSuccess">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">Success</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="serverResponseSuccess"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="../js/getBaseUrl.js"></script>
|
||||
<script src="../js/landing.js"></script>
|
||||
<script src="../js/bootstrap-datepicker.min.js"></script>
|
||||
<script src="../js/typeahead.bundle.js"></script>
|
||||
|
||||
<link href="../css/bootstrap-datepicker.min.css" rel="stylesheet">
|
||||
<link href="../css/landing.css" rel="stylesheet">
|
45
api-payroll/public/html/login.php
Normal file
45
api-payroll/public/html/login.php
Normal file
@ -0,0 +1,45 @@
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="../css/bootstrap.min.css">
|
||||
|
||||
<!-- jQuery library -->
|
||||
<script src="../js/jquery.min.js"></script>
|
||||
|
||||
<!-- Latest compiled JavaScript -->
|
||||
<script src="../js/bootstrap.min.js"></script>
|
||||
|
||||
<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
|
||||
<div class="container">
|
||||
<div class="logo"></div>
|
||||
<div class="login-block">
|
||||
<form action="" method="post" name="Login_Form" class="login">
|
||||
<h1>Login</h1>
|
||||
<input type="text" value="" placeholder="User" id="userName" name="user" required="" autofocus=""/>
|
||||
<input type="password" value="" placeholder="Password" id="password" name="password" required=""/>
|
||||
<a href="#" class="btn btn-lg btn-warning btn-default" id="loginButon" name="login" value="Login" onclick="processLogin();">Login</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalLoginError" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header" id="modalLoginErrorHeader">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title"><center>Ha ocurrido un error</center></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="modalLoginErrorBody"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/login.js"></script>
|
||||
<script src="../js/getBaseUrl.js"></script>
|
||||
<link href="../css/login.css" rel="stylesheet">
|
94
api-payroll/public/html/newEmployee.php
Normal file
94
api-payroll/public/html/newEmployee.php
Normal file
@ -0,0 +1,94 @@
|
||||
<script src="../js/newEmployee.js"></script>
|
||||
<script src="../js/getBaseUrl.js"></script>
|
||||
|
||||
<form class="form-horizontal" id="newEmployeeForm">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">New employee</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="newEmployeeFirstName">First name</label>
|
||||
<div class="col-md-5">
|
||||
<input id="newEmployeeFirstName" name="newEmployeeFirstName" 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="newEmployeeMiddleName">Middle name</label>
|
||||
<div class="col-md-5">
|
||||
<input id="newEmployeeMiddleName" name="newEmployeeMiddleName" 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="newEmployeeLastName">Last name</label>
|
||||
<div class="col-md-5">
|
||||
<input id="newEmployeeLastName" name="newEmployeeLastName" 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="newEmployeeBirthDate">Birth date</label>
|
||||
<div class="col-md-5">
|
||||
<input id="newEmployeeBirthDate" name="newEmployeeBirthDate" type="text" class="form-control input-md datepicker">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="newEmployeeEmail">Email</label>
|
||||
<div class="col-md-5">
|
||||
<input id="newEmployeeEmail" name="newEmployeeEmail" 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="newEmployeePhone">Phone</label>
|
||||
<div class="col-md-5">
|
||||
<input id="newEmployeePhone" name="newEmployeePhone" 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="newEmployeeType">Rol</label>
|
||||
<div class="col-md-5">
|
||||
<select class="form-control input-md" name="newEmployeeType" id="newEmployeeType">
|
||||
<option>Employee type</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="NewEmpployyContractType">Contract type</label>
|
||||
<div class="col-md-5">
|
||||
<select class="form-control input-md" name="NewEmpployyContractType" id="NewEmpployyContractType">
|
||||
<option>Contract type</option>
|
||||
<option value="INTERNO">interno</option>
|
||||
<option value="EXTERNO">Externo</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row col-md-offset-6">
|
||||
<div class="form-group">
|
||||
<a href="#" class="btn btn-lg btn-success " onclick="saveNewEmployee();">Create</a>
|
||||
<a href="#" class="btn btn-lg btn-primary " onclick="loadView(currentView);">Clear</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
143
api-payroll/public/html/registerWorkDays.php
Normal file
143
api-payroll/public/html/registerWorkDays.php
Normal file
@ -0,0 +1,143 @@
|
||||
<script src="../js/getBaseUrl.js"></script>
|
||||
<script src="../js/registerWorkDays.js"></script>
|
||||
|
||||
<form class="form-horizontal" id="workDaysForm">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Managing work days</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<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">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeName">Name</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysEmployeeName" name="workDaysEmployeeName" type="text" class="form-control input-md" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeRol">Rol</label>
|
||||
<div class="col-md-5">
|
||||
<select class="form-control input-md" name="workDaysEmployeeRol" id="workDaysEmployeeRol" disabled>
|
||||
<option></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeContractType">Contract type</label>
|
||||
<div class="col-md-5">
|
||||
<select class="form-control input-md" name="workDaysEmployeeContractType" id="workDaysEmployeeContractType" disabled>
|
||||
<option>Contract type</option>
|
||||
<option value="INTERNO">interno</option>
|
||||
<option value="EXTERNO">Externo</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeWorkedDay">Date</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysEmployeeWorkedDay" name="workDaysEmployeeWorkedDay" type="text" class="form-control input-md datepicker">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeDeliveries">Deliveries</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysEmployeeDeliveries" name="workDaysEmployeeDeliveries" type="number" value="0" class="form-control input-md">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeePerformedRol">Performed rol</label>
|
||||
<div class="col-md-5">
|
||||
<select class="form-control input-md" name="workDaysEmployeePerformedRol" id="workDaysEmployeePerformedRol" disabled>
|
||||
<option>Employee type</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="registerWorkDaysEmployeeSalary" class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeSalaryRaw">Raw</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysEmployeeSalaryRaw" name="workDaysEmployeeSalaryRaw" type="text" class="form-control input-md" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeSalaryTaxes">Taxes</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysEmployeeSalaryTaxes" name="workDaysEmployeeSalaryTaxes" type="text" class="form-control input-md" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeSalaryFinal">Final</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysEmployeeSalaryFinal" name="workDaysEmployeeSalaryFinal" type="text" class="form-control input-md" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="workDaysEmployeeSalaryVouchers">Vouchers</label>
|
||||
<div class="col-md-5">
|
||||
<input id="workDaysEmployeeSalaryVouchers" name="workDaysEmployeeSalaryVouchers" type="text" class="form-control input-md" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row" id="hidenEmployeeCodeForWorkDays">
|
||||
<div class="form-group">
|
||||
<label class="col-md-4 control-label" for="hidenEmployeeCodeForWorkDaysCode">Code</label>
|
||||
<div class="col-md-5">
|
||||
<input id="hidenEmployeeCodeForWorkDaysCode" name="hidenEmployeeCodeForWorkDaysCode" type="text" class="form-control input-md">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row col-md-offset-6">
|
||||
<div class="form-group">
|
||||
<a href="#" class="btn btn-lg btn-success " onclick="processSaveActionWorkDay();">Save</a>
|
||||
<a href="#" class="btn btn-lg btn-primary " onclick="loadView(currentView);">Clear</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
BIN
api-payroll/public/imagenes/grey_background.jpg
Normal file
BIN
api-payroll/public/imagenes/grey_background.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
api-payroll/public/imagenes/login_password.png
Normal file
BIN
api-payroll/public/imagenes/login_password.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
api-payroll/public/imagenes/login_username.png
Normal file
BIN
api-payroll/public/imagenes/login_username.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
@ -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);
|
||||
|
8
api-payroll/public/js/bootstrap-datepicker.min.js
vendored
Normal file
8
api-payroll/public/js/bootstrap-datepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
api-payroll/public/js/bootstrap.min.js
vendored
Normal file
7
api-payroll/public/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
217
api-payroll/public/js/editEmployee.js
Normal file
217
api-payroll/public/js/editEmployee.js
Normal file
@ -0,0 +1,217 @@
|
||||
/**
|
||||
* Bootstrapping the starting actions for the module
|
||||
*/
|
||||
$(document).ready(function(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
loadEmployeeTypes();
|
||||
|
||||
$('.datepicker').datepicker({
|
||||
format: "yyyy/mm/dd",
|
||||
autoclose: true
|
||||
});
|
||||
|
||||
// Not to be edited
|
||||
$("#hidenEmployeeCode").hide();
|
||||
|
||||
// Setting up bloodhound typeahead
|
||||
let employeesList = new Bloodhound({
|
||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace("name"),
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
remote: {
|
||||
'cache': false,
|
||||
url: baseUrl + '/api/employee/find',
|
||||
|
||||
replace: function(url, uriEncodedQuery) {
|
||||
|
||||
return url + '/' + uriEncodedQuery
|
||||
|
||||
},
|
||||
wildcard: '%QUERY',
|
||||
filter: function (data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
employeesList.initialize();
|
||||
|
||||
$("#editEmploySearch").typeahead({
|
||||
hint: true,
|
||||
highlight: true,
|
||||
minLength: 3
|
||||
},
|
||||
{
|
||||
name: "result",
|
||||
displayKey: "fullName",
|
||||
source: employeesList.ttAdapter()
|
||||
}).bind("typeahead:selected", function(obj, datum, name) {
|
||||
$(this).data("id", datum.code);
|
||||
loadEmployeeData(datum.code);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Loads the the employee types into their select option
|
||||
*/
|
||||
function loadEmployeeTypes(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/types',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
$(data).each(function(i,v){
|
||||
$('#editEmployeeType').append(
|
||||
'<option value="' + v.id + '">'+ v.name + '</option>'
|
||||
);
|
||||
});
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the employee data by its employee code and loads it
|
||||
* into the form to be edited and updated
|
||||
*
|
||||
* @param code string
|
||||
*/
|
||||
function loadEmployeeData(code){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/code/' + code,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
$('#editEmployeeFirstName').val(data['firstName']);
|
||||
$('#editEmployeeMiddleName').val(data['middleName']);
|
||||
$('#editEmployeeLastName').val(data['lastName']);
|
||||
$('#editEmployeeBirthDate').val(data['birthDate']);
|
||||
$('#editEmployeeCode').val(data['code']);
|
||||
$('#editEmployeeEmail').val(data['email']);
|
||||
$('#editEmployeePhone').val(data['phone']);
|
||||
$('#editEmployeeType').val(data['idEmployeeType']);
|
||||
$('#editEmployeeContractType').val(data['contractType']);
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Will change the status of an employee to remove them from the
|
||||
* active employee list
|
||||
*/
|
||||
function deleteEmployee(){
|
||||
let baseUrl = getbaseUrl();
|
||||
let code = $('#editEmployeeCode').val();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/' + code,
|
||||
type: 'DELETE',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
$('#modalServerResponseSuccess').modal('show');
|
||||
document.getElementById('serverResponseSuccess').innerHTML = 'The employee ' + data['firstName'] + ' ' + data['middleName'] + ' ' + data['lastName'] + ' has been deleted.';
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function updateEmployee(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
let parameters = {
|
||||
"firstName":$('#editEmployeeFirstName').val(),
|
||||
"middleName":$('#editEmployeeMiddleName').val(),
|
||||
"lastName":$('#editEmployeeLastName').val(),
|
||||
"birthDate":$('#editEmployeeBirthDate').val(),
|
||||
"code":$('#editEmployeeCode').val(),
|
||||
"email":$('#editEmployeeEmail').val(),
|
||||
"phone":$('#editEmployeePhone').val(),
|
||||
"idEmployeeType":$('#editEmployeeType').val(),
|
||||
"contractType":$('#editEmployeeContractType').val()
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee',
|
||||
type: 'PUT',
|
||||
dataType: 'json',
|
||||
data: parameters,
|
||||
success:function(data){
|
||||
$('#modalServerResponseSuccess').modal('show');
|
||||
document.getElementById('serverResponseSuccess').innerHTML = 'The employee ' + data['fullName'] + ' has been updated.';
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
10
api-payroll/public/js/getBaseUrl.js
Normal file
10
api-payroll/public/js/getBaseUrl.js
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Returns the entry point url for the system, this url will be used
|
||||
* to access both the api and the static resources
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function getbaseUrl(){
|
||||
var url = window.location.href;
|
||||
return url.substring(0, url.indexOf('/html/'));
|
||||
}
|
2
api-payroll/public/js/jquery.min.js
vendored
Normal file
2
api-payroll/public/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
96
api-payroll/public/js/landing.js
Normal file
96
api-payroll/public/js/landing.js
Normal file
@ -0,0 +1,96 @@
|
||||
// will contain the current loaded view
|
||||
let currentView;
|
||||
|
||||
/**
|
||||
* Destorys the session for the current user and redirects
|
||||
* back to the login form
|
||||
*/
|
||||
function logout() {
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/session/logout',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
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) {
|
||||
$('#modalServerResponseError').modal('show');
|
||||
document.getElementById('modalResponseError').innerHTML = responseText['message'];
|
||||
} else if(e=='parsererror') {
|
||||
$('#modalErrorParsererror').modal('show');
|
||||
} else if(e=='timeout'){
|
||||
$('#modalErrorTimeout').modal('show');
|
||||
} else {
|
||||
$('#modal_error_otro').modal('show');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for loading elements from the navatation var, this functuion
|
||||
* will filter the junk clicks that have landed in a dropdown menu and pass
|
||||
* only the ones containing an action to the actual view loader
|
||||
*/
|
||||
$('#nevatation-options li a').click(function(){
|
||||
|
||||
let view = $(this).data('nav_accion');
|
||||
|
||||
if (view != "#" && view != undefined) {
|
||||
loadView(view);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Will fetch the html of the desired view and load it into the landing page
|
||||
*
|
||||
* @param requestedView string
|
||||
*/
|
||||
function loadView(requestedView){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/html/' + requestedView,
|
||||
type: 'get',
|
||||
success:function(data){
|
||||
currentView = 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) {
|
||||
$('#modalServerResponseError').modal('show');
|
||||
document.getElementById('modalResponseError').innerHTML = responseText['message'];
|
||||
} else if(e=='parsererror') {
|
||||
$('#modalErrorParsererror').modal('show');
|
||||
} else if(e=='timeout'){
|
||||
$('#modalErrorTimeout').modal('show');
|
||||
} else {
|
||||
$('#modal_error_otro').modal('show');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the last view that was accessed as a way of fully clearing and
|
||||
* resetting the values of the form
|
||||
*/
|
||||
function clearView(view){
|
||||
loadView(view);
|
||||
}
|
47
api-payroll/public/js/login.js
Normal file
47
api-payroll/public/js/login.js
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Maps the enter key to the login action
|
||||
*/
|
||||
$(document).keypress(function(e) {
|
||||
if(e.which == 13) {
|
||||
processLogin();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Takes the input from the username and password fields and send theem to the backend
|
||||
* to be validated
|
||||
*
|
||||
* The response from the api will contain a status that will determine if the login was
|
||||
* successful or not and a message that will contain feedback which can be used to
|
||||
* display errors to the user
|
||||
*/
|
||||
function processLogin() {
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
let parameters = {
|
||||
"userName":$('#userName').val(),
|
||||
"password":$('#password').val()
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/session/login',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: parameters,
|
||||
success:function(data){
|
||||
if(data["status"] == "success"){
|
||||
window.location.replace(baseUrl + '/html/landing.php');
|
||||
|
||||
}else if(data["status"] == "success" || (data["status"] === undefined)){
|
||||
$('#modalLoginError').modal('show');
|
||||
document.getElementById('modalLoginErrorBody').innerHTML = "The server didn't respond in time, please try again or refresh this page.";
|
||||
}
|
||||
},
|
||||
error:function(x) {
|
||||
if (x.status==500){
|
||||
$('#modalLoginError').modal('show');
|
||||
document.getElementById('modalLoginErrorBody').innerHTML = "The user or password didnt match, please try again";
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
94
api-payroll/public/js/newEmployee.js
Normal file
94
api-payroll/public/js/newEmployee.js
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Bootstrapping the starting actions for the module
|
||||
*/
|
||||
$(document).ready(function(){
|
||||
loadEmployeeTypes();
|
||||
|
||||
$('.datepicker').datepicker({
|
||||
format: "yyyy/mm/dd",
|
||||
autoclose: true
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Loads the the enmployee types into their select option
|
||||
*/
|
||||
function loadEmployeeTypes(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/types',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
$(data).each(function(i,v){
|
||||
$('#newEmployeeType').append(
|
||||
'<option value="' + v.id + '">'+ v.name + '</option>'
|
||||
);
|
||||
});
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function saveNewEmployee(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
let parameters = {
|
||||
"firstName":$('#newEmployeeFirstName').val(),
|
||||
"middleName":$('#newEmployeeMiddleName').val(),
|
||||
"lastName":$('#newEmployeeLastName').val(),
|
||||
"birthDate":$('#newEmployeeBirthDate').val(),
|
||||
"email":$('#newEmployeeEmail').val(),
|
||||
"phone":$('#newEmployeePhone').val(),
|
||||
"idEmployeeType":$('#newEmployeeType').val(),
|
||||
"contractType":$('#NewEmpployyContractType').val()
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: parameters,
|
||||
success:function(data){
|
||||
$('#modalServerResponseSuccess').modal('show');
|
||||
document.getElementById('serverResponseSuccess').innerHTML = 'The employee ' + data['fullName'] + ' has been created with the code ' + data['employeeCode'];
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
359
api-payroll/public/js/registerWorkDays.js
Normal file
359
api-payroll/public/js/registerWorkDays.js
Normal file
@ -0,0 +1,359 @@
|
||||
/**
|
||||
* Bootstrapping the starting actions for the module
|
||||
*/
|
||||
$(document).ready(function(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
loadEmployeeTypesForWorkDays();
|
||||
|
||||
$('.datepicker').datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
autoclose: true
|
||||
});
|
||||
|
||||
// Not to be edited
|
||||
$("#hidenEmployeeCodeForWorkDays").hide();
|
||||
|
||||
// Setting up bloodhound typeahead
|
||||
let employeesList = new Bloodhound({
|
||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace("name"),
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
remote: {
|
||||
'cache': false,
|
||||
url: baseUrl + '/api/employee/find',
|
||||
|
||||
replace: function(url, uriEncodedQuery) {
|
||||
|
||||
return url + '/' + uriEncodedQuery
|
||||
|
||||
},
|
||||
wildcard: '%QUERY',
|
||||
filter: function (data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
employeesList.initialize();
|
||||
|
||||
$("#workDaysSearchEmployee").typeahead({
|
||||
hint: true,
|
||||
highlight: true,
|
||||
minLength: 3
|
||||
},
|
||||
{
|
||||
name: "result",
|
||||
displayKey: "fullName",
|
||||
source: employeesList.ttAdapter()
|
||||
}).bind("typeahead:selected", function(obj, datum, name) {
|
||||
$(this).data("id", datum.code);
|
||||
|
||||
loadEmployeeDataForWorkDays(datum.code);
|
||||
validateEmployeeCanDoOtherRoles(datum.code);
|
||||
loadSalaryDetails(datum.code);
|
||||
$('#hidenEmployeeCodeForWorkDaysCode').val(datum.code); // For future reference
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Loads the the employee types into their select option
|
||||
*/
|
||||
function loadEmployeeTypesForWorkDays(){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/types',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
$(data).each(function(i,v){
|
||||
$('#workDaysEmployeeRol').append(
|
||||
'<option value="' + v.id + '">'+ v.name + '</option>'
|
||||
);
|
||||
|
||||
$('#workDaysEmployeePerformedRol').append(
|
||||
'<option value="' + v.id + '">'+ v.name + '</option>'
|
||||
);
|
||||
});
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the employee data by its employee code and loads it
|
||||
* into the form to be edited and saved
|
||||
*
|
||||
* @param code string
|
||||
*/
|
||||
function loadEmployeeDataForWorkDays(code){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/code/' + code,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
let fullName = data['firstName'] + ' ' + data['middleName'] + ' ' + data['lastName'];
|
||||
|
||||
$('#workDaysEmployeeName').val(fullName);
|
||||
$('#workDaysEmployeeRol').val(data['idEmployeeType']);
|
||||
$('#workDaysEmployeePerformedRol').val(data['idEmployeeType']);
|
||||
$('#workDaysEmployeeContractType').val(data['contractType']);
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the employee code determines their type to decide if
|
||||
* they should be able to cover for other roles or not
|
||||
*
|
||||
* @param code string
|
||||
*/
|
||||
function validateEmployeeCanDoOtherRoles(code){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/type/' + code,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
if(data == 3){
|
||||
$("#workDaysEmployeePerformedRol").prop('disabled', false);
|
||||
}
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function loadSalaryDetails(code){
|
||||
let baseUrl = getbaseUrl();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/salary/' + code,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success:function(data){
|
||||
$('#workDaysEmployeeSalaryRaw').val(data['raw']);
|
||||
$('#workDaysEmployeeSalaryTaxes').val(data['taxes']);
|
||||
$('#workDaysEmployeeSalaryFinal').val(data['real']);
|
||||
$('#workDaysEmployeeSalaryVouchers').val(data['vouchers']);
|
||||
},
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
let parameters = {
|
||||
"code":$('#hidenEmployeeCodeForWorkDaysCode').val(),
|
||||
"idEmployeeTypePerformed":$('#workDaysEmployeePerformedRol').val(),
|
||||
"deliveries":$('#workDaysEmployeeDeliveries').val(),
|
||||
"date":$('#workDaysEmployeeWorkedDay').val(),
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/api/employee/workday',
|
||||
type: 'POST',
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
2451
api-payroll/public/js/typeahead.bundle.js
Normal file
2451
api-payroll/public/js/typeahead.bundle.js
Normal file
File diff suppressed because it is too large
Load Diff
1195
api-payroll/src/application/EmployeeApplication.php
Normal file
1195
api-payroll/src/application/EmployeeApplication.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
@ -41,15 +42,18 @@ class SessionApplication{
|
||||
/**
|
||||
* @param $userName string
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
function getPassword($userName){
|
||||
$this->asserts->userName($userName);
|
||||
$this->asserts->isNotEmpty($userName, "The username can't be empty");
|
||||
$this->asserts->isString($userName, "The username must be a string.");
|
||||
$this->asserts->betweenLength($userName, 1, 50, "The username must have a length between 1 and 50 characters.");
|
||||
|
||||
$stmt = $this->pdo->prepare("SELECT password FROM users WHERE name = :userName");
|
||||
$stmt->execute(array(':userName' => $userName));
|
||||
$results = $stmt->fetchAll();
|
||||
if(!$results){
|
||||
exit($this->databaseSelectQueryErrorMessage);
|
||||
throw new Exception('The user or password didnt match, please try again.');
|
||||
}
|
||||
$stmt = null;
|
||||
return $results[0]['password'];
|
||||
@ -62,14 +66,18 @@ class SessionApplication{
|
||||
* @throws Exception
|
||||
*/
|
||||
function newSession($userName, $password){
|
||||
$this->asserts->userName($userName);
|
||||
$this->asserts->password($password);
|
||||
$this->asserts->isNotEmpty($userName, "The username can't be empty");
|
||||
$this->asserts->isString($userName, "The username must be a string.");
|
||||
$this->asserts->betweenLength($userName, 1, 50, "The username must have a length between 1 and 50 characters.");
|
||||
$this->asserts->isNotEmpty($password, "The password can't be empty");
|
||||
$this->asserts->isString($password, "The password must be a string.");
|
||||
$this->asserts->betweenLength($password, 1, 50, "The password must have a length between 1 and 50 characters.");
|
||||
|
||||
$storedPassword = $this->getPassword($userName);
|
||||
|
||||
// If the credentials don't match anything in the the records
|
||||
if(!isset($storedPassword)){
|
||||
throw new Exception('The user or password didnt match, please try again.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Already has a session
|
||||
@ -78,21 +86,54 @@ 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{
|
||||
return false;
|
||||
throw new Exception('The user or password didnt match, please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @param $userName
|
||||
* @param $password
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function login($userName, $password){
|
||||
$this->asserts->isNotEmpty($userName, "The username can't be empty");
|
||||
$this->asserts->isString($userName, "The username must be a string.");
|
||||
$this->asserts->betweenLength($userName, 1, 50, "The username must have a length between 1 and 50 characters.");
|
||||
$this->asserts->isNotEmpty($password, "The password can't be empty");
|
||||
$this->asserts->isString($password, "The password must be a string.");
|
||||
$this->asserts->betweenLength($password, 1, 50, "The password must have a length between 1 and 50 characters.");
|
||||
|
||||
|
||||
if($this->newSession($userName, $password)){
|
||||
return array('status' => 'success', 'message' => 'Logged in successfully.');
|
||||
}
|
||||
else{
|
||||
throw new Exception('The user or password didnt match, please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
function destroySession(){
|
||||
session_destroy();
|
||||
$this->session->clear();
|
||||
|
||||
return "Sucessfully logged out.";
|
||||
if($this->verifySession()){
|
||||
throw new Exception('An error occurred while trying to end the session.');
|
||||
}
|
||||
|
||||
return array('status' => 'success', 'message' => 'Successfully logged out.');
|
||||
}
|
||||
}
|
||||
?>
|
@ -18,12 +18,20 @@ $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'];
|
||||
|
||||
// The database parameters
|
||||
$host = $mysqlSettings['host'];
|
||||
$port = $mysqlSettings['port'];
|
||||
$database = $mysqlSettings['database'];
|
||||
$user = $mysqlSettings['user'];
|
||||
$password = $mysqlSettings['password'];
|
||||
@ -34,7 +42,7 @@ $container['mysql'] = function ($c) {
|
||||
$databaseConnectionErrorMessage = $mysqlSettings['databaseConnectionErrorMessage'];
|
||||
|
||||
// Initiate the connection
|
||||
$dsn = "mysql:host=$host;dbname=$database;charset=$charset";
|
||||
$dsn = "mysql:host=$host;port=$port;dbname=$database;charset=$charset";
|
||||
try {
|
||||
$pdo = new PDO($dsn, $user, $password, $pdoConnectionOptions);
|
||||
} catch (Exception $e) {
|
||||
@ -59,6 +67,15 @@ $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;
|
||||
};
|
||||
|
||||
// The employee application
|
||||
$container['employeeApplication'] = function ($c) {
|
||||
$employeeSettings = $c->get('settings')['employee'];
|
||||
$employeeApplication = new App\Application\EmployeeApplication($employeeSettings,
|
||||
$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']));
|
@ -20,17 +20,112 @@ $app->get('/api/session', function (Request $request, Response $response, array
|
||||
});
|
||||
|
||||
$app->post('/api/session/login', function ($request, $response) {
|
||||
$RequestData = $request->getParsedBody();
|
||||
$requestData = $request->getParsedBody();
|
||||
|
||||
$data = $this->sessionApplication->newSession($RequestData['userName'], $RequestData['password']);
|
||||
$data = $this->sessionApplication->login($requestData['userName'], $requestData['password']);
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($data));
|
||||
});
|
||||
|
||||
$app->post('/api/session/logout', function (Request $request, Response $response, array $args) {
|
||||
$app->get('/api/session/logout', function (Request $request, Response $response, array $args) {
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->sessionApplication->destroySession()));
|
||||
});
|
||||
});
|
||||
|
||||
$app->get('/api/employee/types', function (Request $request, Response $response, array $args) {
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->listEmployeeTypes()));
|
||||
});
|
||||
|
||||
$app->get('/api/employee/find/{partialName}', function (Request $request, Response $response, array $args) {
|
||||
$partialName = $args['partialName'];
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->findEmployeeByFullName($partialName)));
|
||||
});
|
||||
|
||||
$app->post('/api/employee', function ($request, $response) {
|
||||
$requestData = $request->getParsedBody();
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->saveNewEmployee($requestData)));
|
||||
});
|
||||
|
||||
$app->put('/api/employee', function ($request, $response) {
|
||||
$requestData = $request->getParsedBody();
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->updateEmployeeData($requestData)));
|
||||
});
|
||||
|
||||
$app->DELETE('/api/employee/{code}', function (Request $request, Response $response, array $args) {
|
||||
$code = $args['code'];
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->disableEmployeeRecord($code)));
|
||||
});
|
||||
|
||||
$app->get('/api/employee/type/{code}', function (Request $request, Response $response, array $args) {
|
||||
$code = $args['code'];
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->getIdEmployeeTypeByCode($code)));
|
||||
});
|
||||
|
||||
$app->get('/api/employee/id/{idEmployee}', function (Request $request, Response $response, array $args) {
|
||||
$idEmployee = $args['idEmployee'];
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->proxyGetEmployeeDataById($idEmployee)));
|
||||
});
|
||||
|
||||
$app->get('/api/employee/code/{code}', function (Request $request, Response $response, array $args) {
|
||||
$code = $args['code'];
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->write(json_encode($this->employeeApplication->getEmployeeDataByCode($code)));
|
||||
});
|
||||
|
||||
$app->post('/api/employee/workday', function ($request, $response) {
|
||||
$requestData = $request->getParsedBody();
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->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) {
|
||||
$code = $args['code'];
|
||||
|
||||
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)));
|
||||
});
|
||||
|
@ -6,78 +6,104 @@ use Respect\Validation\Validator as v;
|
||||
|
||||
class Asserts{
|
||||
/**
|
||||
* @param $string
|
||||
* @param $string string
|
||||
* @param $errorMessage string
|
||||
* @throws Exception
|
||||
*/
|
||||
function userName($string){
|
||||
$validateFirstName = v::stringType()->notEmpty()->length(1, 50)->validate($string);
|
||||
function isString($string, $errorMessage){
|
||||
$validation = v::stringType()->validate($string);
|
||||
|
||||
if(!$validateFirstName){
|
||||
throw new Exception('The user name must be a string between 1 and 50 characters');
|
||||
if(!$validation){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @param $string string
|
||||
* @param $errorMessage string
|
||||
* @throws Exception
|
||||
*/
|
||||
function password($string){
|
||||
$validateFirstName = v::stringType()->notEmpty()->length(1, 50)->validate($string);
|
||||
function isNotEmpty($string, $errorMessage){
|
||||
$validation = v::notEmpty()->validate($string);
|
||||
|
||||
if(!$validateFirstName){
|
||||
throw new Exception('The password must be a string between 1 and 50 characters');
|
||||
if(!$validation){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @param $string string
|
||||
* @param $min integer
|
||||
* @param $max integer
|
||||
* @param $errorMessage string
|
||||
* @throws Exception
|
||||
*/
|
||||
function firstName($string){
|
||||
$validateFirstName = v::stringType()->notEmpty()->length(1, 100)->validate($string);
|
||||
function betweenLength($string, $min, $max, $errorMessage){
|
||||
$validation = v::length($min, $max)->validate($string);
|
||||
|
||||
if(!$validateFirstName){
|
||||
throw new Exception('The first name must be a string between 1 and 100 characters');
|
||||
if(!$validation){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @param $number integer
|
||||
* @param $errorMessage string
|
||||
* @throws Exception
|
||||
*/
|
||||
function middleName($string){
|
||||
if(!v::stringType()->notEmpty()->length(1, 100)->validate($string)){
|
||||
throw new Exception('The middle name must be a string between 1 and 100 characters');
|
||||
function higherThanZero($number, $errorMessage){
|
||||
if($number <= 0){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* Compares a string against a regex to determine if it's an email
|
||||
*
|
||||
* @param $string string
|
||||
* @param $errorMessage string
|
||||
* @throws Exception
|
||||
*/
|
||||
function birthDate($string){
|
||||
if(!v::date('Y-m-d')->notEmpty()->validate($string)){
|
||||
throw new Exception('The birth date must be in the yyyy-mm-dd format');
|
||||
function isEmail($string, $errorMessage){
|
||||
if(!filter_var($string, FILTER_VALIDATE_EMAIL)){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @param $string string
|
||||
* @param $array array
|
||||
* @param $errorMessage string
|
||||
* @throws Exception
|
||||
*/
|
||||
function email($string){
|
||||
if(!v::stringType()->notEmpty()->length(1, 100)->validate($string)){
|
||||
throw new Exception('The email must be a string between 1 and 100 characters');
|
||||
function existInArray($string, $array, $errorMessage){
|
||||
if(!in_array($string, $array)){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* Compares two dates to dertermine if they have the same month
|
||||
*
|
||||
* @param $firstDate date
|
||||
* @param $secondDate date
|
||||
* @param $errorMessage string
|
||||
* @throws Exception
|
||||
*/
|
||||
function phone($string){
|
||||
if(!v::digit()->notEmpty()->length(10, 10)->validate($string)){
|
||||
throw new Exception('The phone must be a numeric value of 10 digits');
|
||||
function datesHaveSameMonth($firstDate, $secondDate, $errorMessage){
|
||||
if (date("m",strtotime($firstDate)) != date("m",strtotime($secondDate))){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
* @param $errorMessage
|
||||
* @throws Exception
|
||||
*/
|
||||
function dateIsNotInTheFuture($date, $errorMessage){
|
||||
if ($date > date('Y-m-d')){
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,4 +85,18 @@ class CryptographyService{
|
||||
function decryptPassword($plainPassword, $encryptedPassword) {
|
||||
return password_verify($plainPassword, $encryptedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a psudo random string using openssl
|
||||
*
|
||||
* @param $length integer
|
||||
* @return string
|
||||
*/
|
||||
function pseudoRandomStringOpenssl($length){
|
||||
|
||||
$string = openssl_random_pseudo_bytes($length);
|
||||
$string = bin2hex($string);
|
||||
|
||||
return substr($string, 0, $length);
|
||||
}
|
||||
}
|
@ -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',
|
||||
@ -26,7 +55,8 @@ return [
|
||||
|
||||
// Datanase settings
|
||||
'mysql' => [
|
||||
'host' => 'localhost',
|
||||
'host' => 'mysql',
|
||||
'port' => '3307',
|
||||
'database' => 'payroll',
|
||||
'user' => 'root',
|
||||
'password' => '12345678',
|
||||
@ -40,5 +70,23 @@ return [
|
||||
'databaseSelectQueryErrorMessage' => 'There was an error fetching the data.',
|
||||
'databaseInsertQueryErrorMessage' => 'There was an error inserting the record.',
|
||||
],
|
||||
|
||||
// Employee settings
|
||||
'employee' => [
|
||||
'codeLength' => '3',
|
||||
'contractTypes' => array('INTERNO', 'EXTERNO'),
|
||||
'hoursPerWorkDay' => 8,
|
||||
'paymentPerHour' => 30,
|
||||
'bonusPerDelivery' => 5,
|
||||
'perHourBonusDriver' => 10,
|
||||
'perHourBonusLoader' => 5,
|
||||
'perHourBonusAux' => 0,
|
||||
'baseIsr' => .09,
|
||||
'extraIsr' => .03,
|
||||
'taxesAddUp' => true, // If true this will be total/(9 + 3) else they're subtracted separately
|
||||
'amountForExtraTaxes' => 16000,
|
||||
'vouchersForAllContractTypes' => false, // Outsourced personal won't get vouchers
|
||||
'percentOfPaymentForVouchers' => .04,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
@ -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
|
||||
|
BIN
calculatingSalary.bmp
Normal file
BIN
calculatingSalary.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 MiB |
1
database/.dockerignore
Normal file
1
database/.dockerignore
Normal file
@ -0,0 +1 @@
|
||||
Dockerfile
|
7
database/Dockerfile
Normal file
7
database/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM mysql:5.7
|
||||
|
||||
# Starting scripts
|
||||
ADD . /docker-entrypoint-initdb.d
|
||||
|
||||
# Config
|
||||
ADD my.cnf /etc/mysql
|
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
|
@ -11,13 +11,12 @@ CREATE TABLE IF NOT EXISTS `persons` (
|
||||
`lastName` varbinary(500) comment 'The last name of the person',
|
||||
`birthDate` DATE NOT NULL DEFAULT '1900-01-01' comment 'Date of birth of the person',
|
||||
`email` varbinary(500) NOT NULL comment 'The email adress of the person',
|
||||
`phone` INT(10) UNSIGNED NOT NULL comment 'The phone number of the person should be the mobile one but leaves room for home ones',
|
||||
`phone` BIGINT(10) UNSIGNED NOT NULL comment 'The phone number of the person should be the mobile one but leaves room for home ones',
|
||||
`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`),
|
||||
UNIQUE (`phone`),
|
||||
UNIQUE (`firstName`,`middleName`,`lastName`,`birthDate`)
|
||||
UNIQUE (`phone`)
|
||||
);
|
||||
|
||||
INSERT INTO persons (firstName, middleName, lastName, birthDate, email, phone)
|
||||
@ -45,3 +44,71 @@ CREATE TABLE IF NOT EXISTS `users` (
|
||||
|
||||
INSERT INTO users (idPerson, name, password)
|
||||
VALUES (1, 'sloth', '$2y$12$51mfESaLEGXDT4u9Bd9kiOHEpaJ1Bx4SEcVwsU5K6jVPMNkrnpJAa');
|
||||
|
||||
DROP TABLE IF EXISTS employeeType;
|
||||
CREATE TABLE IF NOT EXISTS `employeeType` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(100) NOT NULL comment 'Type or rol that the employee can be',
|
||||
`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`),
|
||||
UNIQUE (`name`)
|
||||
);
|
||||
|
||||
INSERT INTO employeeType (name) VALUES ('Chofer'),
|
||||
('Cargador'),
|
||||
('Auxiliar');
|
||||
|
||||
DROP TABLE IF EXISTS employees;
|
||||
CREATE TABLE IF NOT EXISTS `employees` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`idEmployeeType` INT UNSIGNED NOT NULL comment 'Defines the rol within the company',
|
||||
`idPerson` INT UNSIGNED NOT NULL comment 'Defines the rol within the company',
|
||||
`code` VARCHAR(100) NOT NULL comment 'A code to reference the employee',
|
||||
`contractType` ENUM('INTERNO', 'EXTERNO') NOT NULL comment 'The type of contract',
|
||||
`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`),
|
||||
INDEX `idx_contractType` (`contractType`),
|
||||
UNIQUE (`code`)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS paymentsPerEmployeePerDay;
|
||||
CREATE TABLE IF NOT EXISTS `paymentsPerEmployeePerDay` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`idEmployee` INT UNSIGNED NOT NULL comment 'The employee to who this payment will be made',
|
||||
`date` DATE NOT NULL DEFAULT '1900-01-01' comment 'Date of the worked day',
|
||||
`baseAmount` DOUBLE(10,2) NOT NULL DEFAULT 0.0 comment 'Amount paid for the hours worked',
|
||||
`bonusTime` DOUBLE(10,2) NOT NULL DEFAULT 0.0 comment 'Bonus paid for the hours worked',
|
||||
`deliveries` DOUBLE(10,2) NOT NULL DEFAULT 0.0 comment 'Bonus for the number of deliveries',
|
||||
`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 (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)
|
||||
);
|
20
database/my.cnf
Normal file
20
database/my.cnf
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; version 2 of the License.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
[mysqld]
|
||||
port = 3307
|
||||
|
||||
!includedir /etc/mysql/conf.d/
|
||||
!includedir /etc/mysql/mysql.conf.d/
|
28
docker-compose.yml
Normal file
28
docker-compose.yml
Normal file
@ -0,0 +1,28 @@
|
||||
version: '3'
|
||||
services:
|
||||
api:
|
||||
container_name: payroll_api
|
||||
build: api-payroll/
|
||||
ports:
|
||||
- "8085:80"
|
||||
volumes:
|
||||
- ./volumes/apache-logs:/var/log/apache2
|
||||
depends_on:
|
||||
- mysql
|
||||
mysql:
|
||||
container_name: payroll_mysql
|
||||
restart: always
|
||||
build: database
|
||||
expose:
|
||||
- "3307"
|
||||
ports:
|
||||
- "3307:3307"
|
||||
volumes:
|
||||
- ./volumes/mysql-data:/var/lib/mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: '12345678'
|
||||
MYSQL_USER: 'sloth'
|
||||
MYSQL_PASS: '12345678'
|
||||
volumes:
|
||||
mysql-data:
|
||||
apache-logs:
|
99
documentation/README.rst
Normal file
99
documentation/README.rst
Normal file
@ -0,0 +1,99 @@
|
||||
================
|
||||
Documentation
|
||||
================
|
||||
|
||||
.. contents::
|
||||
|
||||
Requirements
|
||||
----------------------------------
|
||||
Funtional:
|
||||
- A user name and password auth
|
||||
- Encrypted sensitive data
|
||||
- The employee need to have their full name captured
|
||||
- The last name must tolerate being null
|
||||
- An email will be needed for the employee
|
||||
- The email format must be formated
|
||||
- Employees will need a phone number
|
||||
- Searching employees despite the encryption
|
||||
- Employees must have a unique code to reference them
|
||||
- Being able to modify the name, email and phone values of already existing employees
|
||||
- Having the values for the different payments parametrized
|
||||
- Allowing for employees to perform other roles during their work day
|
||||
- Only for the auxiliary personnel
|
||||
- Taking into account only the current month for the salary
|
||||
- Reducing the taxes for the salary
|
||||
- If it goes beyond the threshold a different percentage is paid in taxes
|
||||
- The way the extra tax is handled should be parametrized
|
||||
|
||||
|
||||
Funtional:
|
||||
- Session management
|
||||
- Data integrity
|
||||
- Data security
|
||||
- Accessible through web
|
||||
- Containerized
|
||||
|
||||
Software behaivor
|
||||
-----------------
|
||||
In:
|
||||
- Employee details
|
||||
- First name
|
||||
- Middle name
|
||||
- Last name
|
||||
- Birth date
|
||||
- Email
|
||||
- Phone number
|
||||
- Work per day
|
||||
- Number of deliveries
|
||||
- Rol performed
|
||||
Process:
|
||||
- Register a new employee
|
||||
- Modify employee
|
||||
- Search employee
|
||||
- Add new work day for employee
|
||||
- Calculate monthly payment for employee
|
||||
|
||||
Out:
|
||||
- Upon registering
|
||||
- Employee code
|
||||
- In the work days registry
|
||||
- Raw salary for the the month
|
||||
- Taxes discounted
|
||||
- Real salary for the month
|
||||
- Vouchers (if applicable)
|
||||
|
||||
Calculating the monthly salary
|
||||
--------------------------------
|
||||
.. image:: https://raw.githubusercontent.com/PootisPenserHere/payroll_manager/master/documentation/calculatingSalary.bmp
|
||||
|
||||
Tests cases
|
||||
-----------------
|
||||
+----+----------------------------------------------------------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------+
|
||||
| Id | Description | Input | Expected output |
|
||||
+----+----------------------------------------------------------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------+
|
||||
| 1 | Displaying current salary for the outgoing month | Selecting an employee from the search field | On the right side of the window a break down of the employee's salary for the month will be displayed |
|
||||
+----+----------------------------------------------------------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------+
|
||||
| 2 | Submitting incomplete form | All of the input but one of the fields | An error shown in a red modal describing the missing field |
|
||||
+----+----------------------------------------------------------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------+
|
||||
| 3 | Altering the sent data to change the performed rol to one that can't be done by the employee | A employee other than aux performing a different rol than their own | An error displaying that the employee can't perform that task |
|
||||
+----+----------------------------------------------------------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------+
|
||||
|
||||
Executed tests
|
||||
---------------
|
||||
+----+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------+
|
||||
| Id | Description | Result | What went wrong? |
|
||||
+----+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------+
|
||||
| 1 | Displaying current salary for the outgoing month | When the employee was selected the current salary was loaded succesfully | |
|
||||
+----+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------+
|
||||
| 2 | Submitting incomplete form | Got the error "The number of deliveries cannot be empty or 0" | |
|
||||
+----+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------+
|
||||
| 3 | Altering the sent data to change the performed rol to one that can't be done by the employee | Got the error "The selected rol can't be done by this type of employee" | |
|
||||
+----+----------------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------+
|
||||
|
||||
Tools
|
||||
----------------------------------
|
||||
The following tools and software were used:
|
||||
- phpstorm
|
||||
- git
|
||||
- docker && docker-compose
|
||||
- Ubuntu 16
|
BIN
documentation/calculatingSalary.bmp
Normal file
BIN
documentation/calculatingSalary.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 MiB |
2
volumes/.gitignore
vendored
Normal file
2
volumes/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
mysql-data/
|
||||
apache-logs/
|
1
volumes/README.md
Normal file
1
volumes/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Do not delete this directory, it'll contain the volumes created by the containers
|
Loading…
Reference in New Issue
Block a user