Standard labor entry screens provided by XA may be a bit of a challenge to deploy onto the shop floor.
They also lack the ability to transact data to MOTRAN in near real time.
What we saw happening is that operators would use note cards and record labor (setup & manufacture) times by manufacture Order.
Later they would find an available screen and enter the days activities. Because this method does not enforce near real time
transactions default XA was unable to accurately locate material across the shop floor.

We first found a way to use table SHPATC with overrides and write date in near real time.

Our solution included an easy to use .NET screen to record:

  • Setup time
  • Manufacture/Run time
  • Indirect time

This screen is touch enabled windows 10 PC – i5 running IBM client access and the .NET application.
We have locked out internet access with CISCO Firepower.

We have included some of the objects to give guidance on how we completed this.
If you are interested in seeing more detail — please feel free to reach out.

FTE Screen

FTE Process

FTE Process

Down load the source example:
rbslaborr

RPG Code
[cc lang=”PHP”]
H dftactgrp( *no ) OPTION(*NODEBUGIO) ACTGRP(‘SCHEDULE’)
H bnddir( ‘QC2LE’:’UTILITIES’)
**************************************************************************
** PROGRAM: RBSLABOR *
** LIBRARY: RBSLIB *
** CRT DATE: 06/05/2018 *
** PURPOSE: process labor records *
** Indicators ** *
** XX xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx *
** ** *
**************************************************************************
**************************************************************************
** DATE | CHANGES *
**——|—————————————————————-*
** | *
** | *
**************************************************************************
Fmorout if e k disk usropn
Frbslaborp uf a e k disk usropn
Frbslhistp uf a e k disk usropn
FSHPATC uf a e k disk usropn
FRBSLDAA if e disk
FRBSLDAB if e disk

d charTime s 6 inz
d CurrentIndirectManufacturingOrder…
d s 7 inz
d ErrorMessage s 60 inz
d WorkOrderCompleteStatus…
d s 1 inz(‘0’)
d*CurrentTime s 6 0 inz
d EndofLoop s n inz(‘0’)
d Error s n inz(‘0’)
d fileLibrary s 1 inz
d foundBatch s n inz
d NotFoundInMorout…
d s n
d i s 10i 0 inz
d IndirectLaborTime…
d s n inz(‘0’)
d IndirectNotifyFlag…
d s 1 inz
d ManufacturingTime…
d s n inz(‘0’)
d MaxItemLines s 10i 0 Inz(32766)
d MonthDay s 6 inz
d MyBatchNumber s 3 0 inz(999)
d MyLogFileMessageType…
d s 1 inz
d MyLogText s 100 inz
d MyManufactureOrder…
d s 7 inz
d MyWorkEmployee s 5 0 inz
d MyWorkQuantityGood…
d s 10 3 inz
d MyWorkCenter s 5 inz
d MyWorkSequence…
d s 4 inz
d MyWorkTime…
d s 7 2 inz
d MyOrderStatus s 1 inz(‘0’)
d MyOverheadCode s 1 inz
d OperationStatusCode…
d s 2 inz
d ProcessTheRecord…
d s n inz(‘1’)
d RecordsToProcess…
d s 10i 0 inz
d RowCount s 10i 0 inz
d ShopWorkRecordsToProcess…
d s 10i 0 inz
d SetupTime s n inz(‘0’)
d sleepseconds s 10i 0 inz(120)
d SpecialFacility…
d s n inz
d Terminated s n inz
d WorkCenterDescription…
d s 40 inz

d LDAWorkHeader e ds extname(RBSLDAA) inz
d LDAWorkDetail e ds extname(RBSLDAB) inz

d labordatads e ds extname(SHPWRK) qualified inz

d psds sds
d Qualified
d Program *Proc
d DeviceID 10A Overlay(PSDS: 244)
d JobUser 10A Overlay(PSDS: 254)

d LDA_DS ds DTAARA(*LDA)
d subfield 1 1024A

d LaborHistDS e ds extname(LBRHST)Qualified Inz
d IndirectDS e ds extname(INDLABDP) qualified inz

