Employee hours
This commit is contained in:
@@ -43,5 +43,15 @@ class EmployeeHoursController extends MY_Controller {
|
||||
$this->json( $monthlyhours );
|
||||
}
|
||||
|
||||
public function getMonthlyEmployeehours( $month, $year )
|
||||
{
|
||||
$sql = QEmployeeHours::fire( $month, $year );
|
||||
|
||||
$query = $this->db->query( $sql );
|
||||
|
||||
$result = $query->result();
|
||||
|
||||
$this->json( $result );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,6 +46,7 @@ class ProjecthoursPerEmployeePerMonth extends BaseModel {
|
||||
->join( 'projectemployees AS PEM', 'PEM.id', '=', 'projecthours.projectemployee_id' )
|
||||
->join( 'projects AS PRJ', 'PRJ.id', '=', 'PEM.project_id' )
|
||||
->where( 'PEM.user_id', '=', $employeeID )
|
||||
->where( 'projecthours.hourtype_id', '=', 1)
|
||||
->groupBy('PRJ.id')
|
||||
->groupBy('PEM.tarif')
|
||||
->groupBy('PRJ.projectname')
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by Pensare Software.
|
||||
* User: Theo Weijmer
|
||||
* Date: 01/03/2018
|
||||
* Time: 12:03
|
||||
*/
|
||||
|
||||
class QEmployeeHours {
|
||||
|
||||
public static function fire( $month, $year )
|
||||
{
|
||||
$sql = "
|
||||
SELECT monthID,
|
||||
user_id,
|
||||
CONCAT(firstname, ' ', middlename, ' ', lastname) AS fullname,
|
||||
SUM(projecthours) AS projecthours,
|
||||
SUM(profitablehours) AS profitablehours,
|
||||
SUM(nonprofitablehours) AS nonprofitablehours,
|
||||
SUM(revenues) AS revenues,
|
||||
SUM(nonprojecthours) AS nonprojecthours,
|
||||
SUM(projecthours + nonprojecthours) AS totalhours,
|
||||
targethours
|
||||
FROM (
|
||||
SELECT employee_id AS user_id,
|
||||
MONTH(hourdate) monthID,
|
||||
0 AS projecthours,
|
||||
0 AS profitablehours,
|
||||
0 AS nonprofitablehours,
|
||||
0 AS revenues,
|
||||
SUM(hours) AS nonprojecthours
|
||||
FROM employeehours
|
||||
WHERE YEAR(hourdate) = '{$year}'
|
||||
AND MONTH(hourdate) = '$month'
|
||||
GROUP BY employee_id, MONTH(hourdate)
|
||||
UNION
|
||||
SELECT PEM.user_id,
|
||||
MONTH(hourdate) monthID,
|
||||
SUM(hours) AS projecthours,
|
||||
SUM(IF(hourtype_id = 1, hours, 0)) AS profitablehours,
|
||||
SUM(IF(hourtype_id = 2, hours, 0)) AS nonprofitablehours,
|
||||
ROUND(SUM( IF(hourtype_id = 1, (hours * PEM.tarif), 0) ), 0) AS revenues,
|
||||
0 AS nonprojecthours
|
||||
FROM projecthours PH
|
||||
JOIN projectemployees PEM
|
||||
ON PEM.id = PH.projectemployee_id
|
||||
WHERE YEAR(hourdate) = '{$year}'
|
||||
AND MONTH(hourdate) = '$month'
|
||||
GROUP BY PEM.user_id, MONTH(hourdate)
|
||||
) AS Q1
|
||||
JOIN users AS USR
|
||||
ON USR.id = Q1.user_id
|
||||
GROUP BY monthID, user_id ";
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<div id="EMPLOYEEHOURSREPORT">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-1">
|
||||
<label>jaar</label>
|
||||
</div>
|
||||
<div class="col-xl-3">
|
||||
<select class="form-control form-control-sm"
|
||||
data-bind="options:years,
|
||||
optionsText:'year',
|
||||
optionsValue: 'id',
|
||||
value:selectedYear,
|
||||
event:{change:refreshTab}"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="nav-tabs-horizontal">
|
||||
<ul class="nav nav-tabs nav-tabs-solid">
|
||||
<!-- ko foreach:months -->
|
||||
<li class="nav-item" data-bind="click:$root.setActivetab.bind($data, monthID)" style="z-index:10">
|
||||
<a class="nav-link"
|
||||
data-bind="text:monthName, css:{'active' : $root.selectedMonthID() == monthID}"></a>
|
||||
</li>
|
||||
<!-- /ko -->
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active">
|
||||
<?php $this->load->view('reports/employeehours/employeemonthhours_view'); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,6 +70,7 @@
|
||||
<script src="js/lib/modules/projects/projectsModule.js?version=<?= $this->version ?>"></script>
|
||||
<script src="js/lib/modules/projects/employeehoursModule.js?version=<?= $this->version ?>"></script>
|
||||
<script src="js/lib/modules/reports/monthlyEmployeeHoursModule.js?version=<?= $this->version ?>"></script>
|
||||
<script src="js/lib/modules/reports/employeeHours.js?version=<?= $this->version ?>"></script>
|
||||
|
||||
<!-- ***********MODALS************* -->
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-condensed" style="font-size:.8em;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="rotate"><div><span>medewerker</span></div></th>
|
||||
<th class="rotate"><div><span>uren declarabel</span></div></th>
|
||||
<th class="rotate"><div><span>uren niet declarabel</span></div></th>
|
||||
<th class="rotate"><div><span>uren indirect</span></div></th>
|
||||
<th class="rotate"><div><span>totaal bestede uren</span></div></th>
|
||||
<th class="rotate"><div><span>uren target(1.600 jaar)</span></div></th>
|
||||
<th class="rotate"><div><span>aandeel uren besteed tov target</span></div></th>
|
||||
<th class="rotate"><div><span>bezettingsgraad totaal<br />aantal gewerkte uren</span></div></th>
|
||||
<th class="rotate"><div><span>bezettingsgraad t.o.v. target<br />factureerbare uren</span></div></th>
|
||||
<th class="rotate"><div><span>omzet</span></div></th>
|
||||
<th class="rotate"><div><span>gemiddeld tarief</span></div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- ko foreach:employees -->
|
||||
<tr>
|
||||
<td><span data-bind="text:fullname"></span></td>
|
||||
<td class="align-right"><span data-bind="text:profitablehours"></span></td>
|
||||
<td class="align-right"><span data-bind="text:nonprofitablehours"></span></td>
|
||||
<td class="align-right"><span data-bind="text:nonprojecthours"></span></td>
|
||||
<td class="align-right"><span data-bind="text:totalhours"></span></td>
|
||||
<td class="align-right"><span data-bind="text:target"></span></td>
|
||||
<td class="align-right"><span data-bind="text:occupancyRateTotal"></span>%</td>
|
||||
<td class="align-right"><span data-bind="text:occupancyRateHours"></span>%</td>
|
||||
<td class="align-right"><span data-bind="text:occupancyRateInvoice"></span></td>
|
||||
<td class="align-right"><i class="icon fa-euro"></i> <span data-bind="text:revenues"></span></td>
|
||||
<td class="align-right"><i class="icon fa-euro"></i> <span data-bind="text:averageDayRate"></span></td>
|
||||
</tr>
|
||||
<!-- /ko -->
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<th><span>totaal</span></th>
|
||||
<th class="align-right"><span data-bind="text:profitablehoursTotaal"></span></th>
|
||||
<th class="align-right"><span data-bind="text:nonprofitablehoursTotaal"></span></th>
|
||||
<th class="align-right"><span data-bind="text:nonprojecthoursTotaal"></span></th>
|
||||
<th class="align-right"><span data-bind="text:totalhoursTotaal"></span></th>
|
||||
<th class="align-right"><span data-bind="text:targetTotaal"></span></th>
|
||||
<th class="align-right"><span data-bind="text:occupancyRateTotalTotaal"></span>%</th>
|
||||
<th class="align-right"><span data-bind="text:occupancyRateHoursTotaal"></span>%</th>
|
||||
<th class="align-right"><span data-bind="text:occupancyRateInvoiceTotaal"></span>%</th>
|
||||
<th class="align-right"><i class="icon fa-euro"></i> <span data-bind="text:revenuesTotaal"></span></th>
|
||||
<th class="align-right"><i class="icon fa-euro"></i> <span data-bind="text:averageDayRateTotaal"></span></th>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="graph-holder" style="height:100%">
|
||||
<canvas id="hours-per-month" width="300"></canvas>
|
||||
<div id="hours-per-month-legends"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="graph-holder" style="height:100%">
|
||||
<canvas id="revenues-per-month" width="300"></canvas>
|
||||
<div id="revenues-per-month-legends"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="graph-holder" style="height:100%">
|
||||
<canvas id="average-rate" width="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -14,6 +14,13 @@ dbo.employeehoursDataService = {
|
||||
} );
|
||||
},
|
||||
|
||||
getMonthlyEmployeehours : function(month, $year, callback){
|
||||
$.getJSON( 'index.php/reports/employeehoursController/getMonthlyEmployeehours/' + month + '/' + $year )
|
||||
.done( function ( data ) {
|
||||
callback( data );
|
||||
} );
|
||||
},
|
||||
|
||||
getHourtypes: function ( callback ) {
|
||||
$.getJSON( 'index.php/projects/employeehoursController/getHourtypes' )
|
||||
.done( function ( data ) {
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Created by Pensare Software.
|
||||
* User: Theo Weijmer
|
||||
* Date: 01/03/2018
|
||||
* Time: 11:06
|
||||
*/
|
||||
|
||||
dbo.employeehoursreportInit = function () {
|
||||
|
||||
if ( dbo.employeeHoursreportModel !== undefined ) {
|
||||
dbo.employeeHoursreportModel.initModule();
|
||||
} else {
|
||||
|
||||
dbo.employeeHoursreportModel = {
|
||||
|
||||
months : ko.observableArray( [
|
||||
{monthID: 1, monthName: 'jan'},
|
||||
{monthID: 2, monthName: 'febr'},
|
||||
{monthID: 3, monthName: 'mrt'},
|
||||
{monthID: 4, monthName: 'apr'},
|
||||
{monthID: 5, monthName: 'mei'},
|
||||
{monthID: 6, monthName: 'juni'},
|
||||
{monthID: 7, monthName: 'juli'},
|
||||
{monthID: 8, monthName: 'aug'},
|
||||
{monthID: 9, monthName: 'sept'},
|
||||
{monthID: 10, monthName: 'okt'},
|
||||
{monthID: 11, monthName: 'nov'},
|
||||
{monthID: 12, monthName: 'dec'}
|
||||
] ),
|
||||
month : ko.observable(),
|
||||
selectedMonthID : ko.observable(),
|
||||
years : ko.observableArray( [] ),
|
||||
selectedYear : ko.observable(),
|
||||
employees : ko.observableArray( [] ),
|
||||
profitablehoursTotaal : ko.observable(),
|
||||
nonprofitablehoursTotaal : ko.observable(),
|
||||
nonprojecthoursTotaal : ko.observable(),
|
||||
totalhoursTotaal : ko.observable(),
|
||||
targetTotaal : ko.observable(),
|
||||
occupancyRateTotalTotaal : ko.observable(),
|
||||
occupancyRateHoursTotaal : ko.observable(),
|
||||
occupancyRateInvoiceTotaal: ko.observable(),
|
||||
revenuesTotaal : ko.observable(),
|
||||
averageDayRateTotaal : ko.observable(),
|
||||
initialize : null,
|
||||
|
||||
init: function () {
|
||||
var vm = dbo.employeeHoursreportModel;
|
||||
|
||||
vm.initialize = true;
|
||||
dbo.reportsDataService.getUsedYears( function ( data ) {
|
||||
vm.years.removeAll();
|
||||
_.each( data, function ( p ) {
|
||||
vm.years.push( {id: p.year, year: p.year} );
|
||||
} );
|
||||
|
||||
vm.selectedYear( (new Date()).getFullYear() );
|
||||
vm.selectedMonthID( 1 );
|
||||
|
||||
vm.initialize = false;
|
||||
|
||||
vm.initModule();
|
||||
} );
|
||||
},
|
||||
|
||||
initModule: function () {
|
||||
var vm = dbo.employeeHoursreportModel;
|
||||
|
||||
vm.refreshTab();
|
||||
},
|
||||
|
||||
setActivetab: function ( tab ) {
|
||||
var vm = dbo.employeeHoursreportModel;
|
||||
|
||||
vm.selectedMonthID( tab );
|
||||
|
||||
vm.refreshTab();
|
||||
},
|
||||
|
||||
refreshTab: function () {
|
||||
var vm = dbo.employeeHoursreportModel;
|
||||
|
||||
if ( vm.initialize ) return;
|
||||
|
||||
vm.profitablehoursTotaal( 0 );
|
||||
vm.nonprofitablehoursTotaal( 0 );
|
||||
vm.nonprojecthoursTotaal( 0 );
|
||||
vm.totalhoursTotaal( 0 );
|
||||
vm.targetTotaal( 0 );
|
||||
vm.occupancyRateTotalTotaal( 0 );
|
||||
vm.occupancyRateHoursTotaal( 0 );
|
||||
vm.occupancyRateInvoiceTotaal( 0 );
|
||||
vm.revenuesTotaal( 0 );
|
||||
vm.averageDayRateTotaal( 0 );
|
||||
|
||||
dbo.employeehoursDataService.getMonthlyEmployeehours( vm.selectedMonthID(), vm.selectedYear(), function ( data ) {
|
||||
vm.employees.removeAll();
|
||||
var chartData = {};
|
||||
chartData.revenues = [];
|
||||
chartData.hours = [];
|
||||
chartData.averageRate = [];
|
||||
chartData.labels = [];
|
||||
_.each( data, function ( p ) {
|
||||
vm.employees.push( new vm.Employee( p ) );
|
||||
|
||||
vm.profitablehoursTotaal( vm.profitablehoursTotaal() + Number( p.profitablehours ) );
|
||||
vm.nonprofitablehoursTotaal( vm.nonprofitablehoursTotaal() + Number( p.nonprofitablehours ) );
|
||||
vm.nonprojecthoursTotaal( vm.nonprojecthoursTotaal() + Number( p.nonprojecthours ) );
|
||||
vm.totalhoursTotaal( vm.totalhoursTotaal() + Number( p.totalhours ) );
|
||||
vm.targetTotaal( Math.round( vm.targetTotaal() + Number( p.targethours / 12 ) ) );
|
||||
vm.revenuesTotaal( Number( vm.revenuesTotaal() ) + Number( p.revenues ) );
|
||||
|
||||
chartData.labels.push( p.fullname );
|
||||
chartData.revenues.push( p.revenues );
|
||||
chartData.averageRate.push( Math.round( (p.revenues / p.profitablehours) * 8 ) )
|
||||
chartData.hours.push( p.totalhours );
|
||||
} );
|
||||
vm.occupancyRateTotalTotaal( Math.round( (vm.totalhoursTotaal() / vm.targetTotaal()) * 100 ) );
|
||||
vm.occupancyRateHoursTotaal( Math.round( (vm.profitablehoursTotaal() / vm.totalhoursTotaal() ) * 100 ) );
|
||||
vm.occupancyRateInvoiceTotaal( Math.round( (vm.profitablehoursTotaal() / vm.targetTotaal()) * 100 ) );
|
||||
vm.averageDayRateTotaal( Math.round( Number( vm.revenuesTotaal() ) / Number( vm.profitablehoursTotaal() ) ) * 8 );
|
||||
vm.revenuesTotaal( dbo.helpers.thousandSeparator( vm.revenuesTotaal() ) );
|
||||
|
||||
dbo.charts.drawPie( 'revenues-per-month', chartData.revenues, chartData.labels, 'omzet per medewerker' );
|
||||
dbo.charts.drawPie( 'hours-per-month', chartData.hours, chartData.labels, 'uren per medewerker' );
|
||||
dbo.charts.drawHoursChart( 'average-rate', chartData.averageRate, chartData.labels, 'gemiddeld tarief', '#78be4b' );
|
||||
} );
|
||||
},
|
||||
|
||||
UniqueEmployee: function ( data ) {
|
||||
var vm = dbo.employeeHoursreportModel;
|
||||
var self = this;
|
||||
|
||||
self.id = data.id;
|
||||
self.fullname = data.fullname;
|
||||
self.slug = data.slug;
|
||||
},
|
||||
|
||||
Employee: function ( data ) {
|
||||
this.fullname = data.fullname;
|
||||
this.profitablehours = data.profitablehours;
|
||||
this.nonprofitablehours = data.nonprofitablehours;
|
||||
this.nonprojecthours = data.nonprojecthours;
|
||||
this.totalhours = data.totalhours;
|
||||
this.target = Math.round( data.targethours / 12 );
|
||||
this.occupancyRateTotal = Math.round( (data.totalhours / (data.targethours / 12)) * 100 );
|
||||
this.occupancyRateHours = Math.round( (data.profitablehours / data.totalhours ) * 100 );
|
||||
this.occupancyRateInvoice = Math.round( (data.profitablehours / (data.targethours / 12)) * 100 );
|
||||
this.revenues = dbo.helpers.thousandSeparator( data.revenues );
|
||||
this.averageDayRate = Math.round( (data.revenues / data.profitablehours) * 8 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dbo.employeeHoursreportModel.init();
|
||||
|
||||
ko.applyBindings( dbo.employeeHoursreportModel, document.getElementById( "EMPLOYEEHOURSREPORT" ) );
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,6 @@ dbo.monthlyemployeehoursInit = function () {
|
||||
setActivetab: function ( tab ) {
|
||||
var vm = dbo.monthlyEmployeeHoursModel;
|
||||
|
||||
console.log( 'inside' );
|
||||
$.each( vm.uniqueEmployees(), function ( i, p ) {
|
||||
if ( tab == p.slug ) {
|
||||
vm.currentTab = p;
|
||||
|
||||
Reference in New Issue
Block a user