fix: Ajustes al layout del PDF de catación
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m12s

- Cambiar a 2 formularios por hoja para más espacio vertical
- Corregir distribución de familias de notas (Floral, Afrutado, Verde/Vegetal, Otra, Tostado, etc.)
- Aumentar margen vertical entre checkboxes
- Corregir "beberse" → "haberse" en sección de defectos
- Alinear checkboxes de defectos bajo el header
- Quitar recuadro de Notas y mostrar solo "Notas: valor"
This commit is contained in:
2025-11-24 17:37:47 -06:00
parent 701d0c8fdb
commit ce460a17c2
4 changed files with 159 additions and 258 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

View File

@@ -7,7 +7,7 @@ import { jsPDF } from 'jspdf'
import type { Muestra, SesionCatacion } from '~/types/catacion' import type { Muestra, SesionCatacion } from '~/types/catacion'
import { renderizarFormulario } from '~/utils/pdf' import { renderizarFormulario } from '~/utils/pdf'
const FORMULARIOS_POR_PAGINA = 3 const FORMULARIOS_POR_PAGINA = 2
export const usePdfExport = () => { export const usePdfExport = () => {
const exportando = ref(false) const exportando = ref(false)

View File

@@ -19,7 +19,6 @@ import {
dibujarCheckbox, dibujarCheckbox,
dibujarCheckboxConLabel, dibujarCheckboxConLabel,
dibujarCasillasTazas, dibujarCasillasTazas,
dibujarCeldaTexto,
dibujarRectangulo, dibujarRectangulo,
dibujarLineaHorizontal, dibujarLineaHorizontal,
dibujarLineaVertical, dibujarLineaVertical,
@@ -47,15 +46,15 @@ export function renderizarFormulario(
PDF_CONFIG.lineWidth.thick PDF_CONFIG.lineWidth.thick
) )
// Renderizar cada sección // Renderizar cada sección con posiciones ajustadas para formulario de 130mm
renderizarEncabezado(doc, xBase, yBase, muestra) renderizarEncabezado(doc, xBase, yBase, muestra)
renderizarTablaIntensidades(doc, xBase, yBase + 10, muestra) renderizarTablaIntensidades(doc, xBase, yBase + 14, muestra)
renderizarSeccionFraganciaAroma(doc, xBase + 48, yBase + 10, muestra) renderizarSeccionFraganciaAroma(doc, xBase + 55, yBase + 14, muestra)
renderizarSeccionSabores(doc, xBase + 48, yBase + 32, muestra) renderizarSeccionSabores(doc, xBase + 55, yBase + 55, muestra)
renderizarSeccionSensacionGustos(doc, xBase, yBase + 46, muestra) renderizarSeccionSensacionGustos(doc, xBase, yBase + 76, muestra)
renderizarSeccionTazasDefectos(doc, xBase, yBase + 58, muestra) renderizarSeccionTazasDefectos(doc, xBase, yBase + 96, muestra)
renderizarSeccionOtrasNotas(doc, xBase, yBase + 70, muestra) renderizarSeccionOtrasNotas(doc, xBase, yBase + 112, muestra)
renderizarSeccionSubTotal(doc, xBase, yBase + 78, muestra) renderizarSeccionSubTotal(doc, xBase, yBase + 122, muestra)
} }
/** /**
@@ -70,28 +69,28 @@ function renderizarEncabezado(
const width = PDF_CONFIG.formWidth const width = PDF_CONFIG.formWidth
// Línea separadora del encabezado // Línea separadora del encabezado
dibujarLineaHorizontal(doc, x, y + 10, x + width, PDF_CONFIG.lineWidth.normal) dibujarLineaHorizontal(doc, x, y + 14, x + width, PDF_CONFIG.lineWidth.normal)
// Título principal // Título principal
doc.setFontSize(PDF_CONFIG.fontSize.title) doc.setFontSize(PDF_CONFIG.fontSize.title)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('EVALUACION DEL VALOR DEL CAFE / DESCRIPTIVA-AFECTIVA', x + 2, y + 4) doc.text('EVALUACION DEL VALOR DEL CAFE / DESCRIPTIVA-AFECTIVA', x + 3, y + 5)
// Código EVC-IH01 // Código EVC-IH01
doc.setFontSize(PDF_CONFIG.fontSize.header) doc.setFontSize(PDF_CONFIG.fontSize.header)
doc.text('EVC-IH01', x + width - 20, y + 4) doc.text('EVC-IH01', x + width - 22, y + 5)
// Línea separadora después del título // Línea separadora después del título
dibujarLineaHorizontal(doc, x, y + 5.5, x + width, PDF_CONFIG.lineWidth.thin) dibujarLineaHorizontal(doc, x, y + 7, x + width, PDF_CONFIG.lineWidth.thin)
// MUESTRA // MUESTRA
doc.setFontSize(PDF_CONFIG.fontSize.body) doc.setFontSize(PDF_CONFIG.fontSize.body)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('MUESTRA:', x + 2, y + 8.5) doc.text('MUESTRA:', x + 3, y + 11)
doc.setFont('helvetica', 'normal') doc.setFont('helvetica', 'normal')
const nombreTruncado = truncarTexto(doc, muestra.nombre, 40, PDF_CONFIG.fontSize.body) const nombreTruncado = truncarTexto(doc, muestra.nombre, 60, PDF_CONFIG.fontSize.body)
doc.text(nombreTruncado, x + 20, y + 8.5) doc.text(nombreTruncado, x + 25, y + 11)
} }
/** /**
@@ -104,56 +103,36 @@ function renderizarTablaIntensidades(
muestra: Muestra muestra: Muestra
): void { ): void {
const colWidths = { const colWidths = {
parametro: 22, parametro: 28,
descriptiva: 12, descriptiva: 13,
afectiva: 12, afectiva: 13,
} }
const rowHeight = 3.5 const rowHeight = 5
const tableWidth = colWidths.parametro + colWidths.descriptiva + colWidths.afectiva const tableWidth = colWidths.parametro + colWidths.descriptiva + colWidths.afectiva
const tableHeight = rowHeight * 10
// Borde de la tabla // Borde de la tabla
dibujarRectangulo(doc, x, y, tableWidth, rowHeight * 10, PDF_CONFIG.lineWidth.normal) dibujarRectangulo(doc, x, y, tableWidth, tableHeight, PDF_CONFIG.lineWidth.normal)
// Headers // Headers
doc.setFontSize(PDF_CONFIG.fontSize.tiny) doc.setFontSize(PDF_CONFIG.fontSize.small)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
// Header: Intensidad // Header: Intensidad/Parámetro
dibujarCeldaTexto(doc, x, y, colWidths.parametro, rowHeight, 'Intensidad', { doc.text('Intensidad', x + 2, y + 4)
fontSize: PDF_CONFIG.fontSize.tiny, doc.setFontSize(PDF_CONFIG.fontSize.tiny)
fontStyle: 'bold', doc.text('Parámetro', x + 2, y + rowHeight + 4)
})
// Subheaders de intensidad
doc.setFontSize(PDF_CONFIG.fontSize.tiny - 0.5)
doc.text('Parámetro', x + 1, y + rowHeight + 2.5)
// Header: Descriptiva // Header: Descriptiva
dibujarCeldaTexto( dibujarLineaVertical(doc, x + colWidths.parametro, y, y + tableHeight)
doc,
x + colWidths.parametro,
y,
colWidths.descriptiva,
rowHeight * 2,
'',
{ border: true }
)
doc.setFontSize(PDF_CONFIG.fontSize.tiny) doc.setFontSize(PDF_CONFIG.fontSize.tiny)
doc.text('Descriptiva', x + colWidths.parametro + 1, y + 3) doc.text('Descriptiva', x + colWidths.parametro + 1, y + 4)
doc.text('(1 al 15)', x + colWidths.parametro + 2, y + 6) doc.text('(1 al 15)', x + colWidths.parametro + 2, y + 8)
// Header: Afectiva // Header: Afectiva
dibujarCeldaTexto( dibujarLineaVertical(doc, x + colWidths.parametro + colWidths.descriptiva, y, y + tableHeight)
doc, doc.text('Afectiva', x + colWidths.parametro + colWidths.descriptiva + 2, y + 4)
x + colWidths.parametro + colWidths.descriptiva, doc.text('(1 al 9)', x + colWidths.parametro + colWidths.descriptiva + 3, y + 8)
y,
colWidths.afectiva,
rowHeight * 2,
'',
{ border: true }
)
doc.text('Afectiva', x + colWidths.parametro + colWidths.descriptiva + 1.5, y + 3)
doc.text('(1 al 9)', x + colWidths.parametro + colWidths.descriptiva + 2.5, y + 6)
// Línea horizontal después de headers // Línea horizontal después de headers
dibujarLineaHorizontal(doc, x, y + rowHeight * 2, x + tableWidth) dibujarLineaHorizontal(doc, x, y + rowHeight * 2, x + tableWidth)
@@ -168,14 +147,14 @@ function renderizarTablaIntensidades(
// Parámetro // Parámetro
doc.setFontSize(PDF_CONFIG.fontSize.tiny) doc.setFontSize(PDF_CONFIG.fontSize.tiny)
doc.setFont('helvetica', 'normal') doc.setFont('helvetica', 'normal')
doc.text(param.label, x + 1, currentY + 2.5) doc.text(param.label, x + 2, currentY + 3.5)
// Valor descriptiva // Valor descriptiva
if (intensidad.descriptiva !== null) { if (intensidad.descriptiva !== null) {
doc.text( doc.text(
String(intensidad.descriptiva), String(intensidad.descriptiva),
x + colWidths.parametro + colWidths.descriptiva / 2 - 1, x + colWidths.parametro + colWidths.descriptiva / 2 - 1,
currentY + 2.5 currentY + 3.5
) )
} }
@@ -184,7 +163,7 @@ function renderizarTablaIntensidades(
doc.text( doc.text(
String(intensidad.afectiva), String(intensidad.afectiva),
x + colWidths.parametro + colWidths.descriptiva + colWidths.afectiva / 2 - 1, x + colWidths.parametro + colWidths.descriptiva + colWidths.afectiva / 2 - 1,
currentY + 2.5 currentY + 3.5
) )
} }
@@ -193,15 +172,6 @@ function renderizarTablaIntensidades(
currentY += rowHeight currentY += rowHeight
}) })
// Líneas verticales de la tabla
dibujarLineaVertical(doc, x + colWidths.parametro, y, y + rowHeight * 10)
dibujarLineaVertical(
doc,
x + colWidths.parametro + colWidths.descriptiva,
y,
y + rowHeight * 10
)
} }
/** /**
@@ -213,92 +183,64 @@ function renderizarSeccionFraganciaAroma(
y: number, y: number,
muestra: Muestra muestra: Muestra
): void { ): void {
const width = 151.9 // Ancho total de la sección derecha const width = 144.9 // Ancho total de la sección derecha
const colWidth = width / 2 const sectionHeight = 41
// Borde de la sección // Borde de la sección
dibujarRectangulo(doc, x, y, width, 22, PDF_CONFIG.lineWidth.normal) dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
// Header // Header
doc.setFontSize(PDF_CONFIG.fontSize.small) doc.setFontSize(PDF_CONFIG.fontSize.body)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('Fragancia - Aroma', x + 2, y + 3) doc.text('Fragancia - Aroma', x + 3, y + 5)
// Línea después del header // Línea después del header
dibujarLineaHorizontal(doc, x, y + 4.5, x + width, PDF_CONFIG.lineWidth.thin) dibujarLineaHorizontal(doc, x, y + 7, x + width, PDF_CONFIG.lineWidth.thin)
// Header columna Notas
doc.text('Notas', x + colWidth + 2, y + 3)
// Línea vertical divisoria
dibujarLineaVertical(doc, x + colWidth, y, y + 22)
// Checkboxes columna izquierda
let checkY = y + 6
const checkSpacing = 1.8
// Determinar qué está seleccionado // Determinar qué está seleccionado
const categoriasSeleccionadas = muestra.fraganciaAromaNotas.categorias const categoriasSeleccionadas = muestra.fraganciaAromaNotas.categorias
const subcategoriasSeleccionadas = muestra.fraganciaAromaNotas.subcategorias const subcategoriasSeleccionadas = muestra.fraganciaAromaNotas.subcategorias
// Columna izquierda de checkboxes (primera mitad de categorías) // Espaciado entre checkboxes (mayor margen vertical)
const categoriasIzq = CATEGORIAS_PDF.columnaIzquierda.slice(0, 6) const checkSpacing = 3.2
categoriasIzq.forEach((cat) => { const startY = y + 10
// Columna izquierda de checkboxes
let checkY = startY
CATEGORIAS_PDF.columnaIzquierda.forEach((cat) => {
const isChecked = const isChecked =
categoriasSeleccionadas.includes(cat.key as any) || categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key) subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 2, checkY, cat.label, isChecked, cat.indent) dibujarCheckboxConLabel(doc, x + 3, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing checkY += checkSpacing
}) })
// Continuar en columna media // Columna derecha de checkboxes
checkY = y + 6 checkY = startY
const categoriasMed = CATEGORIAS_PDF.columnaIzquierda.slice(6) CATEGORIAS_PDF.columnaDerecha.forEach((cat) => {
categoriasMed.forEach((cat) => {
const isChecked = const isChecked =
categoriasSeleccionadas.includes(cat.key as any) || categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key) subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 28, checkY, cat.label, isChecked, cat.indent) dibujarCheckboxConLabel(doc, x + 75, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing checkY += checkSpacing
}) })
// Columna derecha de checkboxes (Notas) // Notas específica (sin recuadro, solo texto)
checkY = y + 6 doc.setFontSize(PDF_CONFIG.fontSize.small)
const categoriasDer = CATEGORIAS_PDF.columnaDerecha.slice(0, 6) doc.setFont('helvetica', 'bold')
categoriasDer.forEach((cat) => { doc.text('Notas:', x + 3, y + sectionHeight - 2)
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + colWidth + 2, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Segunda columna de Notas
checkY = y + 6
const categoriasDer2 = CATEGORIAS_PDF.columnaDerecha.slice(6)
categoriasDer2.forEach((cat) => {
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + colWidth + 35, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Nota específica
if (muestra.fraganciaAromaNotas.notaEspecifica) { if (muestra.fraganciaAromaNotas.notaEspecifica) {
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
doc.setFont('helvetica', 'normal') doc.setFont('helvetica', 'normal')
const notaTruncada = truncarTexto( const notaTruncada = truncarTexto(
doc, doc,
muestra.fraganciaAromaNotas.notaEspecifica, muestra.fraganciaAromaNotas.notaEspecifica,
colWidth - 10, width - 25,
PDF_CONFIG.fontSize.tiny PDF_CONFIG.fontSize.small
) )
doc.text(notaTruncada, x + 2, y + 20) doc.text(notaTruncada, x + 18, y + sectionHeight - 2)
} }
} }
@@ -311,91 +253,63 @@ function renderizarSeccionSabores(
y: number, y: number,
muestra: Muestra muestra: Muestra
): void { ): void {
const width = 151.9 const width = 144.9
const colWidth = width / 2 const sectionHeight = 21
// Borde de la sección // Borde de la sección
dibujarRectangulo(doc, x, y, width, 14, PDF_CONFIG.lineWidth.normal) dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
// Header // Header
doc.setFontSize(PDF_CONFIG.fontSize.small) doc.setFontSize(PDF_CONFIG.fontSize.body)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('Sabores', x + 2, y + 3) doc.text('Sabores', x + 3, y + 5)
// Header Notas
doc.text('Notas', x + colWidth + 2, y + 3)
// Línea después del header // Línea después del header
dibujarLineaHorizontal(doc, x, y + 4.5, x + width, PDF_CONFIG.lineWidth.thin) dibujarLineaHorizontal(doc, x, y + 7, x + width, PDF_CONFIG.lineWidth.thin)
// Línea vertical divisoria
dibujarLineaVertical(doc, x + colWidth, y, y + 14)
// Checkboxes (mismas categorías que fragancia)
let checkY = y + 6
const checkSpacing = 1.6
const categoriasSeleccionadas = muestra.saborNotas.categorias const categoriasSeleccionadas = muestra.saborNotas.categorias
const subcategoriasSeleccionadas = muestra.saborNotas.subcategorias const subcategoriasSeleccionadas = muestra.saborNotas.subcategorias
// Columna izquierda (primeras 4) // Checkboxes en una sola fila (solo categorías principales sin subcategorías)
const categoriasIzq = CATEGORIAS_PDF.columnaIzquierda.slice(0, 4) const checkSpacing = 3
categoriasIzq.forEach((cat) => { const startY = y + 11
// Mostrar solo categorías principales
const categoriasPrincipales = [
{ key: 'Floral', label: 'Floral' },
{ key: 'Afrutado', label: 'Afrutado' },
{ key: 'Verde Vegetal', label: 'Verde/Vegetal' },
{ key: 'Otro', label: 'Otra' },
{ key: 'Tostado', label: 'Tostado' },
{ key: 'Nueces/Cacao', label: 'Nueces/Cacao' },
{ key: 'Especias', label: 'Especias' },
{ key: 'Dulce', label: 'Dulce' },
]
let checkX = x + 3
categoriasPrincipales.forEach((cat) => {
const isChecked = const isChecked =
categoriasSeleccionadas.includes(cat.key as any) || categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key) subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 2, checkY, cat.label, isChecked, cat.indent) dibujarCheckboxConLabel(doc, checkX, startY, cat.label, isChecked)
checkY += checkSpacing checkX += 18
}) })
// Columna media // Notas específica de sabor (sin recuadro)
checkY = y + 6 doc.setFontSize(PDF_CONFIG.fontSize.small)
const categoriasMed = CATEGORIAS_PDF.columnaIzquierda.slice(4, 8) doc.setFont('helvetica', 'bold')
categoriasMed.forEach((cat) => { doc.text('Notas:', x + 3, y + sectionHeight - 2)
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 28, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Columna derecha
checkY = y + 6
const categoriasDer = CATEGORIAS_PDF.columnaDerecha.slice(0, 4)
categoriasDer.forEach((cat) => {
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + colWidth + 2, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Segunda columna derecha
checkY = y + 6
const categoriasDer2 = CATEGORIAS_PDF.columnaDerecha.slice(4, 8)
categoriasDer2.forEach((cat) => {
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + colWidth + 35, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Nota específica de sabor
if (muestra.saborNotas.notaEspecifica) { if (muestra.saborNotas.notaEspecifica) {
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
doc.setFont('helvetica', 'normal') doc.setFont('helvetica', 'normal')
const notaTruncada = truncarTexto( const notaTruncada = truncarTexto(
doc, doc,
muestra.saborNotas.notaEspecifica, muestra.saborNotas.notaEspecifica,
colWidth - 10, width - 25,
PDF_CONFIG.fontSize.tiny PDF_CONFIG.fontSize.small
) )
doc.text(notaTruncada, x + 2, y + 12.5) doc.text(notaTruncada, x + 18, y + sectionHeight - 2)
} }
} }
@@ -409,70 +323,53 @@ function renderizarSeccionSensacionGustos(
muestra: Muestra muestra: Muestra
): void { ): void {
const width = PDF_CONFIG.formWidth const width = PDF_CONFIG.formWidth
const sectionHeight = 20
// Borde de la sección // Borde de la sección
dibujarRectangulo(doc, x, y, width, 12, PDF_CONFIG.lineWidth.normal) dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
// Sección Sensación en boca // Sección Sensación en boca
const sensacionWidth = width * 0.6 const sensacionWidth = width * 0.6
doc.setFontSize(PDF_CONFIG.fontSize.small) doc.setFontSize(PDF_CONFIG.fontSize.body)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('Sensación en boca', x + 2, y + 3) doc.text('Sensación en boca', x + 3, y + 5)
// Línea divisoria vertical // Línea divisoria vertical
dibujarLineaVertical(doc, x + sensacionWidth, y, y + 12) dibujarLineaVertical(doc, x + sensacionWidth, y, y + sectionHeight)
// Checkboxes de sensación // Checkboxes de sensación con más espacio vertical
let checkY = y + 5 const checkSpacing = 3.5
const checkSpacing = 2.2 let checkY = y + 9
let checkX = x + 2
SENSACIONES_PDF.forEach((sens, index) => { SENSACIONES_PDF.forEach((sens) => {
const isChecked = muestra.sensacionEnBoca === sens.key const isChecked = muestra.sensacionEnBoca === sens.key
// Ajustar posición para layout de 2 filas
if (index === 3) {
checkY = y + 5
checkX = x + 60
} else if (index > 0 && index < 3) {
checkY += checkSpacing
} else if (index > 3) {
checkY += checkSpacing
}
// Usar label corto para ahorrar espacio // Usar label corto para ahorrar espacio
const labelCorto = const labelCorto =
sens.key === 'Deja seca la boca' sens.key === 'Deja seca la boca'
? 'Deja seca boca' ? 'Deja seca la boca (Astringente)'
: sens.key === 'Áspero' : sens.key === 'Áspero'
? 'Áspero' ? 'Áspero (Arenoso, Rugoso)'
: sens.key : sens.key === 'Suave'
? 'Suave (Aterciopelado)'
: sens.key
dibujarCheckboxConLabel(doc, checkX, checkY, labelCorto, isChecked) dibujarCheckboxConLabel(doc, x + 3, checkY, labelCorto, isChecked)
checkY += checkSpacing
}) })
// Sección Gustos predominantes // Sección Gustos predominantes
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('Gustos predominantes (2)', x + sensacionWidth + 2, y + 3) doc.text('Gustos predominantes (2)', x + sensacionWidth + 3, y + 5)
// Checkboxes de gustos // Checkboxes de gustos con más espacio vertical
checkY = y + 5 checkY = y + 9
checkX = x + sensacionWidth + 2
GUSTOS_PDF.forEach((gusto, index) => { GUSTOS_PDF.forEach((gusto) => {
const isChecked = muestra.gustosPredominantes.includes(gusto.key as any) const isChecked = muestra.gustosPredominantes.includes(gusto.key as any)
dibujarCheckboxConLabel(doc, x + sensacionWidth + 3, checkY, gusto.label, isChecked)
if (index === 3) { checkY += checkSpacing
checkY = y + 5
checkX = x + sensacionWidth + 35
} else if (index > 0 && index < 3) {
checkY += checkSpacing
} else if (index > 3) {
checkY += checkSpacing
}
dibujarCheckboxConLabel(doc, checkX, checkY, gusto.label, isChecked)
}) })
} }
@@ -486,26 +383,29 @@ function renderizarSeccionTazasDefectos(
muestra: Muestra muestra: Muestra
): void { ): void {
const width = PDF_CONFIG.formWidth const width = PDF_CONFIG.formWidth
const sectionHeight = 16
// Borde de la sección // Borde de la sección
dibujarRectangulo(doc, x, y, width, 12, PDF_CONFIG.lineWidth.normal) dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
// Tazas NO Uniformes // Tazas NO Uniformes
dibujarCasillasTazas(doc, x + 2, y + 2, muestra.tazasNoUniformes, 'Tazas NO Uniformes:') dibujarCasillasTazas(doc, x + 3, y + 3, muestra.tazasNoUniformes, 'Tazas NO Uniformes:')
// Tazas Defectuosas // Tazas Defectuosas
dibujarCasillasTazas(doc, x + 55, y + 2, muestra.tazasDefectuosas, 'Tazas defectuosas:') dibujarCasillasTazas(doc, x + 70, y + 3, muestra.tazasDefectuosas, 'Tazas defectuosas:')
// Defectos // Defectos - Header alineado con checkboxes
doc.setFontSize(PDF_CONFIG.fontSize.small) doc.setFontSize(PDF_CONFIG.fontSize.small)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('Defecto (de beberse):', x + 110, y + 3) doc.text('Defecto (de haberse):', x + 140, y + 5)
let checkX = x + 150 // Checkboxes alineados bajo el header
let checkX = x + 140
const checkY = y + 10
DEFECTOS_PDF.forEach((defecto) => { DEFECTOS_PDF.forEach((defecto) => {
const isChecked = muestra.defecto === defecto.key const isChecked = muestra.defecto === defecto.key
dibujarCheckboxConLabel(doc, checkX, y + 5, defecto.label, isChecked) dibujarCheckboxConLabel(doc, checkX, checkY, defecto.label, isChecked)
checkX += 18 checkX += 20
}) })
} }
@@ -519,14 +419,15 @@ function renderizarSeccionOtrasNotas(
muestra: Muestra muestra: Muestra
): void { ): void {
const width = PDF_CONFIG.formWidth const width = PDF_CONFIG.formWidth
const sectionHeight = 10
// Borde de la sección // Borde de la sección
dibujarRectangulo(doc, x, y, width, 8, PDF_CONFIG.lineWidth.normal) dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
// Label // Label
doc.setFontSize(PDF_CONFIG.fontSize.small) doc.setFontSize(PDF_CONFIG.fontSize.body)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('OTRAS NOTAS:', x + 2, y + 4) doc.text('OTRAS NOTAS:', x + 3, y + 6)
// Valor // Valor
if (muestra.otrasNotas) { if (muestra.otrasNotas) {
@@ -534,14 +435,14 @@ function renderizarSeccionOtrasNotas(
const notaTruncada = truncarTexto( const notaTruncada = truncarTexto(
doc, doc,
muestra.otrasNotas, muestra.otrasNotas,
width - 35, width - 45,
PDF_CONFIG.fontSize.small PDF_CONFIG.fontSize.body
) )
doc.text(notaTruncada, x + 30, y + 4) doc.text(notaTruncada, x + 38, y + 6)
} }
// Línea para escribir // Línea para escribir
dibujarLineaHorizontal(doc, x + 30, y + 5, x + width - 2, PDF_CONFIG.lineWidth.thin) dibujarLineaHorizontal(doc, x + 38, y + 7, x + width - 3, PDF_CONFIG.lineWidth.thin)
} }
/** /**
@@ -554,9 +455,10 @@ function renderizarSeccionSubTotal(
muestra: Muestra muestra: Muestra
): void { ): void {
const width = PDF_CONFIG.formWidth const width = PDF_CONFIG.formWidth
const sectionHeight = 8
// Borde de la sección // Borde de la sección
dibujarRectangulo(doc, x, y, width, 7, PDF_CONFIG.lineWidth.normal) dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
// Cálculos // Cálculos
const sumatoriaAfectiva = calcularSumatoriaAfectiva(muestra) const sumatoriaAfectiva = calcularSumatoriaAfectiva(muestra)
@@ -567,7 +469,7 @@ function renderizarSeccionSubTotal(
doc.setFontSize(PDF_CONFIG.fontSize.small) doc.setFontSize(PDF_CONFIG.fontSize.small)
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text('SUB TOTAL:', x + 2, y + 4) doc.text('SUB TOTAL:', x + 3, y + 5)
// Valores // Valores
const valores = [ const valores = [
@@ -577,14 +479,14 @@ function renderizarSeccionSubTotal(
{ label: 'Total (100%) - Daño:', valor: (scaa - dano).toFixed(2) }, { label: 'Total (100%) - Daño:', valor: (scaa - dano).toFixed(2) },
] ]
let currentX = x + 28 let currentX = x + 32
valores.forEach((item) => { valores.forEach((item) => {
doc.setFont('helvetica', 'normal') doc.setFont('helvetica', 'normal')
doc.text(item.label, currentX, y + 4) doc.text(item.label, currentX, y + 5)
currentX += doc.getTextWidth(item.label) + 1 currentX += doc.getTextWidth(item.label) + 1
doc.setFont('helvetica', 'bold') doc.setFont('helvetica', 'bold')
doc.text(item.valor, currentX, y + 4) doc.text(item.valor, currentX, y + 5)
currentX += doc.getTextWidth(item.valor) + 5 currentX += doc.getTextWidth(item.valor) + 6
}) })
} }

View File

@@ -15,18 +15,18 @@ export const PDF_CONFIG = {
marginLeft: 8, marginLeft: 8,
marginRight: 8, marginRight: 8,
// Formulario individual (3 por página) // Formulario individual (2 por página para más espacio)
formHeight: 85, // ~85mm cada formulario formHeight: 130, // ~130mm cada formulario (más alto)
formWidth: 199.9, // pageWidth - marginLeft - marginRight formWidth: 199.9, // pageWidth - marginLeft - marginRight
formSpacing: 2, // Espacio entre formularios formSpacing: 3, // Espacio entre formularios
// Tipografía // Tipografía
fontSize: { fontSize: {
title: 9, title: 10,
header: 7, header: 8,
body: 6, body: 7,
small: 5, small: 6,
tiny: 4.5, tiny: 5.5,
}, },
// Colores // Colores
@@ -44,8 +44,8 @@ export const PDF_CONFIG = {
}, },
// Tamaño de checkbox // Tamaño de checkbox
checkboxSize: 2.5, checkboxSize: 3.5,
checkboxSpacing: 0.8, checkboxSpacing: 1.5,
} as const } as const
/** /**
@@ -157,7 +157,7 @@ export interface CategoriaPdf {
/** /**
* Categorías de notas para el formulario PDF * Categorías de notas para el formulario PDF
* Columna izquierda y derecha de checkboxes * Distribución correcta según formulario físico EVC-IH01
*/ */
export const CATEGORIAS_PDF: { export const CATEGORIAS_PDF: {
columnaIzquierda: CategoriaPdf[] columnaIzquierda: CategoriaPdf[]
@@ -167,14 +167,13 @@ export const CATEGORIAS_PDF: {
{ key: 'Floral', label: 'Floral' }, { key: 'Floral', label: 'Floral' },
{ key: 'Afrutado', label: 'Afrutado' }, { key: 'Afrutado', label: 'Afrutado' },
{ key: 'Bayas', label: 'Bayas', indent: true }, { key: 'Bayas', label: 'Bayas', indent: true },
{ key: 'Ácido', label: 'Ácido', indent: true }, { key: 'Frutas Deshidratadas', label: 'Frutas Deshidratadas', indent: true },
{ key: 'Frutas Deshidratadas', label: 'Frutas Deshid.', indent: true }, { key: 'Cítricos', label: 'Cítricos', indent: true },
{ key: 'Fermentado', label: 'Fermentado', indent: true },
{ key: 'Cítricos', label: 'Cítricos' },
{ key: 'Verde Vegetal', label: 'Verde/Vegetal' }, { key: 'Verde Vegetal', label: 'Verde/Vegetal' },
{ key: 'Químico', label: 'Químico' }, { key: 'Otro', label: 'Otra' },
{ key: 'Humedad/Tierra', label: 'Humedad/Tierra' }, { key: 'Químico', label: 'Químico', indent: true },
{ key: 'Madera', label: 'Madera' }, { key: 'Humedad/Tierra', label: 'Humedad/Tierra', indent: true },
{ key: 'Madera', label: 'Madera', indent: true },
], ],
columnaDerecha: [ columnaDerecha: [
{ key: 'Tostado', label: 'Tostado' }, { key: 'Tostado', label: 'Tostado' },