d MoRoutDS e ds extname(MOROUT) Qualified inz
d

d c1 ds Dim(32766) Qualified Inz
d Environment 2
d WorkCenter 5
d Libraries 220

* Delay – sleep function
d $sleep pr 10i 0 ExtProc( ‘sleep’ )
d seconds_ 10u 0 Value

d $calltheprogram…
d pr ExtPgm(‘AMC51’)
d TheData 1024a Const

/copy qprcsrc,COMMAND_CP
/copy qprcsrc,GETDATE_CP
/copy qprcsrc,getshif_cp
/copy qprcsrc,GETCOST_CP

exec sql
–Naming = *Sys,
set option commit = *none,
srtseq = *langidunq;

dou EndofLoop = *on;

exsr $ReadThruEnvironments ;
exsr $CloseDeviceRecords ;
exsr $GetSleepValue ;

// sleep for variable time (delay job)
$sleep(sleepseconds);

enddo;

*inlr = *on;

//——————————————————–
// $ReadThruEnvironments – read thru XA environments
//——————————————————–
begsr $ReadThruEnvironments;

// loop thru environments looking for entries to process
exec sql
declare c1 scroll cursor for
select concat(lides, fides),
lib01 || ‘ ‘ || lib02 || ‘ ‘ || lib03 || ‘ ‘ ||
lib04 || ‘ ‘ || lib05 || ‘ ‘ || lib06 || ‘ ‘ ||
lib07 || ‘ ‘ || lib08 || ‘ ‘ || lib09 || ‘ ‘ ||
lib10 || ‘ ‘ || lib11 || ‘ ‘ || lib12 || ‘ ‘ ||
lib13 || ‘ ‘ || lib14 || ‘ ‘ || lib15 || ‘ ‘ ||
lib16 || ‘ ‘ || lib17 || ‘ ‘ || lib18 || ‘ ‘ ||
lib19 || ‘ ‘ || lib20
from mmlist
where acrec = ‘A’
for read only;

exec sql
open c1;
exec sql
fetch first from c1 for :maxitemlines rows into :c1;
exec sql
get diagnostics :rowcount = row_count;
DoW RowCount <> 0;

For I = 1 to RowCount;

FileLibrary = %subst(c1(i).environment:2:1);
exsr $setLibaryList;

reset RecordsToProcess ;
exec sql
select count(*)
into : recordstoprocess
from rbslaborp
where lbprced = ‘ ‘ and
lbstatus1 = ‘C’;

if RecordsToProcess > *zeros;
exsr $createshipworktable;
Reset ShopWorkRecordsToProcess;
exsr $ReadRecords;
exsr $SetLDA;

// check shpwrk file in qtemp
// for anything to post
reset ShopWorkRecordsToProcess ;
exec sql
select count(*)
into : shopworkrecordstoprocess
from shpwrk ;

if ShopWorkRecordsToProcess > *zeros;

// process the records;
$CalltheProgram(subfield);
endif;

OneThousandLong = ‘DLTOVR FILE(SHPWRK) LVL(*JOB)’;
monitor;
runcommand(OneThousandLong);
on-error;
endmon;
endif ;

EndFor;

exec sql
fetch next from c1 for :maxitemlines rows into :c1;
exec sql
get diagnostics :rowcount = row_count;
EndDo;
exec sql
close c1;

endsr;

//——————————————————–
// $ReadRecords – pull records from rbslaborp
// and write to RBSSHPWRK
//——————————————————–
begsr $ReadRecords;

// set records to process in table rbslaborp
// to the correct Library
if not%open(rbslaborp);
open(e) rbslaborp;
endif;

if not%open(SHPATC);
open(e) shpatc;
endif;

Exsr $SetBatchNumber;

setll *start rbslaborp;
dou %eof(rbslaborp);
read rbslaborp;

// reset the work fields
reset MyManufactureOrder ;
reset MyWorkEmployee ;
reset MyWorkCenter ;
reset MyWorkTime ;
reset MyWorkSequence ;
reset MyWorkQuantityGood ;

