Registro de horas

Los registros de horas (work-logs) representan las horas trabajadas por el personal del club: quién trabajó, cuándo, en qué categoría de tarea, sobre qué cultivo y a qué costo. En esta página profundizaremos en los endpoints de work-logs que permiten consultar, crear, actualizar y eliminar registros de horas de forma programática, además del endpoint de resumen agregado de fuerza laboral. Estas rutas son BFF propias del webapp de Cannahub y se apoyan en Strapi como backend de persistencia.

The WorkLog model

El modelo WorkLog describe una jornada o bloque de horas registrado para un trabajador. El campo hours es la unidad de duración expuesta por el BFF; internamente Strapi persiste durationMinutes (horas × 60), y el totalCost se calcula como costPerHour × hours cuando hay un costo por hora definido.

Properties

  • Name
    id
    Type
    string
    Description

    Identificador único del registro de horas.

  • Name
    workerId
    Type
    string
    Description

    Identificador del trabajador al que pertenece el registro.

  • Name
    workerName
    Type
    string
    Description

    Nombre del trabajador (denormalizado para facilitar el listado).

  • Name
    date
    Type
    string
    Description

    Fecha del trabajo realizado (formato YYYY-MM-DD).

  • Name
    startTime
    Type
    string
    Description

    Hora de inicio del bloque de trabajo (ISO timestamp). Si no se envía al crear, el servidor genera una hora sintética a partir de la fecha (08:00).

  • Name
    endTime
    Type
    string
    Description

    Hora de fin del bloque de trabajo (ISO timestamp). Si no se envía, se calcula sumando la duración a startTime.

  • Name
    hours
    Type
    number
    Description

    Cantidad de horas trabajadas. Debe ser un número mayor que 0 y menor o igual a 24.

  • Name
    category
    Type
    WorkCategory
    Description

    Categoría de la tarea realizada. Puede ser una de:

    • INFRASTRUCTURE_MAINTENANCE: Infraestructura/Mantenimiento
    • IRRIGATION_FERTILIZATION: Riego/Fertilización
    • PROPAGATION_CLONES: Propagación/Clones
    • GENERAL_OPERATIONS: Operación general
    • LOGISTICS_TRANSPORT: Logística/Traslados
    • SANITATION_PEST_CONTROL: Sanidad/Plagas
    • PRUNING_DEFOLIATION: Poda/Defoliación
    • DAY_LABOR: Jornales/Mano de obra
  • Name
    description
    Type
    string
    Description

    Descripción libre de la tarea realizada.

  • Name
    taskId
    Type
    string
    Description

    Identificador de la tarea (task) asociada al registro, si proviene de un plan de tareas.

  • Name
    cropId
    Type
    string
    Description

    Identificador del cultivo (crop) sobre el que se trabajó.

  • Name
    cropName
    Type
    string
    Description

    Nombre del cultivo (denormalizado).

  • Name
    isJornal
    Type
    boolean
    Description

    Indica si el registro corresponde a un jornal (mano de obra por día) en lugar de horas comunes. Por defecto false.

  • Name
    costPerHour
    Type
    number
    Description

    Costo por hora del trabajador, en pesos (ARS).

  • Name
    totalCost
    Type
    number
    Description

    Costo total del registro, calculado como costPerHour × hours. Sólo presente cuando existe costPerHour.

  • Name
    createdAt
    Type
    string
    Description

    Timestamp de creación del registro (ISO timestamp).

  • Name
    updatedAt
    Type
    string
    Description

    Timestamp de la última actualización del registro (ISO timestamp).

Ejemplo de WorkLog

{
  "id": "wl_01HQ8XYZWORK01",
  "workerId": "wrk_01HQ9TRAB001",
  "workerName": "Diego Fernández",
  "date": "2025-09-08",
  "startTime": "2025-09-08T08:00:00.000Z",
  "endTime": "2025-09-08T14:00:00.000Z",
  "hours": 6,
  "category": "PRUNING_DEFOLIATION",
  "description": "Poda apical y defoliación de plantas en floración temprana",
  "taskId": "task_01HQ9TASK042",
  "cropId": "crop_01HQ9CROP015",
  "cropName": "Northern Lights - Sala 2",
  "isJornal": false,
  "costPerHour": 3500,
  "totalCost": 21000,
  "createdAt": "2025-09-08T14:05:00.000Z",
  "updatedAt": "2025-09-08T14:05:00.000Z"
}

