Gestión de turnos de trabajo con Access
En determinadas empresas, o en determinados servicios, se trabajan muchas más horas a las semana de las que puede hacer un trabajador. Servicios médicos, policía, bomberos, fundiciones, funerarias, etc. dan servicio 24 horas al día 7 días a la semana y, como no hay ningún trabajador capaz de tanto, deben hacer distintos turnos que cubran todo el servicio. También es necesario hacerlos en empresas, como centros comerciales, que sin abrir 24 horas abren más horas, o durante más días, de lo que corresponde a una jornada laboral normal.
Los turnos son distintos dependiendo del tipo de trabajo, necesidades de la empresa, etc., por ejemplo, puede resultar que, dado el tipo de trabajo, por la noche se necesito menos personal, que se cierren los domingos o que no se cierre nunca, que se tenga derecho a más o menos días de descanso, etc. Dependiendo de esas necesidades, cada empresa organiza sus turnos de una manera distinta pero los resultados tienen cosas en común: Se elabora una secuencia de los distintos turnos o tipos de jornada que debe hacer un trabajador y se organiza a los trabajadores en grupos, de manera que empezando cada uno la secuencia en una fecha distinta, se solapen unos con otros y cubran toda la jornada.
Supongamos un caso muy sencillo: Cuatro trabajadores deben cubrir mañana tarde y noche y libran un día de cada cuatro. La secuencia sería mañana, tarde, noche, libre MTNL y cada trabajador la iniciaría con un día de diferencia.
Trabajador 1: MTNLMTNLMTNLMTNLMTNL…
Trabajador 2: TNLMTNLMTNLMTNLMTNLM…
Trabajador 3: NLMTNLMTNLMTNLMTNLMT…
Trabajador 4: LMTNLMTNLMTNLMTNLMTN…
Cada columna representa un día y con este esquema se garantiza que todos los días haya un trabajador de tarde, otro de noche y otro librando.
La mayor parte de los esquemas serán más complejos que éste, pero lo normal es que todos tengan una secuencia de turnos fija comenzando en distinto momento para cada grupo de trabajadores. Tomando esas secuencias y esos grupos como punto de partida, vamos a proponer una serie de soluciones que faciliten la gestión de grupos de trabajo utilizando una aplicación hecha en Access.
Averiguar el turno para una fecha
Necesitamos saber qué turno de trabajo le corresponde a un trabajador o grupo en determinada fecha. La primera tentación puede ser recorrer un bucle partiendo desde un turno conocido, sin embargo, hay otro planteamiento que resulta familiar, pues es el del calendario, es decir, de la misma manera que ordenamos las fechas, que son números consecutivos, en 7 columnas, una para cada día de la semana, y luego podemos mediante una función saber qué día de la semana es una determinada fecha, podemos ordenarlas en tantas columnas como tenga la secuencia.
Enero:
M
T
N
L
1
2
3
4
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Para averiguar la columna que corresponde a un determinado item, hace años que descubrí el Mediterráneo al hacerme esta función:
'--------------------------------------------------------------------------------------- ' Procedure : fColumna ' DateTime : 17/10/10 15:10 ' Author : Chea ' Purpose : Dada una relación de números consecutivos ordenados en columnas, devuelve la ' columna que le corresponde a un determinado item '--------------------------------------------------------------------------------------- ' Public Function fColumna(item As Long, Columnas As Long) As Long fColumna = 1 + ((item + Columnas - 1) Mod (Columnas)) End Function
La función vale para resolver muchos problemas si se pueden plantear a partir de números consecutivos ordenados en columnas, pero es algo genérico que presupone que la primera columna es el número 1, lo cual, como en el ejemplo, no siempre es el caso y tenemos que considerar en qué columna se encuentra el número 1. Para eso con los turnos de trabajo he creado una función específica.
'--------------------------------------------------------------------------------------- ' Procedure : fTurnoFecha ' DateTime : 17/10/10 15:12 ' Author : Chea ' Purpose : Dada una determinada cadencia de turnos expresada en una cadena, sCadena, en la que ' los turnos vienen separados por comas, obtiene el turno que corresponde a una determinada ' fecha, FechaCalculo. Es necesario conocer una fecha de referencia, PrimerDiaTurno, ' que coincida con el primer día de la cadencia. ' Usando una fecha fija como PrimerDiaturno,podemos pasar un parámetro opcional, ' lDesplazamiento, para indicar cuantos días nos desplazamos del PrimerDiaturno. ' Por ejemplo, podemos tener distintos grupos, cada uno empezando en un día ' distinto, el primero tendrá desplazamiento 0, el segundo empezará un día más tarde, etc. '--------------------------------------------------------------------------------------- ' Public Function fTurnoFecha(FechaCalculo As Date, sCadencia As String, PrimerDiaTurno As Date, Optional lDesplazamiento As Long) As String Dim v As Variant Dim lColumnas As Long Dim lCompensacion As Long v = Split(sCadencia, ",") lColumnas = 1 + UBound(v) lCompensacion = fColumna(Int(PrimerDiaTurno + lDesplazamiento), lColumnas) - 1 fTurnoFecha = v(fColumna(Int(FechaCalculo) - lCompensacion, lColumnas) - 1) End Function
A la función le pasamos FechaCalculo, que es la fecha para la que queremos obtener el turno, sCadencia, que es una cadena en la que indicamos la secuencia del turno, con los items separados por comas, por ejemplo “M,T,N,L” o “Mañana,Tarde,Noche,Libre”. También le pasamos PrimerDiaTurno que es una fecha de la que sepamos con certeza que se corresponde con el primer día de la secuencia o, de manera opcional, lDesplazamiento, que podemos utilizar para indicar cuantos días se desplaza de una fecha fija, por ejemplo 0, el primer día del turno, calculándolo por tanteo.
Por ejemplo, el día en que, tras una ardua negociación de empresa y trabajadores, se elaboraron las planillas, se comprobó que el trabajador 2 debía iniciar su secuencia el 4 de enero del 2007 y se anotó esta fecha para posteriores cálculos, según esto, para cálcular cómo le toca la Nochebuena del 2010, el cálculo en la ventana de inmediato sería:
? fTurnoFecha (#2010/12/24#,"Mañana,Tarde,Noche,Libre",#2007/01/04#)
Noche
Aparte de para hacerle la puñeta al trabajador 2, la función se puede utilizar en una consulta para obtener una relación de todos los turnos de todos los trabajadores para todos los días de un periodo determinado.
Elaborar un calendario con turnos
Tomemos una tabla de empleados, con indicación de a qué grupo pertenecen
Id | Empleado | Grupo |
---|---|---|
1 | Pedro | 1 |
2 | Andrés | 1 |
3 | Jaime | 1 |
4 | Juan | 2 |
5 | Felipe | 2 |
6 | Bartolomé | 2 |
7 | Tomás | 3 |
8 | Mateo | 3 |
9 | Santiago | 3 |
10 | Judas | 4 |
11 | Simón | 4 |
13 | Matías | 4 |
Aunque el último ID sea 13, se trata de 12 empleados, pues despidieron a uno por conducta desleal. Están organizados en 4 grupos cada uno de los cuales se inicia al día siguiente del grupo anterior. Vamos a crear el calendario correspondiente a octubre del 2010 utilizando una consulta.
Sabemos que si disponemos de una tabla Numeros poblada de numeros consecutivos empezando por el 1, podemos obtener una secuencia de fechas a base de sumar una FechaInicial menos uno al campo Numero. Este truco y el de no relacionar tablas para que el resultado sea un producto cartesiano lo vamos a utilizar en la siguiente consulta.
El texto SQL es el siguiente:
SELECT EmpleadosTurnos.Empleado, [Numero]+CDate("01-10-10")-1 AS FechaVirtual, EmpleadosTurnos.Grupo, fturnofecha([fechavirtual],"Mañana,Tarde,Noche, Libre",CDate("04-01-07"),[Grupo]-1) AS Turno FROM Numeros, EmpleadosTurnos WHERE ((([Numero]+CDate("01-10-10")-1)<=CVDate("31-10-10"))) ORDER BY EmpleadosTurnos.Empleado, [Numero]+CDate("01-10-10")-1;
Y el resultado, resumiendo, porque serían 372 filas, mostraría algo parecido a lo siguiente completado hasta mostrar todos los empleados con todas las fechas de octubre del 2010 y el turno de cada fecha
Empleado | FechaVirtual | Grupo | Turno |
---|---|---|---|
Andrés | 01/10/10 | 1 | Noche |
Andrés | 02/10/10 | 1 | Libre |
Andrés | 03/10/10 | 1 | Mañana |
Andrés | 04/10/10 | 1 | Tarde |
Andrés | 05/10/10 | 1 | Noche |
Andrés | 06/10/10 | 1 | Libre |
Andrés | 07/10/10 | 1 | Mañana |
Andrés | 08/10/10 | 1 | Tarde |
Andrés | 09/10/10 | 1 | Noche |
Andrés | 10/10/10 | 1 | Libre |
… | … | … | … |
Andrés | 27/10/10 | 1 | Mañana |
Andrés | 28/10/10 | 1 | Tarde |
Andrés | 29/10/10 | 1 | Noche |
Andrés | 30/10/10 | 1 | Libre |
Andrés | 31/10/10 | 1 | Mañana |
Bartolomé | 01/10/10 | 2 | Tarde |
Bartolomé | 02/10/10 | 2 | Noche |
Bartolomé | 03/10/10 | 2 | Libre |
Bartolomé | 04/10/10 | 2 | Mañana |
Bartolomé | 05/10/10 | 2 | Tarde |
Bartolomé | 06/10/10 | 2 | Noche |
Bartolomé | 07/10/10 | 2 | Libre |
… | … | … | .. |
La consulta la podemos personalizar para pasarle las fechas inicial y final del periodo como parámetros y también podemos tomarla como puento de partida para una consulta de datos anexado o de actualización que para cada día del año y cada empleado deje anotado el turno de trabajo. Aunque no es necesario volcar los datos en una tabla, pues podemos utilizar la consulta con cualquier periodo de fechas, si resulta muy conveniente, pues además de los distintos turnos pueden darse otras situaciones, como bajas, vacaciones, cambios de turno que hacen necesario trabajar con datos guardados.
A menudo se pretende realizar una planilla que refleje los trabajadores que entran en cada turno cada día, utilizando los días como encabezado de columna.
Empleado | Grupo | 01/10/10 | 02/10/10 | … | 25/10/10 | 26/10/10 | 27/10/10 | 28/10/10 | 29/10/10 | 30/10/10 | 31/10/10 |
---|---|---|---|---|---|---|---|---|---|---|---|
Andrés | 1 | Noche | Libre | … | Noche | Libre | Mañana | Tarde | Noche | Libre | Mañana |
Jaime | 1 | Noche | Libre | … | Noche | Libre | Mañana | Tarde | Noche | Libre | Mañana |
Pedro | 1 | Noche | Libre | … | Noche | Libre | Mañana | Tarde | Noche | Libre | Mañana |
Bartolomé | 2 | Tarde | Noche | … | Tarde | Noche | Libre | Mañana | Tarde | Noche | Libre |
Felipe | 2 | Tarde | Noche | … | Tarde | Noche | Libre | Mañana | Tarde | Noche | Libre |
Juan | 2 | Tarde | Noche | … | Tarde | Noche | Libre | Mañana | Tarde | Noche | Libre |
Mateo | 3 | Mañana | Tarde | … | Mañana | Tarde | Noche | Libre | Mañana | Tarde | Noche |
Santiago | 3 | Mañana | Tarde | … | Mañana | Tarde | Noche | Libre | Mañana | Tarde | Noche |
Tomás | 3 | Mañana | Tarde | … | Mañana | Tarde | Noche | Libre | Mañana | Tarde | Noche |
Judas | 4 | Libre | Mañana | … | Libre | Mañana | Tarde | Noche | Libre | Mañana | Tarde |
Matías | 4 | Libre | Mañana | … | Libre | Mañana | Tarde | Noche | Libre | Mañana | Tarde |
Simón | 4 | Libre | Mañana | … | Libre | Mañana | Tarde | Noche | Libre | Mañana | Tarde |
Evidentemente es fácil de hacer con una consulta de referencias cruzadas.
El texto SQL sería:
TRANSFORM First(qTurnosVirtuales.Turno) AS PrimeroDeTurno SELECT qTurnosVirtuales.Empleado, qTurnosVirtuales.Grupo FROM qTurnosVirtuales GROUP BY qTurnosVirtuales.Empleado, qTurnosVirtuales.Grupo ORDER BY qTurnosVirtuales.Grupo PIVOT qTurnosVirtuales.FechaVirtual;
Si nuestra consulta de referencias cruzadas la convertimos en un informe y usamos formato condicional, el resultado es la mar de vistoso (o la mar de hortera, según gustos). En el ejemplo le hemos quitado columnas para que nos quepa bien en la página.
En fin, se trata de una serie de ideas que pueden servir a alguien que debe plantearse la gestión de turnos de trabajo, pero no pretende ser ni “la solución” ni la forma canónica de plantearlo.
8 Responsesso far
Didáctico, estructurado, profesional… Chea, no nos conocemos pero por lo que leo de tí ¡¡eres un crack!! 😉
Gracias :’-) En realidad, sólo soy albañil informático.
Muy bueno !!
Me interesaría mucho poder ver esta información en un informe pero en formato «calendario» como aquí: http://jbchea.net/subinformecalendario.aspx.
Por ejemplo, para una persona determinada, que sus días libres aparezcan en negrita o marcados con un círculo.
¿Es posible hacer esto?
Sí se puede. Sólo hay que buscar dónde se pone el formato de texto enriquecido cuando es festivo y sustituirlo por el formato de texto enriquecido que hayamos decidido para el turno de trabajo que corresponda ese día.
En realidad es un poco más complicado, pero no ando con tiempo para preparar un ejemplo, que tengo en borrador, o un nuevo artículo.
Vale para access 97, pues me dice que la funcion fturnofecha no esta definida en la expresion.
Gracias
Hola, tengo una plata de produccion con 60 personas que manejan los tres turnos pero rotan semanalmente y cada dia tiene una funcion diferente, es posible realizar la gestion de turnos con este ejemplo? Me gustaria que hiciera un pequeño ejemplo con un minimo de personal para poder montarlo con las 60 personas
Gracias por este aporte, muy bueno.
quien me puede ayudar por favorr!!
como incluyo la funcion fturnofecha!1
sql o macroo??
ayuda !