if not %eof(rbslaborp) and
lbprced = *blanks and lbstatus1 = ‘C’ ;
MyManufactureOrder = lbmo ;
MyWorkCenter = lbwkctr ;
MyWorkTime = lbttime;
MyWorkSequence = lbmoseq ;
MyWorkQuantityGood = lbgood ;
MyWorkEmployee = %dec(%trimr(Lboper :’0′):5:0) ;

// set the time type
reset IndirectLaborTime ;
reset ManufacturingTime ;
reset SetupTime ;
reset Terminated ;

select;
when lbtype = ‘I’ or lbtype = ‘B’ ;
IndirectLaborTime = *on;
exsr $ProcessIndirect ;
when lbtype = ‘M’;
ManufacturingTime = *on;
when lbtype = ‘S’;
SetupTime = *on;
when lbtype = ‘T’;
Terminated = *on;
endsl;

reset WorkOrderCompleteStatus ;

if LbComplete = ‘Y’ ;
WorkOrderCompleteStatus = ‘1’ ;
endif ;

// this sets process the record to *on
reset ProcessTheRecord;
// if error then validate turns Processtherecord to *off

select ;
when Terminated ;
when IndirectLaborTime ;
other ;
exsr $validate;
if ProcessTheRecord;
exsr $WriteRecordS;
endif;
endsl ;

else;
// release the locks
unlock rbslaborp;
endif;
enddo;

if %open(rbslaborp);
close rbslaborp;
endif;

if %open(SHPATC);
close shpatc;
endif;

endsr;
//——————————————————–
// $validate- validate the data prior to write
//——————————————————–
begsr $Validate;

reset Error ;
reset NotFoundInMorout ;
reset OperationStatusCode ;
reset ErrorMessage ;

// validate that the manufacure order and the workcenter are found in MOROUT

if not %open(morout);
open morout;
endif;

chain(n) (MyManufactureOrder: MyWorkSequence) morout;
if %found(morout);
OperationStatusCode = opstc;
else;
NotFoundInMoRout = *on;
endif;

if %open(morout);
close morout;
endif;

select ;

when NotFoundInMorout ;
Error = *on ;
ErrorMessage = ‘Mo + Seq not found in MOROUT’ ;

when OperationStatusCode = ’00’ ;
Error = *on ;
ErrorMessage = ‘The sequence has been suspended’ ;

when OperationStatusCode = ’40’ ;
Error = *on ;
ErrorMessage = ‘The sequence is already closed’ ;

endsl ;

if not Error ;

select ;

when ManufacturingTime and MyWorkQuantityGood = *zeros
and MyWorkTime = *zeros ;
Error = *on ;
ErrorMessage = ‘Manufacturing with no time or qty’ ;

when MyWorkTime = *zeros ;
Error = *on ;
ErrorMessage = ‘Record has no minutes to report’ ;

endsl ;

endif ;

if Error ;
exsr $BadLaborRecord ;
endif ;

endsr;

//——————————————————–
// $WriteRecords – write records to RBSSHPWRK
//——————————————————–
begsr $WriteRecords;

clear labordatads;
labordatads.acrec = ‘A’;
labordatads.batch = MyBatchNumber ;
labordatads.terml = LBwkctr;
labordatads.ordno = MyManufactureOrder;
labordatads.costy = ‘1’;
LabordataDS.RECD =
‘T’ + %xlate(‘.’:’:’:%subst(%CHAR(%TIME()):1:5));

reset SpecialFacility ;
exec sql
select ‘1’
into : SpecialFacility
from facmst
where wkctr = : lbwkctr and
uusb = ‘I’ ;

labordatads.wkctr = MyWorkCenter;
labordatads.dptno = ’40’;
labordatads.satyp = ‘LABOR’;

if dateds.cymd0 = *zeros;
labordatads.trndt = %dec(%date():*cymd) ;
else;
labordatads.trndt = dateds.cymd0;
EndIf;

// setup – manufacture –
// 1 = closed, 0 = opened
labordatads.ocmpc = WorkOrderCompleteStatus ;