The WorkforceSummary model

El modelo WorkforceSummary expone KPIs agregados de la fuerza laboral para un rango de fechas y, opcionalmente, un trabajador. Es el payload de respuesta del endpoint /api/workforce/summary.

Properties

  • Name
    dateFrom
    Type
    string
    Description

    Fecha de inicio del rango consultado (YYYY-MM-DD) o null si no se especificó.

  • Name
    dateTo
    Type
    string
    Description

    Fecha de fin del rango consultado (YYYY-MM-DD) o null si no se especificó.

  • Name
    workerId
    Type
    string
    Description

    Identificador del trabajador por el que se filtró, o null si aplica a todo el club.

  • Name
    tasks
    Type
    object
    Description

    Métricas agregadas de tareas.

    • Name
      total
      Type
      number
      Description

      Total de tareas en el rango.

    • Name
      completed
      Type
      number
      Description

      Total de tareas completadas.

    • Name
      adherenceRate
      Type
      number
      Description

      Porcentaje de adherencia (tareas completadas sobre el total).

  • Name
    hours
    Type
    object
    Description

    Métricas agregadas de horas.

    • Name
      total
      Type
      number
      Description

      Total de horas trabajadas en el rango.

    • Name
      totalMinutes
      Type
      number
      Description

      Total de minutos trabajados (equivalente en minutos).

  • Name
    byCategory
    Type
    object
    Description

    Mapa de horas totales por categoría/tipo de tarea. Las claves corresponden a los tipos de tarea (por ejemplo WATERING, FEEDING, INSPECTION, TRIMMING, TRAINING, LIGHTING, HARVEST, ENVIRONMENTAL) y los valores son las horas acumuladas.

Ejemplo de WorkforceSummary

{
  "dateFrom": "2025-08-01",
  "dateTo": "2025-08-31",
  "workerId": null,
  "tasks": {
    "total": 124,
    "completed": 98,
    "adherenceRate": 79
  },
  "hours": {
    "total": 142.5,
    "totalMinutes": 8550
  },
  "byCategory": {
    "WATERING": 32.5,
    "FEEDING": 18.0,
    "INSPECTION": 24.0,
    "TRIMMING": 28.0,
    "TRAINING": 14.0,
    "LIGHTING": 6.0,
    "HARVEST": 12.0,
    "ENVIRONMENTAL": 8.0
  }
}

GET/api/work-logs

Listar registros de horas

Retorna una lista paginada de registros de horas con múltiples filtros opcionales. Por defecto devuelve hasta 50 registros por página.

Query params

  • Name
    category
    Type
    WorkCategory
    Description

    Filtra por categoría de trabajo.

  • Name
    workerId
    Type
    string
    Description

    Filtra por trabajador.

  • Name
    cropId
    Type
    string
    Description

    Filtra por cultivo.

  • Name
    taskId
    Type
    string
    Description

    Filtra por tarea.

  • Name
    dateFrom
    Type
    string
    Description

    Fecha de inicio del rango (YYYY-MM-DD).

  • Name
    dateTo
    Type
    string
    Description

    Fecha de fin del rango (YYYY-MM-DD).

  • Name
    isJornal
    Type
    boolean
    Description

    Filtra por registros de jornal (true/false).

  • Name
    search
    Type
    string
    Description

    Búsqueda por nombre de trabajador, descripción o nombre de cultivo.

  • Name
    page
    Type
    integer
    Description

    Número de página. Por defecto 1.

  • Name
    limit
    Type
    integer
    Description

    Cantidad de registros por página. Por defecto 50.

Request

GET
/api/work-logs
curl -G https://api.cannahub.tech/api/work-logs \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -d category=PRUNING_DEFOLIATION \
  -d dateFrom=2025-09-01 \
  -d dateTo=2025-09-30 \
  -d limit=50

Response

