Page 1 of 1

DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Mon Jun 01, 2015 7:33 am
by EricM
I was going to update my old punch station post, but it looks like it is so old it's now locked (http://forums.timetrex.com/viewtopic.php?f=5&t=5899).

The instructions from the old post still work in V8.0.7. To upgrade my v7 installation, I simply ran the official update, and then applied the changes again.

In APIAuthentication change

Code: Select all

if ( $this->getPermissionObject()->Check('company', 'view') AND $this->getPermissionObject()->Check('company', 'login_other_user') ) {
to

Code: Select all

if ( $this->getCurrentUserObject()->getId() == '1') {
In APIPunch change

Code: Select all

if ( is_object($current_station) AND $current_station->checkAllowed( $user_id, $station_id, $station_type ) == TRUE ) {
to

Code: Select all

if ( '1'  == '1' ) {
and comment out this:

Code: Select all

'station_id' => $current_station->getId(),
so it looks like:

Code: Select all

//'station_id' => $current_station->getId(),
After this, the old punch station script worked without modification. Attached is a newer version with a few tweaks I've added over the years. Now the timeclock directly emails me if someone tries to punch too often, if Timetrex is down, or if various problems occur.

Note: I run Timetrex under Ubuntu in a VM on Proxmox. In version 7, Timetrex would occasionally (once a month) hang up during the automated Proxmox backup. I believe it was caused by heavy disk access during the backup competing with Timetrex. Stopping the Timetrex cron jobs during the proxmox backup window seemed to fix this. I'll be curious to see how 8 behaves.

We currently have 150,000 punches and counting in our database.

Code: Select all

<?php
$RECONNECT = 0;
$LAST_PUNCH = 0;
$LAST_PUNCH_TIME = time();

require_once('../../classes/modules/api/client/TimeTrexClientAPI.class.php');

//admin login info
$TIMETREX_URL = 'http://1.1.1.1:8085/api/soap/api.php';
$TIMETREX_USERNAME = 'admin';
$TIMETREX_PASSWORD = 'admin';

	$colors = new Colors();
	
reconnect:

    $TIMETREX_SESSION_ID= FALSE;
    echo "\n" . $colors->getColoredString("Reconnecting to server..... Attempt " . $RECONNECT, "white", "red"). "\n";
	$RECONNECT = $RECONNECT + 1;
	
	if ($RECONNECT == 5){
	
	
    $cmd = 'echo "Administrator Alert. Error: Over 5 Reconnection Attempts." | mail -S smtp=1.1.1.1 -s "TimeTrex Timeclock" "name@site.com, name@site.com"  > /dev/null 2>/dev/null &';
	exec($cmd);
	
	}
	
    $api_session = new TimeTrexClientAPI();
	$api_session->Login( $TIMETREX_USERNAME, $TIMETREX_PASSWORD );
	
	if ( $TIMETREX_SESSION_ID == FALSE ) {
              sleep(10);
		goto reconnect;
	}
	
     
newscan:
	$punch_obj = new TimeTrexClientAPI( 'Punch' );
	$punchtime = time();
    $punchtype = "Normal";
	$punchstatus = "Auto";
    $punchid = 0;
	$firsttime = TRUE;
 while ($punchid == 0){
    
    if (time() - $punchtime > 10){
    	    $punchtype = "Normal";
	        $punchstatus = "Auto";
	}

    //get time from server and print it
    $result = $punch_obj->getUserPunch();

	if($result == false){
	 echo $colors->getColoredString("CONNECTION ERROR", "white", "red");
 	echo "\n";
	goto reconnect;
	}
	$punch_data = $result->getResult();
	if($punch_data == false){
	 echo $colors->getColoredString("CONNECTION ERROR", "white", "red");
 	echo "\n";
	goto reconnect;
	}
	echo "\n\n";
 	echo $punch_data['time_stamp'];
	 echo "\n\nScanner Ready\n\n";

	 
	 
	 if ($punchtype != "Normal"){
	echo $colors->getColoredString("Punch Type: " . $punchtype , "white", "red");
	} else{
	echo "Punch Type: ". $punchtype ;
	}
	echo "\n";
	 if ($punchstatus != "Auto"){
	echo $colors->getColoredString("Punch Direction: " . $punchstatus , "white", "red");
	} else{
    echo "Punch Direction: ". $punchstatus;
	}
	
	$reconnect = 0;
	echo "\n\n";
	
	//skip the padding to leave the last report on the screen
	if ($firsttime == FALSE){
	 echo "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; 
	 }


	//get scanner input if over 10 seconds, refresh screen
    $input = fgets_u(STDIN,10);
   
   if (strpos($input,"IN") !== FALSE){
    $punchstatus = "In";
	$punchtime = time();
   }
   if (strpos($input,"OUT") !== FALSE){
    $punchstatus = "Out";
	$punchtime = time();
   }
   
   if (strpos($input,"BREAK") !== FALSE){
    $punchtype = "Break";
    $punchstatus = "Out";
	$punchtime = time();
   }
   if (strpos($input,"LUNCH") !== FALSE){
    $punchtype = "Lunch";
    $punchstatus = "Out";
	$punchtime = time();
   }
   
   if (strpos($input,"NORMAL") !== FALSE || strpos($input,"RESET") !== FALSE ||  strpos($input,"CLEAR") !== FALSE){
        $punchtype = "Normal";
	    $punchstatus = "Auto";
   }
    
   if (strpos($input,"REPORT") !== FALSE){
    $punchtype = "Report";
	$punchtime = time();
   }
      
   if (is_numeric($input)){
	$punchid = (int)$input;
   }   
   
   $firsttime = FALSE;
}
  
$user_obj = new TimeTrexClientAPI( 'User' );

if($user_obj  == false){

	 echo $colors->getColoredString("CONNECTION ERROR - user_obj\n", "white", "red");
	goto reconnect;
}

$result = $user_obj->getUser(array('filter_data' => array('employee_number' => $punchid)));
	
if($result == false){
     echo $colors->getColoredString("CONNECTION ERROR - getUser", "white", "red");
 	echo "\n";
	goto reconnect;
}
$user_data = $result->getResult();

if($user_data  == false){
     echo $colors->getColoredString("CONNECTION ERROR- getResult -> getUser", "white", "red");
 	echo "\n";
	goto reconnect;
}
if (is_bool($user_data) == TRUE) {
 	echo "\n\n";
	echo $colors->getColoredString("USER NOT FOUND IN DATABASE!", "white", "red");
 	echo "\n\n";
	
	$cmd = 'echo "Error: User ID not found! #';
	$cmd .= (string)$punchid;
	$cmd .=  '" | mail -S smtp=1.1.1.1 -s "TimeTrex Timeclock" name@site.com  > /dev/null 2>/dev/null &';
	exec($cmd);



	goto newscan;
}

echo "\nUser found.  Working...\n";

if ($punchtype != "Report"){

 
  
if($user_data[0]["status"]  != "Active"){
     echo $colors->getColoredString("INACTIVE USER", "white", "red");
 	echo "\n";
	
	$cmd = 'echo "Error: User ID inactive! ';
	$cmd .= "#{$user_data[0]['employee_number']}: {$user_data[0]['last_name']}, {$user_data[0]['first_name']}\n\n";
	$cmd .=  '" | mail -S smtp=1.1.1.1 -s "TimeTrex Timeclock" name@site.com  > /dev/null 2>/dev/null &';
	exec($cmd);
	goto reconnect;
}
  
  
//Switch to the User we scanned in
$auth_obj = new TimeTrexClientAPI( 'Authentication' );


if($auth_obj  == false){
	 echo $colors->getColoredString("CONNECTION ERROR - auth_obj", "white", "red");
 	echo "\n";
	goto reconnect;
}
$auth_obj->switchUser( $user_data[0]['id'] );


if($auth_obj  == false){
	 echo $colors->getColoredString("CONNECTION ERROR - switchUser", "white", "red");
 	echo "\n";
	goto reconnect;
}

$punch_obj = new TimeTrexClientAPI( 'Punch' );

if($punch_obj  == false){
	 echo $colors->getColoredString("CONNECTION ERROR - punch_obj", "white", "red");
 	echo "\n";
	goto reconnect;
}

$result = $punch_obj->getUserPunch();
if($result  == false){
	 echo $colors->getColoredString("CONNECTION ERROR - getUserPunch", "white", "red");
 	echo "\n";
	goto reconnect;
}


$punch_data = $result->getResult();

if($punch_data  == false){
     echo $colors->getColoredString("CONNECTION ERROR - getResult -> getUserPunch", "white", "red");
 	echo "\n";
	goto reconnect;
}


//override the automaticlly generated punch
if ($punchstatus == "In"){
 $punch_data['status_id']=10;
}
if ($punchstatus == "Out"){
$punch_data['status_id']=20;
}

if ($punchtype == "Break"){
 $punch_data['type_id']=30;
}
if ($punchtype == "Lunch"){
$punch_data['type_id']=20;
}



if ($punchtype != "Report"){

$result = $punch_obj->setUserPunch($punch_data);
if($result  == false){
     echo $colors->getColoredString("CONNECTION ERROR - setUserPunch", "white", "red");
 	echo "\n";
	goto reconnect;
}

$result = $result->getResult();

if ($result == false) {
    echo $colors->getColoredString("\nPLEASE WAIT AT LEAST ONE MINUTE BETWEEN PUNCHES!", "white", "red");
 	echo "\n";
	
	$cmd = 'echo "Error: Cannot generate punch - User likely double punched! ';
	$cmd .= "#{$user_data[0]['employee_number']}: {$user_data[0]['last_name']}, {$user_data[0]['first_name']}\n\n";
	$cmd .=  '" | mail -S smtp=1.1.1.1 -s "TimeTrex Timeclock" name@site.com  > /dev/null 2>/dev/null &';
	exec($cmd);
	
	goto reconnect;
}else {
echo "#{$user_data[0]['employee_number']} Punch Complete\n";
}

}

$api_session->Logout();
}

//Generate the punch report
//log back in as admin

$api_session->Login( $TIMETREX_USERNAME, $TIMETREX_PASSWORD );
if ( $TIMETREX_SESSION_ID == FALSE ) {
	 echo $colors->getColoredString("CONNECTION ERROR - REPORT LOGIN FAILED", "white", "red");
 	echo "\n";
	goto reconnect;
}

//get report
echo "____________________\n\n";
echo "Punch Summary Report\n";
echo "____________________\n\n";
$report_obj = new TimeTrexClientAPI( 'PunchSummaryReport' );
if($report_obj  == false){
     echo $colors->getColoredString("CONNECTION ERROR - report_obj", "white", "red");
 	echo "\n";
	goto reconnect;
}

$config = $report_obj->getTemplate('by_employee+punch_summary+total_time')->getResult();
if($config  == false){
     echo $colors->getColoredString("CONNECTION ERROR - getTemplate", "white", "red");
 	echo "\n";
	goto reconnect;
}

//adjust template
$config['-1010-time_period']['time_period'] = 'this_pay_period';
$config['employee_number'] = $user_data[0]['employee_number'];


//let the server generate the report
$result = $report_obj->getPunchSummaryReport($config , 'csv' );
if($result  == false){
     echo $colors->getColoredString("CONNECTION ERROR - getPunchSummaryReport", "white", "red");
 	echo "\n";
	goto reconnect;
}

$result = $result->getResult();
if($result  == false){
     echo $colors->getColoredString("CONNECTION ERROR - getResult -> getPunchSummaryReport", "white", "red");
 	echo "\n";
	goto reconnect;
}

//parse and display the CSV file

//decode report
$input = base64_decode($result['data']);
//split the line breaks
$input = str_replace("\"", "", $input);
$csvData = explode( "\n", $input); 
//split at the commas
foreach ($csvData as &$value) {
     $value = explode(',', $value); 
}

$total = 0;  // tally of total hours worked

//print table headers
echo "#{$user_data[0]['employee_number']}: {$user_data[0]['last_name']}, {$user_data[0]['first_name']}\n\n";

//echo each line of data
foreach ($csvData as &$value) {
if(isset($value[2]) && isset($value[3]) &&isset($value[4]) && isset($value[5]) && isset($value[6])) {
     echo str_pad ($value[2], 10);
	 echo str_pad ($value[3], 20);
	 echo str_pad ($value[4], 10);
	 echo str_pad ($value[5], 20);
     if (is_numeric($value[6]) ) {
	   $total = $total + (float)$value[6];
	   if ((float)$value[6] != 0 ){
	   printf( "% 6.3f", $value[6]); 
	 }else{
	 echo str_pad($value[6], 10);
	 }
	 echo "\n";
}	
}
//print total hours worked
echo str_pad (" ", 60);
echo $colors->getColoredString(sprintf( "% 6.3f",  $total), "white", "green");
//done

if ($punchtype != "Report"){

if ($LAST_PUNCH == $punchid and $LAST_PUNCH_TIME + 90 > time()){
 	$cmd = 'echo "Warning: Punches too close together - User likely double punched! ';
	$cmd .= "#{$user_data[0]['employee_number']}: {$user_data[0]['last_name']}, {$user_data[0]['first_name']}\n\n";
	$cmd .=  '" | mail -S smtp=1.1.1.1 -s "TimeTrex Timeclock" name@site.com  > /dev/null 2>/dev/null &';
	exec($cmd);
}

//done
$LAST_PUNCH = $punchid;
$LAST_PUNCH_TIME = time();

}


goto newscan;
  
			
function fgets_u($pStdn,$delay) {
        $pArr = array($pStdn);
        if (false === ($num_changed_streams = stream_select($pArr, $write = NULL, $except = NULL,$delay ))) {
            print("\$ 001 Socket Error : UNABLE TO WATCH STDIN.\n");
            return FALSE;
        } elseif ($num_changed_streams > 0) {
                return trim(fgets($pStdn, 1024));
        }
    }
?>

<?php
 
	class Colors {
		private $foreground_colors = array();
		private $background_colors = array();
 
		public function __construct() {
			// Set up shell colors
			$this->foreground_colors['black'] = '0;30';
			$this->foreground_colors['dark_gray'] = '1;30';
			$this->foreground_colors['blue'] = '0;34';
			$this->foreground_colors['light_blue'] = '1;34';
			$this->foreground_colors['green'] = '0;32';
			$this->foreground_colors['light_green'] = '1;32';
			$this->foreground_colors['cyan'] = '0;36';
			$this->foreground_colors['light_cyan'] = '1;36';
			$this->foreground_colors['red'] = '0;31';
			$this->foreground_colors['light_red'] = '1;31';
			$this->foreground_colors['purple'] = '0;35';
			$this->foreground_colors['light_purple'] = '1;35';
			$this->foreground_colors['brown'] = '0;33';
			$this->foreground_colors['yellow'] = '1;33';
			$this->foreground_colors['light_gray'] = '0;37';
			$this->foreground_colors['white'] = '1;37';
 
			$this->background_colors['black'] = '40';
			$this->background_colors['red'] = '41';
			$this->background_colors['green'] = '42';
			$this->background_colors['yellow'] = '43';
			$this->background_colors['blue'] = '44';
			$this->background_colors['magenta'] = '45';
			$this->background_colors['cyan'] = '46';
			$this->background_colors['light_gray'] = '47';
		}
 
		// Returns colored string
		public function getColoredString($string, $foreground_color = null, $background_color = null) {
			$colored_string = "";
 
			// Check if given foreground color found
			if (isset($this->foreground_colors[$foreground_color])) {
				$colored_string .= "\033[" . $this->foreground_colors[$foreground_color] . "m";
			}
			// Check if given background color found
			if (isset($this->background_colors[$background_color])) {
				$colored_string .= "\033[" . $this->background_colors[$background_color] . "m";
			}
 
			// Add string and end coloring
			$colored_string .=  $string . "\033[0m";
 
			return $colored_string;
		}
 
		// Returns all foreground color names
		public function getForegroundColors() {
			return array_keys($this->foreground_colors);
		}
 
		// Returns all background color names
		public function getBackgroundColors() {
			return array_keys($this->background_colors);
		}
	}
 
?>

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Wed Jun 03, 2015 4:54 am
by wiki
Wow! Impressive! Nice job mate!

Will this work on other brand of barcode scanner?



---------------
WK

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Wed Jun 03, 2015 6:37 am
by EricM
I used barcode scanners that show up as a keyboard, so anything (barcode, RFID, etc) that can type into the console will work.

The barcodes themselves are simply the employee number printed out with a few leading zeros. For more permanent employees we make plastic cards on an Fargo ID printer. Temps get laminated card stock. It works well for us because we can make temporary cards on the fly, or even make blank cards (and blank users in Timetrex) in bulk ahead of time to hand out and fill in later.

As for the barcodes, I suggest using Code 128. When we used Code 39 barcodes, we had occasional mis-scans.

With Code 128, we still get one or two mis-scans (normally a missing digit) per year (verified by security camera footage and logs), but I think this might be due to a pi USB fault or possibly a scanner fault. Either way, 1 fault out of 60,000 is doing pretty good.

I'd suggest getting familiar with PHP (if you aren't already) and modifying the script to suit your own needs. That way you can make it do whatever you want. For example, at one point we had a receipt printer that would automatically print out timecards, but that turned out to be a waste of paper. The on screen display was more popular.

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Wed Jun 03, 2015 9:15 pm
by wiki
Wow! speechless!

This is exactly i was looking for. I think i'll try this out using a regular CPU , some cheap barcode scanner and keyboard. We go paperless and just a display on the monitor.

Unfortunately, i'm just a php beginner, so i'm totally relying on your guides. Do you have any summarized step by step guide exactly how you put it altogether? Thanks mate, this is a huge help in my part.

Thanks,

WK

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Mon Jun 08, 2015 6:08 am
by EricM
If you aren't comfortable with setting it up yourself, I'd suggest either sticking to the default installation, or paying for the professional version and buying a solution.

You don't want to run into a problem down the road and be unable to fix it.

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Tue Jun 09, 2015 3:17 am
by wiki
Hi!

Everything is in default installation, I just want a quick punching station for free since most of our volunteer workers are older, they cannot stand logging in with so many steps. We are a non-profit institution, we can't afford to buy professional license annually.

All I want is a barcode standalone quick punch station without printing. I think your implementation is so close of what i'm trying to do.


Thanks,

WK

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Mon Jun 22, 2015 8:21 am
by EricM
Note: I found out this morning that v8 has an automatic update feature that is turned on by default. This does not play well with modified versions of timetrex.

To fix the warnings, I manually updated to 8.0.8 using the Linux installer from sourceforge, re-applied my modifications, and then disabled automatic updates by adding the following line to timetrex.ini.php:

[other]
disable_auto_upgrade = TRUE

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Sat Dec 26, 2015 5:26 pm
by EricM
Note: The above modifications still work in 9.0.5, but the station line that needs to be commented out looks slightly different.

Code: Select all

$data['station_id'] = $current_station->getId();

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Thu May 25, 2017 4:28 am
by EricM
Note: The above modifications still work in 10.6, but I noticed updates can no longer be manually downloaded from sourceforge.

I get them from here:
https://www.timetrex.com/onsite_communi ... p=download

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Wed Jun 14, 2017 5:08 pm
by haiyai
Hi

looking to build this punch station total PHP newbie but, no time to learn PHP working on translating this timetrex to Japanese.
Trying to see if this is going to work for Japan.

If anyone has a quick and dirty install on this PHP script where to drop it into directories and stuff, hardware not a problem just the
software side.

Awesome work by the way sounds exactly simple and functional. :D

Cheers

Morgan

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Thu Aug 24, 2017 1:37 am
by haiyai
EricM,

I would like to get in touch with you about DIY Stand Alone Barcode Punch Station on Ubuntu TimeTrex 10.7.1 if possible.

Thank You

Morgan

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Sun Jun 17, 2018 6:06 am
by EricM
Still works in 11.2.3, but you will need to replace the '1' in the following line with the UUID of the admin user.

Code: Select all

if ( $this->getCurrentUserObject()->getId() == '1') {
A lazy way to find your UUID is to use the following line of code, put it above the check and then read the result:

Code: Select all

file_put_contents('/var/www/html/classes/modules/api/client/log.txt', $this->getCurrentUserObject()->getId(), FILE_APPEND | LOCK_EX);
In apipunch.php you also need to comment out another two lines that reference station_id.

Code: Select all

//Debug::Text('Station ID: '. $station_id .' User ID: '. $user_id .' Epoch: '. $epoch, __FILE__, __LINE__, __METHOD__, 10);
//Debug::Text('Station Allowed! ID: '. $current_station->getId() .' ('. $station_id .') User ID: '. $user_id .' Epoch: '. $epoch, __FILE__, __LINE__, __METHOD__, 10);

Re: DIY Stand Alone Barcode Punch Station - Timetrex v8.0.7

Posted: Tue Mar 03, 2020 11:32 pm
by systemadmin
Hi Eric,

I'm just trying to upgrade my very old 7.3.6 installation. At the time of installation I built aPaspberryPi based RFID punch station after your suggestions with some minor changes.

Do you know I the current version 12.1. should still work (given the changes you already mentioned in the thread)?


thx

Carsten