// grap the work center description
reset WorkCenterDescription;
reset MyOverheadCode ;
exec sql
select wcdsc, socod
into : WorkCenterDescription, :MyOverheadCode
from FACMST
where wkctr = :MyWorkCenter;

labordatads.OPSEQ = MyWorkSequence;

// – indirect=current year indirect labor MO
labordatads.RUNCD = ‘R’;
select;

when IndirectLaborTime;
labordatads.lbtim = MyWorkTime;

when SetupTime;
labordatads.runcd = ‘S’;

//overhead code ‘B’ is labor hours
//overhead code ‘A’ is machine hours
if MyOverheadCode = ‘B’ ;
labordatads.lbtim = MyworkTime;
else ;
labordatads.matim = MyWorkTime;
endif ;

when ManufacturingTime and SpecialFacility ;
labordatads.matim = MyWorkTime;

when ManufacturingTime ;
labordatads.lbtim = MyWorkTime;

endsl;

labordatads.qtcom = MyWorkQuantityGood;

// ***************************************
// Do not load per Brain
//
// labordatads.qtscp = lbreject;
//
// ***************************************

labordatads.empno = MyWorkEmployee;

if ManufacturingTime or SetupTime ;
// call the getcost procedure to calculate cost
CostsDS = ReturnCosts(MyManufactureOrder:
MyWorkSequence:
MyWorkCenter:
LbType:
MyworkTime);
endif ;

if SetupTime and CostsDS.OverHeadCode = ‘B’ ;
labordatads.setco = CostsDS.SetupCost ;
endif ;

if CostsDS.OverHeadCode = ‘B’ ;
labordatads.labco = CostsDS.LaborCost ;
endif ;

labordatads.ovhco = CostsDS.OverheadCost ;

if CostsDS.OverHeadCode = ‘A’ ;
labordatads.maccw = CostsDS.MachineCost ;
endif ;

if lbprced =*blanks and lbstatus1 =’C’ ;
exsr $WriteLaborHistoryRecord ;

exec sql
insert into shpwrk
values (: labordatads) ;

exsr $UpdateRbsLaborP ;

endif;

//*****************************************************
// Fields not being loaded yet
//*****************************************************
//labordatads.APCDE =
// labordatads.FORCE =
//labordatads.TCOST =
//labordatads.JOBNO =
//labordatads.JREFN =
//labordatads.ERATE =
//reset TransactionShift ;
//TransactionShift = ReturnShift(LbOper:LbStrStmp) ;
//labordatads.SHIFT = TransactionShift ;
//labordatads.MDESC =
//labordatads.MUCST =
//labordatads.MSCST =
//labordatads.MUQTY =
//labordatads.MSQTY =
//labordatads.RTHIS =
//labordatads.acsta =
//labordatads.RECD =
//labordatads.RFNO =
//*****************************************************

endsr;

//——————————————————–
// $SetBatchNumber – set the batch number
//——————————————————–
begsr $SetBatchNumber;

// create a batch number
// exclude error batches — these will need to be cleaned up
reset foundBatch;

// check to see if there is already a -999 batch
exec sql
select ‘1’
into : foundbatch
from shpatc
where batch = : mybatchnumber ;

// add a 999 batch if it is not already there
if not foundBatch;

rcdcd = ‘PQ’;
bstat = ‘A’;
batch = MyBatchNumber;
wsid1 = ‘AUTOPOST’;
wsid2 = ‘AUTOPOST’;
opid1 = ‘NTJ’;
opid2 = ‘NTJ’;
chartime = %char(%TIME(): *HMS0);
utime = %dec(CHARTIME:6:0);
dateds = GETDATE();
clear monthday;
monthday = %char(dateds.MDY);
modaY = %dec(%subst(MONTHDAY:1:2) + %subst(MONTHDAY:4:2):4:0);
nxseq = 1;
write shpatcc;

endif ;

endsr;

//——————————————————–
// $ProcessIndirect
//——————————————————–
begsr $ProcessIndirect ;

reset IndirectDS ;