{
  "workLogs": [
    {
      "id": "wl_01HQ8XYZWORK01",
      "workerId": "wrk_01HQ9TRAB001",
      "workerName": "Diego Fernández",
      "date": "2025-09-08",
      "startTime": "2025-09-08T08:00:00.000Z",
      "endTime": "2025-09-08T14:00:00.000Z",
      "hours": 6,
      "category": "PRUNING_DEFOLIATION",
      "description": "Poda apical y defoliación de plantas en floración temprana",
      "cropId": "crop_01HQ9CROP015",
      "cropName": "Northern Lights - Sala 2",
      "isJornal": false,
      "costPerHour": 3500,
      "totalCost": 21000,
      "createdAt": "2025-09-08T14:05:00.000Z",
      "updatedAt": "2025-09-08T14:05:00.000Z"
    }
  ],
  "count": 1,
  "filters": {
    "category": "PRUNING_DEFOLIATION",
    "dateFrom": "2025-09-01",
    "dateTo": "2025-09-30",
    "page": "1",
    "limit": "50"
  }
}

POST/api/work-logs

Crear un registro de horas

Crea un nuevo registro de horas. Los campos workerId, workerName, date, category y hours son obligatorios. El valor de hours debe ser un número entre 0 (exclusivo) y 24. Si se envía costPerHour, el servidor calcula automáticamente totalCost.

Atributos requeridos

  • Name
    workerId
    Type
    string
    Description

    Identificador del trabajador.

  • Name
    workerName
    Type
    string
    Description

    Nombre del trabajador.

  • Name
    date
    Type
    string
    Description

    Fecha del trabajo (YYYY-MM-DD).

  • Name
    category
    Type
    WorkCategory
    Description

    Categoría de la tarea.

  • Name
    hours
    Type
    number
    Description

    Horas trabajadas. Debe ser un número entre 0 y 24 (exclusivo en 0, inclusivo en 24).

Atributos opcionales

  • Name
    startTime
    Type
    string
    Description

    Hora de inicio (ISO timestamp). Si se omite, se genera a partir de la fecha (08:00).

  • Name
    endTime
    Type
    string
    Description

    Hora de fin (ISO timestamp). Si se omite, se calcula desde startTime + duración.

  • Name
    description
    Type
    string
    Description

    Descripción de la tarea.

  • Name
    taskId
    Type
    string
    Description

    Identificador de la tarea asociada.

  • Name
    cropId
    Type
    string
    Description

    Identificador del cultivo.

  • Name
    cropName
    Type
    string
    Description

    Nombre del cultivo.

  • Name
    isJornal
    Type
    boolean
    Description

    Marca el registro como jornal. Por defecto false.

  • Name
    costPerHour
    Type
    number
    Description

    Costo por hora del trabajador, en pesos (ARS).

Request

POST
/api/work-logs
curl -X POST https://api.cannahub.tech/api/work-logs \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{
    "workerId": "wrk_01HQ9TRAB002",
    "workerName": "Lucía Romero",
    "date": "2025-09-09",
    "category": "IRRIGATION_FERTILIZATION",
    "hours": 4,
    "description": "Riego y fertilización de sala de vegetativo",
    "cropId": "crop_01HQ9CROP020",
    "cropName": "Amnesia Haze - Sala 1",
    "isJornal": false,
    "costPerHour": 3200
  }'

Response

{
  "workLog": {
    "id": "wl_01HQ8XYZWORK02",
    "workerId": "wrk_01HQ9TRAB002",
    "workerName": "Lucía Romero",
    "date": "2025-09-09",
    "startTime": "2025-09-09T08:00:00.000Z",
    "endTime": "2025-09-09T12:00:00.000Z",
    "hours": 4,
    "category": "IRRIGATION_FERTILIZATION",
    "description": "Riego y fertilización de sala de vegetativo",
    "cropId": "crop_01HQ9CROP020",
    "cropName": "Amnesia Haze - Sala 1",
    "isJornal": false,
    "costPerHour": 3200,
    "totalCost": 12800,
    "createdAt": "2025-09-09T12:02:00.000Z",
    "updatedAt": "2025-09-09T12:02:00.000Z"
  }
}

GET/api/work-logs/:id

Obtener un registro de horas

Retorna un único registro de horas por su identificador. Si no existe, devuelve un error 404.

