The Packaging Production module manages the final stage of the production line, handling roll palletizing, excess production (demasía) tracking, and finished goods registration.
Overview
The packaging system manages:
- Roll palletizing and lot completion
- Demasía tracking (excess/partial rolls)
- Lot status management (Completo/Parcial)
- OT search and autocomplete
- Dual-mode interface (operator full-screen and manager list view)
- KPI dashboard for packaging metrics
Component: ProductionPackagingComponent
Location: src/features/production/production-packaging.component.ts
Data Model
The packaging report structure includes:
Unique report identifier (e.g., “PKG-1000”)
Report date (ISO string for form input, Date object for display)
Operator/packager responsible
Shift identifier: “Día - A”, “Noche - B”, “Día - C”, “Noche - D”
Client name (Razón Social)
status
'Completo' | 'Parcial'
required
Lot completion status
Quantity of finished rolls palletized
Average linear meters per roll (MLL/ROLLO)
Number of excess/partial rolls (default: 0)
Total linear meters in excess rolls (default: 0)
Dual Interface Modes
The component supports two distinct modes:
List View (Manager Mode)
Default view showing:
- Reports table with search
- KPI dashboard cards
- “Nuevo Registro” button to create reports
Navigation: From dashboard → Production → Packaging
Full-Screen Operator Mode
Optimized for production floor:
- Large form fields for touchscreen
- Full-screen immersive interface
- Auto-opens when accessed via operator route
Navigation: From operator terminal → Empaquetado (ST-04)
// From production-packaging.component.ts:416
ngOnInit() {
this.isOperatorMode = this.router.url.includes('/operator/packaging');
if (this.isOperatorMode) {
this.createNewReport();
this.showForm = true;
}
}
KPI Dashboard
Four key metrics are displayed (mock data in current implementation):
Rollos Terminados
- Icon: Check circle (teal)
- Value: 2,450 units
- Description: Total finished rolls
Metros Lineales
- Icon: Straighten (blue)
- Value: 145.2 km
- Description: Total linear meters produced
Demasía Recuperada
- Icon: Add circle (purple)
- Value: 12.5%
- Description: Percentage of excess production recovered
Lotes Parciales
- Icon: Timelapse (yellow)
- Value: 3 lotes
- Description: Number of partial lots (shift closures)
Master Data Entry
Editable Fields:Report date (pre-filled with today)
Packager name (pre-filled with logged-in user)
Shift selection:
- Día - A
- Noche - B
- Día - C
- Noche - D
Search Work Order
OT Autocomplete with Live SearchsearchOt(event: any) {
const query = (event.target.value || '').toLowerCase();
this.showOtSuggestions = true;
if (!query) {
this.otSuggestions = this.ordersService.ots.slice(0, 5);
return;
}
this.otSuggestions = this.ordersService.ots.filter(ot =>
String(ot.OT).toLowerCase().includes(query) ||
String(ot['Razon Social']).toLowerCase().includes(query)
).slice(0, 8);
}
Suggestion Display:
- OT number (bold, blue)
- Order status badge
- Client name (bold)
- Product description (small, gray)
Select OT
When user clicks a suggestion:selectOt(ot: any) {
this.currentReport.ot = ot.OT;
this.currentReport.client = ot['Razon Social'];
this.currentReport.description = ot.descripcion;
this.showOtSuggestions = false;
}
This auto-populates client and product description. Set Lot Status
Choose production status:
- Icon: Check circle (emerald)
- Meaning: Production finished, order complete
- Use: Final delivery, no pending work
- Icon: Timelapse (amber)
- Meaning: Shift closure, more production pending
- Use: End of shift with incomplete order
<div (click)="currentReport.status = 'Completo'"
class="cursor-pointer p-4 rounded-lg border"
[ngClass]="currentReport.status === 'Completo' ?
'bg-[#064e3b]/20 border-emerald-500/50' :
'bg-[#121921] border-gray-700'">
Enter Packaging Data
Main Production Section:Quantity of finished rolls
- Label: “Total de unidades paletizadas”
- Unit: UND
Average linear meters per roll
- Label: “Longitud promedio por unidad”
- Unit: MLL/ROLLO
Record Excess (Demasía)
Demasía Section (right column with purple accent):Demasía refers to material that exceeds the ordered quantity or partial rolls that don’t meet the standard roll specification.
Number of excess/partial rolls
Total linear meters in excess
Info message:
“Registrar aquí el material sobrante recuperable que no cumple con el estándar de rollo completo.”
Add Observations
<textarea [(ngModel)]="currentReport.notes"
placeholder="Ingrese comentarios adicionales sobre el proceso de empaquetado...">
</textarea>
Document:
- Minor incidents
- Packing discrepancies
- Notes for next shift
Save Report
Validation (production-packaging.component.ts:497):saveReport() {
if(!this.currentReport.rolls) {
alert("Debe ingresar la cantidad de rollos.");
return;
}
alert("Reporte guardado exitosamente.");
this.closeForm();
}
System validates that roll count is entered before saving.
OT Autocomplete Details
Suggestions Dropdown
Appears when user types in OT field:
<div *ngIf="showOtSuggestions && otSuggestions.length > 0"
class="absolute z-50 w-full bg-[#1a2332] border border-gray-700 rounded-lg shadow-2xl max-h-60 overflow-y-auto">
<div *ngFor="let suggestion of otSuggestions"
(mousedown)="selectOt(suggestion)"
class="p-3 hover:bg-blue-600/20 cursor-pointer border-b border-gray-800">
<!-- Suggestion content -->
</div>
</div>
Blur Handling
hideSuggestions() {
// Small delay to allow click event on suggestion to fire first
setTimeout(() => {
this.showOtSuggestions = false;
}, 200);
}
The 200ms delay ensures the click event on a suggestion fires before the dropdown closes.
Reports List Table
Manager view displays reports with columns:
Fecha
OT
Cliente / Producto
Estado Lote
Cant. Rollos
Metros (MLL)
Demasía
Acciones
Date (dd/MM/yyyy) and time (HH:mm)
Work order with teal badge
Client name (bold) and product description (truncated)
Status badge:
- COMPLETO: Emerald with check icon
- PARCIAL: Amber with timelapse icon
Roll count (bold, large, white)
Linear meters (monospace, gray)
Excess rolls (purple badge) or ”-” if zero
Edit button (pencil icon, teal hover)
Row Click Behavior
<tr (click)="editReport(report)" class="hover:bg-[#202b3d] transition-colors group cursor-pointer">
Clicking a row opens the form in edit mode:
editReport(report: any) {
this.currentReport = {
...report,
date: new Date(report.date).toISOString().split('T')[0],
operator: report.operator || this.state.userName(),
shift: report.shift || this.state.currentShift() || 'Día - A'
};
this.showForm = true;
}
When creating a new report (production-packaging.component.ts:424):
createNewReport() {
const ot = this.ordersService.ots.find(o =>
o.Estado_pedido === 'EN PROCESO' ||
o.Estado_pedido === 'PENDIENTE'
) || null;
this.currentReport = {
id: null,
date: new Date().toISOString().split('T')[0], // yyyy-MM-dd
operator: this.state.userName(),
shift: this.state.currentShift() || 'Día - A',
ot: ot ? ot.OT : '',
client: ot ? ot['Razon Social'] : '',
description: ot ? (ot.descripcion || '') : '',
status: 'Completo',
rolls: null,
meters: null,
demasiaRolls: null,
demasiaMeters: null,
notes: ''
};
this.showForm = true;
}
Features:
- Pre-fills with first available “EN PROCESO” or “PENDIENTE” OT
- Uses current logged-in user as operator
- Defaults to current shift
- Sets status to “Completo” initially
Operator Mode Navigation
Entry Point
From operator selector (operator-selector.component.ts:311):
navigateTo(type: string) {
if (type === 'packaging') {
this.router.navigate(['/operator/packaging']);
} else {
this.router.navigate(['/operator/select-machine', type]);
}
}
Note: Packaging is unique—it doesn’t require machine selection, so it navigates directly to the form.
Exit Behavior
closeForm() {
if (this.isOperatorMode) {
this.router.navigate(['/operator']);
} else {
this.showForm = false;
}
}
- Operator mode: Returns to operator selector
- Manager mode: Hides form, shows list view
Mock Data Generation
For demonstration purposes (production-packaging.component.ts:512):
get reports() {
const ots = this.ordersService.ots.slice(0, 10);
return ots.map((ot, i) => ({
id: `PKG-${1000+i}`,
date: new Date(new Date().getTime() - i * 86400000),
ot: ot.OT,
client: ot['Razon Social'],
description: ot.descripcion,
status: i % 3 === 0 ? 'Parcial' : 'Completo',
rolls: Math.floor(Math.random() * 50) + 10,
meters: Math.floor(Math.random() * 5000) + 1000,
demasiaRolls: i % 4 === 0 ? Math.floor(Math.random() * 5) : 0,
demasiaMeters: i % 4 === 0 ? Math.floor(Math.random() * 500) : 0,
notes: ''
}));
}
Patterns:
- Every 3rd report is “Parcial”
- Every 4th report has demasía
- Dates are staggered by day
User Helper Functions
Get User Initials
getInitials(name: string): string {
return name ?
name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase() :
'US';
}
Examples:
- “Juan Martinez” → “JM”
- “Ana Lopez” → “AL”
- "" → “US”
Best Practices
Verify roll count matches rewind production before recording
Record demasía immediately—it affects inventory and cost tracking
Use “Parcial” status only for shift closures, not partial deliveries
Document packing discrepancies in observations
Ensure linear meters per roll are consistent with specifications
Coordinate with warehouse for pallet pickup after recording
Always validate roll counts before marking as “Completo”
Do not leave demasía fields empty if excess production exists