IndirectDS.EmpNo = MyWorkEmployee ;
IndirectDS.OrdNo = MyManufactureOrder ;
IndirectDS.WkCtr = MyWorkCenter ;
IndirectDS.OpSeqNo = MyWorkSequence ;
IndirectDS.TotlTime = MyWorkTime ;
IndirectDS.TimeStmp = %timestamp() ;

if %len(%trim(LbIDesc)) <= 5 ;

IndirectDS.IndCode = LbIDesc ;

exec sql
select Notify
into : IndirectNotifyFlag
from indlabcp
where code = : LbIDesc ;

else;

exec sql
select code,
Notify
into : IndirectDS.IndCode,
: IndirectNotifyFlag
from indlabcp
where desc = : LbIDesc ;

endif ;

exec sql
insert into indlabdp
values (: IndirectDS) ;

exsr $UpdateRbsLaborP ;

// write error record to the labor history table if notify = ‘Y’
if IndirectNotifyFlag = ‘Y’;
MyLogFileMessageType = ‘I’;
MyLogText = LbIDesc;
exsr $WriteToLogFile;
endif;

endsr;

//——————————————————–
// $UpdateRbsLaborp
//——————————————————–
begsr $UpdateRbsLaborp ;

lbprced = ‘P’;
exec sql
set :lbprcstmp = current_timestamp;
lbprcpgm = psds.Program;
lbprcusr = psds.Jobuser;
update rlabor
%fields(lbprced : lbprcstmp : lbprcpgm : lbprcusr);

endsr;

//——————————————————–
// $setLibaryList;
//——————————————————–
begsr $setLibaryList;

// CHGLIBL LIBL(LABOR)
OneThousandLong = ‘CHGLIBL LIBL(‘ +
%trim(c1(i).libraries) + ‘)’;
monitor;
runcommand(OneThousandLong);
on-error;
endmon;
endsr;

//——————————————————–
// $BadLaborRecord
//——————————————————–
begsr $BadLaborRecord ;

ProcessTheRecord = *off;

lbprced = ‘P’ ;
exec sql SET :lbprcstmp = current_timestamp;
lbprcpgm = psds.Program;
lbprcusr = psds.Jobuser;

update rlabor %fields(lbprced : lbprcstmp :
lbprcpgm : lbprcusr );

// write error record to the labor history table
MyLogFileMessageType = ‘E’;
MyLogText = ErrorMessage;
exsr $WriteToLogFile;

endsr;

//——————————————————–
// $WriteToLogFile – write records to logfile
//——————————————————–
begsr $WriteToLogFile ;

if not %open(rbslhistp);
open rbslhistp;
endif;

clear RHISTR;
LRMO = MyManufactureOrder ;
LRMOSEQ = MyWorkSequence ;
LRWKCTR = MyWorkCenter ;
LROPER = %char(MyWorkEmployee) ;
LRMSG = MyLogText ;
LRTYPE = lbtype;
LRMSGTYP = MyLogFileMessageType;
exec sql SET :lrinstmp = current_timestamp;
LREPRCSTMP = *loval;
write RHISTR;

if %open(rbslhistp);
close rbslhistp;
endif;

endsr;
//——————————————————–
// $WriteLaborHistoryRecord
//——————————————————–
begsr $WriteLaborHistoryRecord ;
reset LaborHistDS ;

LaborHistDS.lordno = MyManufactureOrder ;
LaborHistDS.lopseq = MyWorkSequence ;
LaborHistDS.lwkctr = MyWorkCenter ;
LaborHistDS.lopdsc = lbidesc ;
LaborHistDS.ldptno = labordatads.dptno ;
LaborHistDS.ltdate = dateds.cymd0 ;
LaborHistDS.lruncd = labordatads.runcd ;
LaborHistDS.llbtim = MyWorkTime ;
LaborHistDS.lempno = labordatads.empno ;

if LaborHistDS.lopdsc = *blanks ;
exec sql
select wcdsc
into : LaborHistDS.lopdsc
from facmst
where wkctr = : MyWorkCenter ;
endif ;
exec sql
select jobno,
refno,
fitem
into : LaborHistDS.ljobno,
: LaborHistDS.lrefno,
: LaborHistDS.litnbr
from momast
where ordno = : MyManufactureOrder ;