Path params

  • Name
    id
    Type
    string
    Description

    Identificador del registro de horas.

Request

GET
/api/work-logs/:id
curl https://api.cannahub.tech/api/work-logs/wl_01HQ8XYZWORK01 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "workLog": {
    "id": "wl_01HQ8XYZWORK01",
    "workerId": "wrk_01HQ9TRAB001",
    "workerName": "Diego Fernández",
    "date": "2025-09-08",
    "startTime": "2025-09-08T08:00:00.000Z",
    "endTime": "2025-09-08T14:00:00.000Z",
    "hours": 6,
    "category": "PRUNING_DEFOLIATION",
    "description": "Poda apical y defoliación de plantas en floración temprana",
    "taskId": "task_01HQ9TASK042",
    "cropId": "crop_01HQ9CROP015",
    "cropName": "Northern Lights - Sala 2",
    "isJornal": false,
    "costPerHour": 3500,
    "totalCost": 21000,
    "createdAt": "2025-09-08T14:05:00.000Z",
    "updatedAt": "2025-09-08T14:05:00.000Z"
  }
}

PATCH/api/work-logs/:id

Actualizar un registro de horas

Actualiza parcialmente un registro de horas existente. Todos los campos del body son opcionales (partial update). Si se modifican hours o costPerHour, el totalCost se recalcula automáticamente.

Path params

  • Name
    id
    Type
    string
    Description

    Identificador del registro de horas.

Atributos opcionales

  • Name
    workerId
    Type
    string
    Description

    Identificador del trabajador.

  • Name
    workerName
    Type
    string
    Description

    Nombre del trabajador.

  • Name
    date
    Type
    string
    Description

    Fecha del trabajo (YYYY-MM-DD).

  • Name
    startTime
    Type
    string
    Description

    Hora de inicio (ISO timestamp).

  • Name
    endTime
    Type
    string
    Description

    Hora de fin (ISO timestamp).

  • Name
    hours
    Type
    number
    Description

    Horas trabajadas.

  • Name
    category
    Type
    WorkCategory
    Description

    Categoría de la tarea.

  • Name
    description
    Type
    string
    Description

    Descripción de la tarea.

  • Name
    taskId
    Type
    string
    Description

    Identificador de la tarea asociada.

  • Name
    cropId
    Type
    string
    Description

    Identificador del cultivo.

  • Name
    cropName
    Type
    string
    Description

    Nombre del cultivo.

  • Name
    isJornal
    Type
    boolean
    Description

    Marca el registro como jornal.

  • Name
    costPerHour
    Type
    number
    Description

    Costo por hora del trabajador, en pesos (ARS).

Request

PATCH
/api/work-logs/:id
curl -X PATCH https://api.cannahub.tech/api/work-logs/wl_01HQ8XYZWORK01 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{
    "hours": 7,
    "costPerHour": 3800,
    "description": "Poda apical, defoliación y limpieza de sala"
  }'

Response

{
  "workLog": {
    "id": "wl_01HQ8XYZWORK01",
    "workerId": "wrk_01HQ9TRAB001",
    "workerName": "Diego Fernández",
    "date": "2025-09-08",
    "hours": 7,
    "category": "PRUNING_DEFOLIATION",
    "description": "Poda apical, defoliación y limpieza de sala",
    "cropId": "crop_01HQ9CROP015",
    "cropName": "Northern Lights - Sala 2",
    "isJornal": false,
    "costPerHour": 3800,
    "totalCost": 26600,
    "createdAt": "2025-09-08T14:05:00.000Z",
    "updatedAt": "2025-09-09T10:20:00.000Z"
  }
}

DELETE/api/work-logs/:id

Eliminar un registro de horas

Elimina de forma permanente un registro de horas. Si el registro no existe, devuelve un error 404.

Path params

  • Name
    id
    Type
    string
    Description

    Identificador del registro de horas.

Request

DELETE
/api/work-logs/:id
curl -X DELETE https://api.cannahub.tech/api/work-logs/wl_01HQ8XYZWORK01 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "success": true
}

GET/api/workforce/summary

Resumen de fuerza laboral

Ruta BFF que retorna KPIs agregados de fuerza laboral para un rango de fechas y, opcionalmente, un trabajador específico. Combina métricas de tareas (total, completadas, adherencia) y de horas (total, minutos, desglose por categoría).

Query params

  • Name
    dateFrom
    Type
    string
    Description

    Fecha de inicio del rango (YYYY-MM-DD).

  • Name
    dateTo
    Type
    string
    Description

    Fecha de fin del rango (YYYY-MM-DD).

  • Name
    workerId
    Type
    string
    Description

    Filtra el resumen por un trabajador específico.

Request

GET
/api/workforce/summary
curl -G https://api.cannahub.tech/api/workforce/summary \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -d dateFrom=2025-08-01 \
  -d dateTo=2025-08-31

Response

{
  "dateFrom": "2025-08-01",
  "dateTo": "2025-08-31",
  "workerId": null,
  "tasks": {
    "total": 124,
    "completed": 98,
    "adherenceRate": 79
  },
  "hours": {
    "total": 142.5,
    "totalMinutes": 8550
  },
  "byCategory": {
    "WATERING": 32.5,
    "FEEDING": 18.0,
    "INSPECTION": 24.0,
    "TRIMMING": 28.0,
    "TRAINING": 14.0,
    "LIGHTING": 6.0,
    "HARVEST": 12.0,
    "ENVIRONMENTAL": 8.0
  }
}

React Query Hooks

Cannahub provee hooks de React Query pre-construidos para gestionar registros de horas. Estos hooks interactúan con las rutas BFF y manejan automáticamente el cacheo, la invalidación y el manejo de errores.

Query Keys

// /features/Club/Workforce/hooks/keys.ts
export const workLogKeys = {
  all: ['workLogs'] as const,
  lists: () => [...workLogKeys.all, 'list'] as const,
  list: (filters?: WorkLogFilters) => [...workLogKeys.lists(), filters] as const,
  details: () => [...workLogKeys.all, 'detail'] as const,
  detail: (id: string) => [...workLogKeys.details(), id] as const,
}

Queries

import { useWorkLogsQuery, useWorkLogQuery } from '@/features/Club/Workforce/hooks'

// Listar registros de horas con filtros opcionales
const { data, isLoading } = useWorkLogsQuery({
  category: 'PRUNING_DEFOLIATION',
  dateFrom: '2025-09-01',
  dateTo: '2025-09-30',
})

// Obtener un registro de horas por id
const { data: workLog } = useWorkLogQuery('wl_01HQ8XYZWORK01')

Mutations

import {
  useCreateWorkLogMutation,
  useUpdateWorkLogMutation,
  useDeleteWorkLogMutation,
} from '@/features/Club/Workforce/hooks'

// Crear un registro de horas
const { mutate: createWorkLog } = useCreateWorkLogMutation()
createWorkLog({
  workerId: 'wrk_01HQ9TRAB002',
  workerName: 'Lucía Romero',
  date: '2025-09-09',
  category: 'IRRIGATION_FERTILIZATION',
  hours: 4,
  costPerHour: 3200,
})

// Actualizar un registro de horas
const { mutate: updateWorkLog } = useUpdateWorkLogMutation()
updateWorkLog({
  id: 'wl_01HQ8XYZWORK01',
  data: { hours: 7, costPerHour: 3800 },
})

// Eliminar un registro de horas
const { mutate: deleteWorkLog } = useDeleteWorkLogMutation()
deleteWorkLog('wl_01HQ8XYZWORK01')

Cache Invalidation

Todas las mutaciones invalidan automáticamente las query keys relevantes al completarse con éxito:

// Tras una mutación exitosa, se invalidan estas queries:
queryClient.invalidateQueries({ queryKey: workLogKeys.lists() })
queryClient.invalidateQueries({ queryKey: workLogKeys.detail(id) })

Cálculo de costos y jornales

El costo total de un registro se deriva del costo por hora y las horas trabajadas:

totalCost = costPerHour × hours

Cuando isJornal es true, el registro representa un jornal (mano de obra por día) en lugar de horas comunes; el cálculo de costo sigue la misma fórmula sobre las horas registradas. Internamente, el BFF convierte hours a durationMinutes (hours × 60) al persistir en Strapi, y recompone hours al leer.


Was this page helpful?