exec sql
insert into lbrhst
values (: LaborHistDS) ;

endsr;

//——————————————————–
// $GetSleepValue – set the sleepseconds parameter
//——————————————————–
begsr $GetSleepValue ;

reset SleepSeconds ;

// get the control record setting for sleep time
exec sql
select syvalued
into : sleepseconds
from sycontrol
where syprogram = ‘RBSLABORR’ and
sykey = ‘SLEEP’ ;

endsr ;

//——————————————————–
// $CloseDeviceRecords close any remaining open records in RBSDEVICEP
//——————————————————–
begsr $CloseDeviceRecords ;

exec sql
update RBSDEVICEP set DVSTATUS = ‘C’ where DVSTATUS <> ‘C’ ;
endsr ;

//——————————————————–
// $CreateShipWorkTable – create the table and/or clear it
//——————————————————–
begsr $CreateShipWorkTable;

exec sql
drop table qtemp.shpwrk;

exec sql
create table qtemp.shpwrk(acrec char(1) ccsid 37 not null
default ”,
batch numeric(3, 0) not null default 0,
terml char(10) ccsid 37 not null
default ”,
ordno char(7) ccsid 37 not null
default ”,
costy char(1) ccsid 37 not null
default ”,
opseq char(4) ccsid 37 not null
default ”,
mitno char(15) ccsid 37 not null
default ”,
wkctr char(5) ccsid 37 not null
default ”,
nopsq char(4) ccsid 37 not null
default ”,
nxloc char(5) ccsid 37 not null
default ”,
dptno char(4) ccsid 37 not null
default ”,
apcde char(2) ccsid 37 not null
default ”,
satyp char(6) ccsid 37 not null
default ”,
trndt decimal(7, 0) not null default 0,
runcd char(1) ccsid 37 not null
default ”,
ocmpc char(1) ccsid 37 not null
default ”,
force char(1) ccsid 37 not null
default ”,
lbtim decimal(7, 2) not null default 0,
matim decimal(7, 2) not null default 0,
tcost decimal(13, 2) not null
default 0,
qtcom decimal(10, 3) not null
default 0,
qtscp decimal(10, 3) not null
default 0,
jobno char(12) ccsid 37 not null
default ”,
jrefn char(12) ccsid 37 not null
default ”,
empno numeric(5, 0) not null default 0,
erate decimal(5, 3) not null default 0,
shift char(1) ccsid 37 not null
default ”,
mdesc char(20) ccsid 37 not null
default ”,
mucst decimal(19, 8) not null
default 0,
mscst decimal(13, 2) not null
default 0,
muqty decimal(11, 4) not null
default 0,
msqty decimal(10, 3) not null
default 0,
rthis decimal(7, 0) not null default 0,
setco decimal(13, 2) not null
default 0,
labco decimal(13, 2) not null
default 0,
ovhco decimal(13, 2) not null
default 0,
acsta decimal(15, 2) not null
default 0,
maccw decimal(13, 2) not null
default 0,
recd char(6) ccsid 37 not null
default ”,
rfno char(10) ccsid 37 not null
default ”)
rcdfmt shpwrkc1 ;

//OVRDBF FILE(SHPWRK) TOFILE(QTEMP/SHPWRK) MBR(*FIRST) OVRSCOPE(*JOB)
OneThousandLong = ‘OVRDBF FILE(SHPWRK) TOFILE(QTEMP/SHPWRK) ‘ +
‘MBR(*FIRST) OVRSCOPE(*JOB)’;
monitor;
runcommand(OneThousandLong);
on-error;
endmon;

endsr;
//——————————————————–
// $SetLDA – setup the default *LDA
//——————————————————–
begsr $SetLDA;

// setup the LDA —
read RBSLDAA;
read RBSLDAB;
// need to adjust this data based on environment
subfield = header + LDAWorkDetail + Footer;
out lda_ds;

endsr;
//——————————————————–

[/cc]

XA (MAPICS) Facility Time Entry